Change Triage View's tables to ScrollTable, and implement bulk triage
Signed-off-by: James Ren <[email protected]>
Index: autotest/frontend/planner/rpc_utils_unittest.py
===================================================================
--- autotest/frontend/planner/rpc_utils_unittest.py (revision 4446)
+++ autotest/frontend/planner/rpc_utils_unittest.py (working copy)
@@ -3,9 +3,11 @@
import unittest
import common
from autotest_lib.frontend import setup_django_environment
-from autotest_lib.frontend.planner import planner_test_utils
+from autotest_lib.frontend import setup_test_environment
from autotest_lib.frontend.afe import model_logic, models as afe_models
+from autotest_lib.frontend.planner import planner_test_utils, model_attributes
from autotest_lib.frontend.planner import models, rpc_utils, failure_actions
+from autotest_lib.frontend.tko import models as tko_models
from autotest_lib.client.common_lib import utils, host_queue_entry_states
@@ -132,6 +134,58 @@
self.assertTrue(self._planner_host.complete)
+ 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_utils.process_failure(
+ failure_id=failure.id, host_action=host_action,
+ test_action=test_action, labels=labels, keyvals=keyvals,
+ bugs=bugs, reason=reason, invalidate=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()
+
+
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)
@@ -149,7 +203,7 @@
blocked=False)
assert not host.blocked
- rpc_utils.process_host_action(host, failure_actions.HostAction.BLOCK)
+ rpc_utils._process_host_action(host, failure_actions.HostAction.BLOCK)
host = models.Host.objects.get(id=host.id)
self.assertTrue(host.blocked)
@@ -162,7 +216,7 @@
blocked=True)
assert host.blocked
- rpc_utils.process_host_action(host, failure_actions.HostAction.UNBLOCK)
+ rpc_utils._process_host_action(host, failure_actions.HostAction.UNBLOCK)
host = models.Host.objects.get(id=host.id)
self.assertFalse(host.blocked)
@@ -175,7 +229,7 @@
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,
+ self.assertRaises(AssertionError, rpc_utils._process_host_action,
host, action)
self.god.check_playback()
@@ -185,7 +239,7 @@
return True
self._replace_site_process_host_action(_site_process_host_action)
- rpc_utils.process_host_action(host, action)
+ rpc_utils._process_host_action(host, action)
self.assertTrue(self._called)
self.god.check_playback()
@@ -196,8 +250,8 @@
planner_job = self._planner_job
assert not planner_job.requires_rerun
- rpc_utils.process_test_action(planner_job,
- failure_actions.TestAction.SKIP)
+ 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)
@@ -208,8 +262,8 @@
planner_job = self._planner_job
assert not planner_job.requires_rerun
- rpc_utils.process_test_action(planner_job,
- failure_actions.TestAction.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)
Index: autotest/frontend/planner/rpc_interface_unittest.py
===================================================================
--- autotest/frontend/planner/rpc_interface_unittest.py (revision 4446)
+++ autotest/frontend/planner/rpc_interface_unittest.py (working copy)
@@ -3,6 +3,7 @@
import unittest
import common
from autotest_lib.frontend import setup_django_environment
+from autotest_lib.frontend import setup_test_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
@@ -163,55 +164,5 @@
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()
Index: autotest/frontend/planner/rpc_utils.py
===================================================================
--- autotest/frontend/planner/rpc_utils.py (revision 4446)
+++ autotest/frontend/planner/rpc_utils.py (working copy)
@@ -3,6 +3,7 @@
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.frontend.tko import models as tko_models
from autotest_lib.client.common_lib import global_config, utils, global_config
@@ -169,11 +170,58 @@
test_run.save()
+def process_failure(failure_id, host_action, test_action, labels, keyvals,
+ bugs, reason, invalidate):
+ if keyvals is None:
+ keyvals = {}
+
+ failure = models.TestRun.objects.get(id=failure_id)
+
+ _process_host_action(failure.host, host_action)
+ _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 _site_process_host_action_dummy(host, action):
return False
-def process_host_action(host, action):
+def _process_host_action(host, action):
"""
Takes the specified action on the host
"""
@@ -199,7 +247,7 @@
host.save()
-def process_test_action(planner_job, action):
+def _process_test_action(planner_job, action):
"""
Takes the specified action for this planner job
"""
Index: autotest/frontend/planner/rpc_interface.py
===================================================================
--- autotest/frontend/planner/rpc_interface.py (revision 4446)
+++ autotest/frontend/planner/rpc_interface.py (working copy)
@@ -254,10 +254,11 @@
failures = plan.testrun_set.filter(
finalized=True, triaged=False,
status=model_attributes.TestRunStatus.FAILED)
- failures = failures.select_related('test_job__test', 'host__host',
- 'tko_test')
+ failures = failures.order_by('seen').select_related('test_job__test',
+ 'host__host',
+ 'tko_test')
for failure in failures:
- test_name = '%s:%s' % (
+ test_name = '%s: %s' % (
failure.test_job.test_config.alias, failure.tko_test.test)
group_failures = result.setdefault(failure.tko_test.reason, [])
@@ -309,8 +310,8 @@
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):
+def process_failures(failure_ids, host_action, test_action, labels=(),
+ keyvals=None, bugs=(), reason=None, invalidate=False):
"""
Triage a failure
@@ -325,9 +326,6 @@
@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:
@@ -339,53 +337,17 @@
{'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)
+ for failure_id in failure_ids:
+ rpc_utils.process_failure(
+ failure_id=failure_id, host_action=host_action,
+ test_action=test_action, labels=labels, keyvals=keyvals,
+ bugs=bugs, reason=reason, invalidate=invalidate)
- 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_motd():
return afe_rpc_utils.get_motd()
-
def get_static_data():
result = {'motd': get_motd(),
'host_actions': sorted(failure_actions.HostAction.values),
Index: autotest/frontend/client/src/autotest/planner/triage/FailureTable.java
===================================================================
--- autotest/frontend/client/src/autotest/planner/triage/FailureTable.java (revision 4446)
+++ autotest/frontend/client/src/autotest/planner/triage/FailureTable.java (working copy)
@@ -1,26 +1,25 @@
package autotest.planner.triage;
-import autotest.common.spreadsheet.Spreadsheet;
-import autotest.common.spreadsheet.Spreadsheet.CellInfo;
-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.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.user.client.IncrementalCommand;
+import com.google.gwt.user.client.ui.HasValue;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
-class FailureTable implements SpreadsheetListener, TriagePopup.Listener {
+class FailureTable implements ClickHandler {
- public interface Display {
- public Spreadsheet getSpreadsheet();
- public TriagePopup.Display generateTriagePopupDisplay();
+ public static final String[] COLUMN_NAMES = {"Machine", "Test", "Reason"};
+
+ public static interface Display {
+ public void addRow(String[] cells, boolean isNew);
+ public void setAllRowsSelected(boolean selected);
+ public HasClickHandlers getSelectAllControl();
+ public HasValue<Boolean> getSelectAllValue();
+ public Set<Integer> getSelectedFailures();
}
private static class Failure {
@@ -42,7 +41,8 @@
}
public static Failure fromJsonObject(JSONObject failureObj) {
- return new Failure((int) failureObj.get("id").isNumber().doubleValue(),
+ return new Failure(
+ (int) failureObj.get("id").isNumber().doubleValue(),
failureObj.get("machine").isString().stringValue(),
failureObj.get("blocked").isBoolean().booleanValue(),
failureObj.get("test_name").isString().stringValue(),
@@ -52,139 +52,37 @@
}
private Display display;
- private String group;
- private LinkedList<Failure> failures = new LinkedList<Failure>();
- private boolean rendered;
+ private List<Integer> failureIds = new ArrayList<Integer>();
- public FailureTable(String group) {
- this.group = group;
- }
-
public void bindDisplay(Display display) {
this.display = display;
- display.getSpreadsheet().setListener(this);
+ display.getSelectAllControl().addClickHandler(this);
}
public void addFailure(JSONObject failureObj) {
- setRendered(false);
-
Failure failure = Failure.fromJsonObject(failureObj);
- if (failure.seen) {
- failures.addLast(failure);
- } else {
- failures.addFirst(failure);
+ String machineDisplay = failure.machine;
+ if (failure.blocked) {
+ machineDisplay += " (blocked)";
}
- }
-
- public Display getDisplay() {
- if (!rendered) {
- renderDisplay();
- }
- return display;
+ display.addRow(
+ new String[] {machineDisplay, failure.testName, failure.reason}, !failure.seen);
+ failureIds.add(failure.id);
}
-
- 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();
- columnFields.add("group");
- columnFields.add("failure");
- spreadsheet.setHeaderFields(rowFields, columnFields);
-
- for (int i = 0; i < failures.size(); i++) {
- Failure failure = failures.get(i);
- String machine = (i+1) + ": " + failure.machine;
- if (failure.blocked) {
- machine += " (blocked)";
- }
- spreadsheet.addRowHeader(Collections.singletonList(machine));
- }
- spreadsheet.addColumnHeader(createHeaderGroup("Test"));
- spreadsheet.addColumnHeader(createHeaderGroup("Reason"));
-
- spreadsheet.prepareForData();
-
- for (int row = 0; row < failures.size(); row++) {
- CellInfo test = spreadsheet.getCellInfo(row, 0);
- CellInfo reason = spreadsheet.getCellInfo(row, 1);
- Failure failure = failures.get(row);
-
- 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>";
- reason.contents = "<b>" + reason.contents + "</b>";
- }
- }
-
- spreadsheet.setVisible(true);
-
- spreadsheet.render(new IncrementalCommand() {
- @Override
- public boolean execute() {
- setRendered(true);
- return false;
- }
- });
- }
-
- private List<String> createHeaderGroup(String label) {
- List<String> header = new ArrayList<String>();
- header.add(group);
- header.add(label);
- return header;
- }
@Override
- public void onCellClicked(CellInfo cellInfo, boolean isRightClick) {
- if (cellInfo.testIndex == 0) {
- return;
- }
-
- TriagePopup popup = new TriagePopup(this, cellInfo.testIndex);
- popup.bindDisplay(display.generateTriagePopupDisplay());
- popup.render();
+ public void onClick(ClickEvent event) {
+ assert event.getSource() == display.getSelectAllControl();
+ display.setAllRowsSelected(display.getSelectAllValue().getValue());
}
-
- @Override
- public void onTriage(TriagePopup source) {
- if (removeFailure(source.getId())) {
- if (!failures.isEmpty()) {
- // If no more failures, leave the spreadsheet invisible
- 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;
- }
+ public List<Integer> getSelectedFailureIds() {
+ List<Integer> selected = new ArrayList<Integer>();
+ for (int i : display.getSelectedFailures()) {
+ selected.add(failureIds.get(i));
}
-
- /*
- * 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;
+ return selected;
}
}
Index: autotest/frontend/client/src/autotest/planner/triage/TriagePopup.java
===================================================================
--- autotest/frontend/client/src/autotest/planner/triage/TriagePopup.java (revision 4446)
+++ autotest/frontend/client/src/autotest/planner/triage/TriagePopup.java (working copy)
@@ -3,6 +3,7 @@
import autotest.common.JsonRpcCallback;
import autotest.common.JsonRpcProxy;
import autotest.common.StaticDataRepository;
+import autotest.common.Utils;
import autotest.common.ui.SimplifiedList;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -13,7 +14,6 @@
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;
@@ -21,6 +21,8 @@
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.PopupPanel;
+import java.util.List;
+
public class TriagePopup implements ClickHandler, CloseHandler<PopupPanel> {
public static interface Display extends HasCloseHandlers<PopupPanel> {
public HasClickHandlers getCloseButton();
@@ -37,17 +39,17 @@
}
public static interface Listener {
- public void onTriage(TriagePopup source);
+ public void onTriage();
}
private Display display;
private Listener listener;
- private int id;
+ private List<Integer> ids;
private boolean triaged = false;
- public TriagePopup(Listener listener, int id) {
+ public TriagePopup(Listener listener, List<Integer> ids) {
this.listener = listener;
- this.id = id;
+ this.ids = ids;
}
public void bindDisplay(Display display) {
@@ -61,8 +63,8 @@
display.center();
}
- public int getId() {
- return id;
+ public List<Integer> getIds() {
+ return ids;
}
private void populateActionsFields() {
@@ -93,7 +95,7 @@
JSONObject params = getParams();
- proxy.rpcCall("process_failure", params, new JsonRpcCallback() {
+ proxy.rpcCall("process_failures", params, new JsonRpcCallback() {
@Override
public void onSuccess(JSONValue result) {
triaged = true;
@@ -105,7 +107,7 @@
private JSONObject getParams() {
JSONObject params = new JSONObject();
- params.put("failure_id", new JSONNumber(id));
+ params.put("failure_ids", Utils.integersToJSON(ids));
params.put("host_action", new JSONString(display.getHostActionField().getSelectedName()));
params.put("test_action", new JSONString(display.getTestActionField().getSelectedName()));
@@ -146,7 +148,7 @@
@Override
public void onClose(CloseEvent<PopupPanel> event) {
if (triaged) {
- listener.onTriage(this);
+ listener.onTriage();
}
}
}
Index: autotest/frontend/client/src/autotest/planner/triage/FailureTableDisplay.java
===================================================================
--- autotest/frontend/client/src/autotest/planner/triage/FailureTableDisplay.java (revision 4446)
+++ autotest/frontend/client/src/autotest/planner/triage/FailureTableDisplay.java (working copy)
@@ -1,24 +1,86 @@
package autotest.planner.triage;
-import autotest.common.spreadsheet.Spreadsheet;
-
+import com.google.gwt.event.dom.client.HasClickHandlers;
+import com.google.gwt.gen2.table.client.FixedWidthFlexTable;
+import com.google.gwt.gen2.table.client.FixedWidthGrid;
+import com.google.gwt.gen2.table.client.ScrollTable;
+import com.google.gwt.gen2.table.client.AbstractScrollTable.ResizePolicy;
+import com.google.gwt.gen2.table.client.AbstractScrollTable.ScrollPolicy;
+import com.google.gwt.gen2.table.client.AbstractScrollTable.SortPolicy;
+import com.google.gwt.gen2.table.client.SelectionGrid.SelectionPolicy;
+import com.google.gwt.gen2.table.override.client.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HasValue;
+import java.util.Set;
+
+
public class FailureTableDisplay extends Composite implements FailureTable.Display {
- private Spreadsheet spreadsheet = new Spreadsheet();
+ private FixedWidthGrid dataTable;
+ private ScrollTable scrollTable;
+ private CheckBox selectAll = new CheckBox();
- public FailureTableDisplay() {
- initWidget(spreadsheet);
+ public FailureTableDisplay(String group, String[] columnNames) {
+ FixedWidthFlexTable header = new FixedWidthFlexTable();
+ FlexCellFormatter formatter = header.getFlexCellFormatter();
+
+ dataTable = new FixedWidthGrid(0, columnNames.length);
+ dataTable.setSelectionPolicy(SelectionPolicy.CHECKBOX);
+
+ scrollTable = new ScrollTable(dataTable, header);
+ scrollTable.setSortPolicy(SortPolicy.DISABLED);
+ scrollTable.setResizePolicy(ResizePolicy.UNCONSTRAINED);
+ scrollTable.setScrollPolicy(ScrollPolicy.BOTH);
+ scrollTable.setHeight("200px");
+
+ header.setText(0, 0, group);
+ header.setWidget(1, 0, selectAll);
+
+ formatter.setColSpan(0, 0, columnNames.length + 1);
+ for (int i = 0; i < columnNames.length; i++) {
+ header.setText(1, i + 1, columnNames[i]);
+ header.setColumnWidth(i + 1, 1);
+ scrollTable.setHeaderColumnTruncatable(i, false);
+ scrollTable.setColumnTruncatable(i, false);
+ }
+
+ initWidget(scrollTable);
}
@Override
- public Spreadsheet getSpreadsheet() {
- return spreadsheet;
+ public void addRow(String[] cells, boolean isNew) {
+ assert dataTable.getColumnCount() == cells.length;
+
+ int row = dataTable.getRowCount();
+ dataTable.resizeRows(row + 1);
+ for (int cell = 0; cell < cells.length; cell++) {
+ dataTable.setText(row, cell, cells[cell]);
+ dataTable.setColumnWidth(cell, 1);
+ }
}
-
+
@Override
- public TriagePopup.Display generateTriagePopupDisplay() {
- return new TriagePopupDisplay();
+ public HasClickHandlers getSelectAllControl() {
+ return selectAll;
}
+
+ @Override
+ public HasValue<Boolean> getSelectAllValue() {
+ return selectAll;
+ }
+
+ public void setAllRowsSelected(boolean selected) {
+ if (selected) {
+ dataTable.selectAllRows();
+ } else {
+ dataTable.deselectAllRows();
+ }
+ }
+
+ @Override
+ public Set<Integer> getSelectedFailures() {
+ return dataTable.getSelectedRows();
+ }
}
Index: autotest/frontend/client/src/autotest/planner/triage/TriageViewDisplay.java
===================================================================
--- autotest/frontend/client/src/autotest/planner/triage/TriageViewDisplay.java (revision 4446)
+++ autotest/frontend/client/src/autotest/planner/triage/TriageViewDisplay.java (working copy)
@@ -2,34 +2,75 @@
import autotest.common.ui.NotifyManager;
+import com.google.gwt.event.dom.client.HasClickHandlers;
+import com.google.gwt.event.logical.shared.ResizeEvent;
+import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
-public class TriageViewDisplay implements TriageViewPresenter.Display {
+public class TriageViewDisplay implements TriageViewPresenter.Display, ResizeHandler {
+ private static final int HEIGHT_FUDGE = 300;
+
private VerticalPanel container = new VerticalPanel();
+ private ScrollPanel scroll = new ScrollPanel(container);
+ private Button triage = new Button("Triage");
public void initialize(HTMLPanel htmlPanel) {
container.setSpacing(25);
- htmlPanel.add(container, "triage_failure_tables");
+ container.setWidth("90%");
+
+ scroll.setSize("100%", getHeightParam(Window.getClientHeight()));
+ scroll.setVisible(false);
+ triage.setVisible(false);
+
+ htmlPanel.add(scroll, "triage_failure_tables");
+ htmlPanel.add(triage, "triage_button");
+
+ Window.addResizeHandler(this);
}
@Override
public void setLoading(boolean loading) {
NotifyManager.getInstance().setLoading(loading);
- container.setVisible(!loading);
+ scroll.setVisible(!loading);
+
+ scroll.setVisible(true);
+ triage.setVisible(true);
}
@Override
- public FailureTable.Display generateFailureTable() {
- FailureTableDisplay display = new FailureTableDisplay();
+ public FailureTable.Display generateFailureTable(String group, String[] columnNames) {
+ FailureTableDisplay display = new FailureTableDisplay(group, columnNames);
container.add(display);
return display;
}
+
+ @Override
+ public TriagePopup.Display generateTriagePopupDisplay() {
+ return new TriagePopupDisplay();
+ }
@Override
public void clearAllFailureTables() {
container.clear();
}
+
+ @Override
+ public HasClickHandlers getTriageButton() {
+ return triage;
+ }
+
+ @Override
+ public void onResize(ResizeEvent event) {
+ scroll.setHeight(getHeightParam(event.getHeight()));
+ }
+
+ private String getHeightParam(int height) {
+ return (height - HEIGHT_FUDGE) + "px";
+ }
}
Index: autotest/frontend/client/src/autotest/planner/triage/TriageViewPresenter.java
===================================================================
--- autotest/frontend/client/src/autotest/planner/triage/TriageViewPresenter.java (revision 4446)
+++ autotest/frontend/client/src/autotest/planner/triage/TriageViewPresenter.java (working copy)
@@ -5,22 +5,32 @@
import autotest.common.ui.HasTabVisible;
import autotest.planner.TestPlanSelector;
+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.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
-public class TriageViewPresenter implements TestPlanSelector.Listener {
+import java.util.ArrayList;
+import java.util.List;
+
+public class TriageViewPresenter implements ClickHandler, TestPlanSelector.Listener,
+ TriagePopup.Listener {
public interface Display {
public void setLoading(boolean loading);
public void clearAllFailureTables();
- public FailureTable.Display generateFailureTable();
+ public FailureTable.Display generateFailureTable(String group, String[] columnNames);
+ public TriagePopup.Display generateTriagePopupDisplay();
+ public HasClickHandlers getTriageButton();
}
private TestPlanSelector selector;
private Display display;
private HasTabVisible tab;
+ private List<FailureTable> failureTables = new ArrayList<FailureTable>();
public TriageViewPresenter(TestPlanSelector selector, HasTabVisible tab) {
this.selector = selector;
@@ -30,6 +40,7 @@
public void bindDisplay(Display display) {
this.display = display;
+ display.getTriageButton().addClickHandler(this);
}
public void refresh() {
@@ -47,16 +58,25 @@
@Override
public void onSuccess(JSONValue result) {
display.clearAllFailureTables();
+ display.setLoading(false);
generateFailureTables(result.isObject());
+ }
+
+ @Override
+ public void onError(JSONObject errorObject) {
+ super.onError(errorObject);
display.setLoading(false);
}
});
}
- private void generateFailureTables(JSONObject failures) {
+ private void generateFailureTables(JSONObject failures) {
+ failureTables.clear();
+
for (String group : failures.keySet()) {
- FailureTable table = new FailureTable(group);
- FailureTable.Display tableDisplay = display.generateFailureTable();
+ FailureTable table = new FailureTable();
+ FailureTable.Display tableDisplay =
+ display.generateFailureTable(group, FailureTable.COLUMN_NAMES);
table.bindDisplay(tableDisplay);
JSONArray groupFailures = failures.get(group).isArray();
@@ -65,7 +85,7 @@
table.addFailure(groupFailures.get(i).isObject());
}
- table.renderDisplay();
+ failureTables.add(table);
}
}
@@ -75,4 +95,23 @@
refresh();
}
}
+
+ @Override
+ public void onClick(ClickEvent event) {
+ assert event.getSource() == display.getTriageButton();
+
+ List<Integer> failureIds = new ArrayList<Integer>();
+ for (FailureTable failure : failureTables) {
+ failureIds.addAll(failure.getSelectedFailureIds());
+ }
+
+ TriagePopup popup = new TriagePopup(this, failureIds);
+ popup.bindDisplay(display.generateTriagePopupDisplay());
+ popup.render();
+ }
+
+ @Override
+ public void onTriage() {
+ refresh();
+ }
}
Index: autotest/frontend/client/src/autotest/common/Utils.java
===================================================================
--- autotest/frontend/client/src/autotest/common/Utils.java (revision 4446)
+++ autotest/frontend/client/src/autotest/common/Utils.java (working copy)
@@ -39,6 +39,18 @@
}
return result;
}
+
+ /**
+ * Converts a collection of Java <code>Integers</code>s into a <code>JSONArray
+ * </code> of <code>JSONNumber</code>s.
+ */
+ public static JSONArray integersToJSON(Collection<Integer> integers) {
+ JSONArray result = new JSONArray();
+ for(Integer i : integers) {
+ result.set(result.size(), new JSONNumber(i));
+ }
+ return result;
+ }
/**
* Converts a <code>JSONArray</code> of <code>JSONStrings</code> to an
Index: autotest/frontend/client/src/autotest/TestPlannerClient.gwt.xml
===================================================================
--- autotest/frontend/client/src/autotest/TestPlannerClient.gwt.xml (revision 0)
+++ autotest/frontend/client/src/autotest/TestPlannerClient.gwt.xml (revision 0)
@@ -0,0 +1,18 @@
+<module>
+ <inherits name='com.google.gwt.user.User'/>
+ <inherits name='com.google.gwt.json.JSON'/>
+ <inherits name='com.google.gwt.http.HTTP'/>
+
+ <inherits name='com.google.gwt.widgetideas.WidgetIdeas'/>
+ <inherits name='com.google.gwt.gen2.table.ScrollTable'/>
+
+ <source path="planner"/>
+ <source path="common"/>
+ <entry-point class='autotest.planner.TestPlannerClient'/>
+
+ <stylesheet src='common.css'/>
+ <stylesheet src='standard.css'/>
+ <stylesheet src='scrolltable.css'/>
+ <stylesheet src='afeclient.css'/>
+ <stylesheet src='tkoclient.css'/>
+</module>
Index: autotest/frontend/client/src/autotest/public/TestPlannerClient.html
===================================================================
--- autotest/frontend/client/src/autotest/public/TestPlannerClient.html (revision 4446)
+++ autotest/frontend/client/src/autotest/public/TestPlannerClient.html (working copy)
@@ -46,6 +46,7 @@
<div id="triage_view" title="Triage View">
<div id="triage_failure_tables"></div>
+ <div id="triage_button"></div>
</div>
<div id="autoprocessed" title="Auto-processed">
Index: autotest/frontend/client/src/autotest/public/scrolltable.css
===================================================================
--- autotest/frontend/client/src/autotest/public/scrolltable.css (revision 0)
+++ autotest/frontend/client/src/autotest/public/scrolltable.css (revision 0)
@@ -0,0 +1,43 @@
+.gwt-ScrollTable {
+ border-color: #aaa;
+ border-style: solid;
+ border-width: 1px 0px 1px 1px;
+}
+
+.gwt-ScrollTable .headerWrapper {
+ background: #8bd;
+}
+
+.gwt-ScrollTable .footerWrapper {
+ border-top: 1px solid #aaa;
+ background: #8bd;
+}
+
+.gwt-ScrollTable .dataTable td,
+.gwt-ScrollTable .headerTable td,
+.gwt-ScrollTable .footerTable td {
+ border-color: #aaa;
+ border-style: solid;
+ border-width: 0px 1px 1px 0px;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.gwt-ScrollTable .headerTable td,
+.gwt-ScrollTable .footerTable td {
+ color: #fff;
+}
+
+.gwt-ScrollTable .dataTable tr.highlighted {
+ background: #C3D9FF;
+}
+
+.gwt-ScrollTable .dataTable td.highlighted {
+ background: #FFFFAA;
+ cursor: hand;
+ cursor: pointer;
+}
+
+.gwt-ScrollTable .dataTable tr.selected td {
+ background: #7AA5D6;
+}
_______________________________________________
Autotest mailing list
[email protected]
http://test.kernel.org/cgi-bin/mailman/listinfo/autotest