Add Planner triage actions
Signed-off-by: James Ren <[email protected]>
--- /dev/null 2009-12-17 12:29:38.000000000 -0800
+++
autotest/frontend/client/src/autotest/planner/resources/PlannerImageBundle.java
2010-04-06 15:56:40.000000000 -0700
@@ -0,0 +1,8 @@
+package autotest.planner.resources;
+
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.user.client.ui.ImageBundle;
+
+public interface PlannerImageBundle extends ImageBundle {
+ public AbstractImagePrototype close();
+}
--- autotest/frontend/client/src/autotest/planner/triage/FailureTable.java
2010-04-05 13:07:24.000000000 -0700
+++ autotest/frontend/client/src/autotest/planner/triage/FailureTable.java
2010-04-06 15:56:40.000000000 -0700
@@ -5,19 +5,22 @@
import autotest.common.spreadsheet.Spreadsheet.Header;
import autotest.common.spreadsheet.Spreadsheet.HeaderImpl;
import autotest.common.spreadsheet.Spreadsheet.SpreadsheetListener;
+import autotest.common.ui.NotifyManager;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.IncrementalCommand;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-class FailureTable implements SpreadsheetListener {
+class FailureTable implements SpreadsheetListener, TriagePopup.Listener {
public interface Display {
public Spreadsheet getSpreadsheet();
+ public TriagePopup.Display generateTriagePopupDisplay();
}
private static class Failure {
@@ -59,10 +62,11 @@
public void bindDisplay(Display display) {
this.display = display;
+ display.getSpreadsheet().setListener(this);
}
public void addFailure(JSONObject failureObj) {
- rendered = false;
+ setRendered(false);
Failure failure = Failure.fromJsonObject(failureObj);
@@ -81,8 +85,14 @@
return display;
}
+ private void setRendered(boolean rendered) {
+ this.rendered = rendered;
+ display.getSpreadsheet().setVisible(rendered);
+ }
+
public void renderDisplay() {
Spreadsheet spreadsheet = display.getSpreadsheet();
+ spreadsheet.clear();
Header rowFields =
HeaderImpl.fromBaseType(Collections.singletonList("machine"));
Header columnFields = new HeaderImpl();
@@ -110,6 +120,8 @@
test.contents = failure.testName;
reason.contents = failure.reason;
+ test.testIndex = failure.id;
+ reason.testIndex = failure.id;
if (!failure.seen) {
test.contents = "<b>" + test.contents + "</b>";
@@ -122,8 +134,8 @@
spreadsheet.render(new IncrementalCommand() {
@Override
public boolean execute() {
- rendered = true;
- return false;
+ setRendered(true);
+ return false;
}
});
}
@@ -137,6 +149,39 @@
@Override
public void onCellClicked(CellInfo cellInfo, boolean isRightClick) {
- //TODO: handle row clicks (pop up the triage panel)
+ if (cellInfo.testIndex == 0) {
+ return;
+ }
+
+ TriagePopup popup = new TriagePopup(this, cellInfo.testIndex);
+ popup.bindDisplay(display.generateTriagePopupDisplay());
+ popup.render();
+ }
+
+ @Override
+ public void onTriage(TriagePopup source) {
+ if (removeFailure(source.getId())) {
+ renderDisplay();
+ }
+ }
+
+ private boolean removeFailure(int id) {
+ setRendered(false);
+
+ Iterator<Failure> iter = failures.iterator();
+ while (iter.hasNext()) {
+ if (iter.next().id == id) {
+ iter.remove();
+ return true;
+ }
+ }
+
+ /*
+ * TODO: throw an Exception instead, and register a handler with
+ * GWT.setUncaughtExceptionHandler()
+ */
+ NotifyManager.getInstance().showError("Did not find failure id " + id);
+ setRendered(true);
+ return false;
}
}
---
autotest/frontend/client/src/autotest/planner/triage/FailureTableDisplay.java
2010-04-05 13:07:24.000000000 -0700
+++
autotest/frontend/client/src/autotest/planner/triage/FailureTableDisplay.java
2010-04-06 15:56:40.000000000 -0700
@@ -12,7 +12,13 @@
initWidget(spreadsheet);
}
+ @Override
public Spreadsheet getSpreadsheet() {
return spreadsheet;
}
+
+ @Override
+ public TriagePopup.Display generateTriagePopupDisplay() {
+ return new TriagePopupDisplay();
+ }
}
--- /dev/null 2009-12-17 12:29:38.000000000 -0800
+++ autotest/frontend/client/src/autotest/planner/triage/TriagePopup.java
2010-04-06 15:56:40.000000000 -0700
@@ -0,0 +1,152 @@
+package autotest.planner.triage;
+
+import autotest.common.JsonRpcCallback;
+import autotest.common.JsonRpcProxy;
+import autotest.common.StaticDataRepository;
+import autotest.common.ui.SimplifiedList;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.HasClickHandlers;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.event.logical.shared.HasCloseHandlers;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONBoolean;
+import com.google.gwt.json.client.JSONNumber;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONString;
+import com.google.gwt.json.client.JSONValue;
+import com.google.gwt.user.client.ui.HasText;
+import com.google.gwt.user.client.ui.HasValue;
+import com.google.gwt.user.client.ui.PopupPanel;
+
+public class TriagePopup implements ClickHandler, CloseHandler<PopupPanel> {
+ public static interface Display extends HasCloseHandlers<PopupPanel> {
+ public HasClickHandlers getCloseButton();
+ public HasText getLabelsField();
+ public HasText getKeyvalsField();
+ public HasText getBugsField();
+ public HasText getReasonField();
+ public SimplifiedList getHostActionField();
+ public SimplifiedList getTestActionField();
+ public HasValue<Boolean> getInvalidateField();
+ public HasClickHandlers getApplyButton();
+ public void center();
+ public void hide();
+ }
+
+ public static interface Listener {
+ public void onTriage(TriagePopup source);
+ }
+
+ private Display display;
+ private Listener listener;
+ private int id;
+ private boolean triaged = false;
+
+ public TriagePopup(Listener listener, int id) {
+ this.listener = listener;
+ this.id = id;
+ }
+
+ public void bindDisplay(Display display) {
+ this.display = display;
+ display.addCloseHandler(this);
+ populateActionsFields();
+ setHandlers();
+ }
+
+ public void render() {
+ display.center();
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ private void populateActionsFields() {
+ populateList("host_actions", display.getHostActionField());
+ populateList("test_actions", display.getTestActionField());
+ }
+
+ private void populateList(String staticDataKey, SimplifiedList list) {
+ JSONArray choices =
StaticDataRepository.getRepository().getData(staticDataKey).isArray();
+ for (int i = 0; i < choices.size(); i++) {
+ String item = choices.get(i).isString().stringValue();
+ list.addItem(item, item);
+ }
+ }
+
+ private void setHandlers() {
+ display.getCloseButton().addClickHandler(this);
+ display.getApplyButton().addClickHandler(this);
+ }
+
+ @Override
+ public void onClick(ClickEvent event) {
+ if (event.getSource() == display.getCloseButton()) {
+ display.hide();
+ } else {
+ assert event.getSource() == display.getApplyButton();
+ JsonRpcProxy proxy = JsonRpcProxy.getProxy();
+
+ JSONObject params = getParams();
+
+ proxy.rpcCall("process_failure", params, new JsonRpcCallback() {
+ @Override
+ public void onSuccess(JSONValue result) {
+ triaged = true;
+ display.hide();
+ }
+ });
+ }
+ }
+
+ private JSONObject getParams() {
+ JSONObject params = new JSONObject();
+ params.put("failure_id", new JSONNumber(id));
+ params.put("host_action", new
JSONString(display.getHostActionField().getSelectedName()));
+ params.put("test_action", new
JSONString(display.getTestActionField().getSelectedName()));
+
+ if (!display.getLabelsField().getText().trim().equals("")) {
+ params.put("labels",
parseCommaDelimited(display.getLabelsField().getText()));
+ }
+
+ if (!display.getKeyvalsField().getText().trim().equals("")) {
+ JSONObject keyvals = new JSONObject();
+ for (String keyval :
display.getKeyvalsField().getText().split("\n")) {
+ String split[] = keyval.split("=", 2);
+ keyvals.put(split[0].trim(), new JSONString(split[1].trim()));
+ }
+ params.put("keyvals", keyvals);
+ }
+
+ if (!display.getBugsField().getText().trim().equals("")) {
+ params.put("bugs",
parseCommaDelimited(display.getBugsField().getText()));
+ }
+
+ if (!display.getReasonField().getText().trim().equals("")) {
+ params.put("reason", new
JSONString(display.getReasonField().getText()));
+ }
+
+ params.put("invalidate",
JSONBoolean.getInstance(display.getInvalidateField().getValue()));
+
+ return params;
+ }
+
+ private JSONArray parseCommaDelimited(String line) {
+ JSONArray values = new JSONArray();
+ for (String value : line.split(",")) {
+ values.set(values.size(), new JSONString(value.trim()));
+ }
+ return values;
+ }
+
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event) {
+ if (triaged) {
+ listener.onTriage(this);
+ }
+ }
+}
--- /dev/null 2009-12-17 12:29:38.000000000 -0800
+++
autotest/frontend/client/src/autotest/planner/triage/TriagePopupDisplay.java
2010-04-06 15:56:40.000000000 -0700
@@ -0,0 +1,113 @@
+package autotest.planner.triage;
+
+import autotest.common.ui.ExtendedListBox;
+import autotest.common.ui.SimplifiedList;
+import autotest.planner.resources.PlannerImageBundle;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.HasClickHandlers;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.HasText;
+import com.google.gwt.user.client.ui.HasValue;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.TextArea;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class TriagePopupDisplay extends PopupPanel implements
TriagePopup.Display {
+ private Panel container = new VerticalPanel();
+ private Image closeX =
+ ((PlannerImageBundle)
GWT.create(PlannerImageBundle.class)).close().createImage();
+ private TextBox labels = new TextBox();
+ private TextArea keyvals = new TextArea();
+ private TextBox bugs = new TextBox();
+ private TextBox reason = new TextBox();
+ private ExtendedListBox hostAction = new ExtendedListBox();
+ private ExtendedListBox testAction = new ExtendedListBox();
+ private CheckBox invalidate = new CheckBox("Invalidate Test");
+ private Button apply = new Button("Apply");
+
+ public TriagePopupDisplay() {
+ super(false, true);
+
+ HorizontalPanel topPanel = new HorizontalPanel();
+ topPanel.setWidth("100%");
+ topPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
+ topPanel.add(closeX);
+ container.add(topPanel);
+
+ FlexTable bottomTable = new FlexTable();
+ addRow(bottomTable, "Labels", labels);
+ addRow(bottomTable, "Keyvals", keyvals);
+ addRow(bottomTable, "Bugs", bugs);
+ addRow(bottomTable, "Reason", reason);
+ addRow(bottomTable, "Host", hostAction);
+ addRow(bottomTable, "Test", testAction);
+ addRow(bottomTable, null, invalidate);
+ container.add(bottomTable);
+
+ container.add(apply);
+
+ setWidget(container);
+ }
+
+ private void addRow(FlexTable table, String label, Widget field) {
+ int row = table.getRowCount();
+ if (label != null) {
+ table.setText(row, 0, label + ":");
+ }
+ table.setWidget(row, 1, field);
+ }
+
+ @Override
+ public HasClickHandlers getApplyButton() {
+ return apply;
+ }
+
+ @Override
+ public HasText getBugsField() {
+ return bugs;
+ }
+
+ @Override
+ public HasClickHandlers getCloseButton() {
+ return closeX;
+ }
+
+ @Override
+ public SimplifiedList getHostActionField() {
+ return hostAction;
+ }
+
+ @Override
+ public HasValue<Boolean> getInvalidateField() {
+ return invalidate;
+ }
+
+ @Override
+ public HasText getKeyvalsField() {
+ return keyvals;
+ }
+
+ @Override
+ public HasText getLabelsField() {
+ return labels;
+ }
+
+ @Override
+ public HasText getReasonField() {
+ return reason;
+ }
+
+ @Override
+ public SimplifiedList getTestActionField() {
+ return testAction;
+ }
+}
--- /dev/null 2009-12-17 12:29:38.000000000 -0800
+++ autotest/frontend/migrations/057_add_planner_triage_actions.py
2010-04-06 15:56:40.000000000 -0700
@@ -0,0 +1,15 @@
+UP_SQL = """
+ALTER TABLE planner_test_runs
+ADD COLUMN invalidated TINYINT(1) DEFAULT FALSE;
+
+ALTER TABLE planner_test_jobs
+ADD COLUMN requires_rerun TINYINT(1) DEFAULT FALSE;
+"""
+
+DOWN_SQL = """
+ALTER TABLE planner_test_jobs
+DROP COLUMN requires_rerun;
+
+ALTER TABLE planner_test_runs
+DROP COLUMN invalidated;
+"""
--- /dev/null 2009-12-17 12:29:38.000000000 -0800
+++ autotest/frontend/planner/failure_actions.py 2010-04-06
15:56:40.000000000 -0700
@@ -0,0 +1,17 @@
+import common
+from autotest_lib.client.common_lib import enum, utils
+
+
+def _site_host_actions_dummy():
+ return []
+
+_site_host_actions = utils.import_site_function(
+ __file__, 'autotest_lib.frontend.planner.site_failure_actions',
+ 'site_host_actions', _site_host_actions_dummy)
+
+HostAction = enum.Enum(
+ string_values=True,
+ *(_site_host_actions() + ['Block', 'Unblock', 'Reinstall']))
+
+
+TestAction = enum.Enum('Skip', 'Rerun', string_values=True)
--- autotest/frontend/planner/models.py 2010-04-06 14:05:56.000000000 -0700
+++ autotest/frontend/planner/models.py 2010-04-06 15:56:40.000000000 -0700
@@ -167,6 +167,7 @@
"""
test_config = dbmodels.ForeignKey(TestConfig)
afe_job = dbmodels.ForeignKey(afe_models.Job)
+ requires_rerun = dbmodels.BooleanField(default=False)
class Meta:
db_table = 'planner_test_jobs'
@@ -222,6 +223,7 @@
finalized = dbmodels.BooleanField(default=False)
seen = dbmodels.BooleanField(default=False)
triaged = dbmodels.BooleanField(default=False)
+ invalidated = dbmodels.BooleanField(default=False)
bugs = dbmodels.ManyToManyField(Bug, null=True,
db_table='planner_test_run_bugs')
--- autotest/frontend/planner/rpc_interface.py 2010-04-06 14:05:56.000000000
-0700
+++ autotest/frontend/planner/rpc_interface.py 2010-04-06 15:56:40.000000000
-0700
@@ -13,6 +13,7 @@
from autotest_lib.frontend.afe import rpc_utils as afe_rpc_utils
from autotest_lib.frontend.tko import models as tko_models
from autotest_lib.frontend.planner import models, rpc_utils, model_attributes
+from autotest_lib.frontend.planner import failure_actions
from autotest_lib.client.common_lib import utils
# basic getter/setter calls
@@ -207,7 +208,7 @@
tko_test_idx: the ID of the TKO test added
hostname: the host added
"""
- plan = models.Plan.objects.get(id=plan_id)
+ plan = models.Plan.smart_get(plan_id)
updated = []
for planner_job in plan.job_set.all():
@@ -298,6 +299,90 @@
config.skipped_hosts.add(afe_models.Host.objects.get(hostname=hostname))
+def mark_failures_as_seen(failure_ids):
+ """
+ Marks a set of failures as 'seen'
+
+ @param failure_ids: A list of failure IDs, as returned by get_failures(),
to
+ mark as seen
+ """
+ models.TestRun.objects.filter(id__in=failure_ids).update(seen=True)
+
+
+def process_failure(failure_id, host_action, test_action, labels=(),
+ keyvals=None, bugs=(), reason=None, invalidate=False):
+ """
+ Triage a failure
+
+ @param failure_id: The failure ID, as returned by get_failures()
+ @param host_action: One of 'Block', 'Unblock', 'Reinstall'
+ @param test_action: One of 'Skip', 'Rerun'
+
+ @param labels: Test labels to apply, by name
+ @param keyvals: Dictionary of job keyvals to add (or replace)
+ @param bugs: List of bug IDs to associate with this failure
+ @param reason: An override for the test failure reason
+ @param invalidate: True if failure should be invalidated for the purposes
of
+ reporting. Defaults to False.
+ """
+ if keyvals is None:
+ keyvals = {}
+
+ host_choices = failure_actions.HostAction.values
+ test_choices = failure_actions.TestAction.values
+ if host_action not in host_choices:
+ raise model_logic.ValidationError(
+ {'host_action': ('host action %s not valid; must be one of %s'
+ % (host_action, ', '.join(host_choices)))})
+ if test_action not in test_choices:
+ raise model_logic.ValidationError(
+ {'test_action': ('test action %s not valid; must be one of %s'
+ % (test_action, ', '.join(test_choices)))})
+
+ failure = models.TestRun.objects.get(id=failure_id)
+
+ rpc_utils.process_host_action(failure.host, host_action)
+ rpc_utils.process_test_action(failure.test_job, test_action)
+
+ # Add the test labels
+ for label in labels:
+ tko_test_label, _ = (
+ tko_models.TestLabel.objects.get_or_create(name=label))
+ failure.tko_test.testlabel_set.add(tko_test_label)
+
+ # Set the job keyvals
+ for key, value in keyvals.iteritems():
+ keyval, created = tko_models.JobKeyval.objects.get_or_create(
+ job=failure.tko_test.job, key=key)
+ if not created:
+ tko_models.JobKeyval.objects.create(job=failure.tko_test.job,
+ key='original_' + key,
+ value=keyval.value)
+ keyval.value = value
+ keyval.save()
+
+ # Add the bugs
+ for bug_id in bugs:
+ bug, _ = models.Bug.objects.get_or_create(external_uid=bug_id)
+ failure.bugs.add(bug)
+
+ # Set the failure reason
+ if reason is not None:
+ tko_models.TestAttribute.objects.create(test=failure.tko_test,
+ attribute='original_reason',
+ value=failure.tko_test.reason)
+ failure.tko_test.reason = reason
+ failure.tko_test.save()
+
+ # Set 'invalidated', 'seen', and 'triaged'
+ failure.invalidated = invalidate
+ failure.seen = True
+ failure.triaged = True
+ failure.save()
+
+
def get_static_data():
- result = {'motd': afe_rpc_utils.get_motd()}
+ result = {'motd': afe_rpc_utils.get_motd(),
+ 'host_actions': sorted(failure_actions.HostAction.values),
+ 'test_actions': sorted(failure_actions.TestAction.values)}
return result
--- autotest/frontend/planner/rpc_interface_unittest.py 2010-04-06
14:05:56.000000000 -0700
+++ autotest/frontend/planner/rpc_interface_unittest.py 2010-04-06
15:56:40.000000000 -0700
@@ -5,6 +5,7 @@
from autotest_lib.frontend import setup_django_environment
from autotest_lib.frontend.planner import planner_test_utils, model_attributes
from autotest_lib.frontend.planner import rpc_interface, models, rpc_utils
+from autotest_lib.frontend.planner import failure_actions
from autotest_lib.frontend.afe import model_logic
from autotest_lib.frontend.afe import models as afe_models
from autotest_lib.frontend.tko import models as tko_models
@@ -162,5 +163,55 @@
self.god.check_playback()
+ def test_process_failure(self):
+ self._setup_active_plan()
+ tko_test = tko_models.Test.objects.create(job=self._tko_job,
+ machine=self._tko_machine,
+ kernel=self._tko_kernel,
+ status=self._running_status)
+ failure = models.TestRun.objects.create(
+ plan=self._plan,
+ test_job=self._planner_job,
+ tko_test=tko_test,
+ host=self._planner_host,
+ status=model_attributes.TestRunStatus.FAILED,
+ finalized=True, seen=False, triaged=False)
+ host_action = failure_actions.HostAction.UNBLOCK
+ test_action = failure_actions.TestAction.SKIP
+ labels = ['label1', 'label2']
+ keyvals = {'key1': 'value1',
+ 'key2': 'value2'}
+ bugs = ['bug1', 'bug2']
+ reason = 'overriden reason'
+ invalidate = True
+
+ self.god.stub_function(rpc_utils, 'process_host_action')
+ self.god.stub_function(rpc_utils, 'process_test_action')
+
+ rpc_utils.process_host_action.expect_call(self._planner_host,
+ host_action)
+ rpc_utils.process_test_action.expect_call(self._planner_job,
+ test_action)
+
+ rpc_interface.process_failure(failure.id, host_action, test_action,
+ labels, keyvals, bugs, reason,
invalidate)
+ failure = models.TestRun.objects.get(id=failure.id)
+
+ self.assertEqual(
+ set(failure.tko_test.testlabel_set.all()),
+ set(tko_models.TestLabel.objects.filter(name__in=labels)))
+ self.assertEqual(
+ set(failure.tko_test.job.jobkeyval_set.all()),
+ set(tko_models.JobKeyval.objects.filter(
+ key__in=keyvals.iterkeys())))
+ self.assertEqual(set(failure.bugs.all()),
+ set(models.Bug.objects.filter(external_uid__in=bugs)))
+ self.assertEqual(failure.tko_test.reason, reason)
+ self.assertEqual(failure.invalidated, invalidate)
+ self.assertTrue(failure.seen)
+ self.assertTrue(failure.triaged)
+ self.god.check_playback()
+
+
if __name__ == '__main__':
unittest.main()
--- autotest/frontend/planner/rpc_utils.py 2010-04-06 14:05:56.000000000
-0700
+++ autotest/frontend/planner/rpc_utils.py 2010-04-06 15:56:40.000000000
-0700
@@ -2,6 +2,7 @@
import os
from autotest_lib.frontend.afe import models as afe_models, model_logic
from autotest_lib.frontend.planner import models, model_attributes
+from autotest_lib.frontend.planner import failure_actions
from autotest_lib.client.common_lib import global_config, utils
@@ -159,3 +160,50 @@
host=planner_host)
test_run.status = status
test_run.save()
+
+
+def _site_process_host_action_dummy(host, action):
+ return False
+
+
+def process_host_action(host, action):
+ """
+ Takes the specified action on the host
+ """
+ HostAction = failure_actions.HostAction
+ if action not in HostAction.values:
+ raise ValueError('Unexpected host action %s' % action)
+
+ site_process = utils.import_site_function(
+ __file__, 'autotest_lib.frontend.planner.site_rpc_utils',
+ 'site_process_host_action', _site_process_host_action_dummy)
+
+ if not site_process(host, action):
+ # site_process_host_action returns True and and only if it matched a
+ # site-specific processing option
+ if action == HostAction.BLOCK:
+ host.blocked = True
+ elif action == HostAction.UNBLOCK:
+ host.blocked = False
+ else:
+ assert action == HostAction.REINSTALL
+ raise NotImplemented('TODO: implement reinstall')
+
+ host.save()
+
+
+def process_test_action(planner_job, action):
+ """
+ Takes the specified action for this planner job
+ """
+ TestAction = failure_actions.TestAction
+ if action not in TestAction.values:
+ raise ValueError('Unexpected test action %s' % action)
+
+ if action == TestAction.SKIP:
+ # Do nothing
+ pass
+ else:
+ assert action == TestAction.RERUN
+ planner_job.requires_rerun = True
+ planner_job.save()
--- autotest/frontend/planner/rpc_utils_unittest.py 2010-04-06
14:05:56.000000000 -0700
+++ autotest/frontend/planner/rpc_utils_unittest.py 2010-04-06
15:56:40.000000000 -0700
@@ -5,8 +5,7 @@
from autotest_lib.frontend import setup_django_environment
from autotest_lib.frontend.planner import planner_test_utils
from autotest_lib.frontend.afe import model_logic, models as afe_models
-from autotest_lib.frontend.afe import rpc_interface as afe_rpc_interface
-from autotest_lib.frontend.planner import models, rpc_utils
+from autotest_lib.frontend.planner import models, rpc_utils, failure_actions
from autotest_lib.client.common_lib import utils, host_queue_entry_states
@@ -133,5 +132,88 @@
self.assertTrue(self._planner_host.complete)
+ def _replace_site_process_host_action(self, replacement):
+ self.god.stub_function(utils, 'import_site_function')
+ utils.import_site_function.expect_any_call().and_return(replacement)
+
+
+ def _remove_site_process_host_action(self):
+ def _site_process_host_action_dummy(host, action):
+ return False
+ self._replace_site_process_host_action(_site_process_host_action_dummy)
+
+
+ def test_process_host_action_block(self):
+ self._remove_site_process_host_action()
+ host = models.Host.objects.create(plan=self._plan, host=self.hosts[0],
+ blocked=False)
+ assert not host.blocked
+
+ rpc_utils.process_host_action(host, failure_actions.HostAction.BLOCK)
+ host = models.Host.objects.get(id=host.id)
+
+ self.assertTrue(host.blocked)
+ self.god.check_playback()
+
+
+ def test_process_host_action_unblock(self):
+ self._remove_site_process_host_action()
+ host = models.Host.objects.create(plan=self._plan, host=self.hosts[0],
+ blocked=True)
+ assert host.blocked
+
+ rpc_utils.process_host_action(host, failure_actions.HostAction.UNBLOCK)
+ host = models.Host.objects.get(id=host.id)
+
+ self.assertFalse(host.blocked)
+ self.god.check_playback()
+
+
+ def test_process_host_action_site(self):
+ self._remove_site_process_host_action
+ action = object()
+ failure_actions.HostAction.values.append(action)
+ host = models.Host.objects.create(plan=self._plan, host=self.hosts[0])
+
+ self.assertRaises(AssertionError, rpc_utils.process_host_action,
+ host, action)
+ self.god.check_playback()
+
+ self._called = False
+ def _site_process_host_action(host, action):
+ self._called = True
+ return True
+ self._replace_site_process_host_action(_site_process_host_action)
+
+ rpc_utils.process_host_action(host, action)
+
+ self.assertTrue(self._called)
+ self.god.check_playback()
+
+
+ def test_process_test_action_skip(self):
+ self._setup_active_plan()
+ planner_job = self._planner_job
+ assert not planner_job.requires_rerun
+
+ rpc_utils.process_test_action(planner_job,
+ failure_actions.TestAction.SKIP)
+ planner_job = models.Job.objects.get(id=planner_job.id)
+
+ self.assertFalse(planner_job.requires_rerun)
+
+
+ def test_process_test_action_rerun(self):
+ self._setup_active_plan()
+ planner_job = self._planner_job
+ assert not planner_job.requires_rerun
+
+ rpc_utils.process_test_action(planner_job,
+ failure_actions.TestAction.RERUN)
+ planner_job = models.Job.objects.get(id=planner_job.id)
+
+ self.assertTrue(planner_job.requires_rerun)
+
+
if __name__ == '__main__':
unittest.main()
_______________________________________________
Autotest mailing list
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest