add the option to include verify/repair/cleanups in the job table on the host detail page.
* added RPC get_host_queue_entries_and_special_tasks, which returns both HQEs and SpecialTasks for a host in a carefully interleaved manner.
* added appropriate frontend unit tests
* added support to HostDetailView to include these entries, but have them not be selectable. this required changes to SelectionManager. I also added a utility method to Utils for opening a new window, and changed all sites that do that to call the new method.
Signed-off-by: Steve Howard <showard@google.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@3385 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/frontend/afe/model_logic.py b/frontend/afe/model_logic.py
index 32b4898..c94233b 100644
--- a/frontend/afe/model_logic.py
+++ b/frontend/afe/model_logic.py
@@ -602,6 +602,7 @@
DB layer documentation)
-extra_where: extra WHERE clause to append
"""
+ filter_data = dict(filter_data) # copy so we don't mutate the original
query_start = filter_data.pop('query_start', None)
query_limit = filter_data.pop('query_limit', None)
if query_start and not query_limit:
diff --git a/frontend/afe/models.py b/frontend/afe/models.py
index 4aa7547..48cbcf7 100644
--- a/frontend/afe/models.py
+++ b/frontend/afe/models.py
@@ -840,6 +840,13 @@
self._check_for_updated_attributes()
+ def execution_path(self):
+ """
+ Path to this entry's results (relative to the base results directory).
+ """
+ return self.execution_subdir
+
+
def host_or_metahost_name(self):
if self.host:
return self.host.hostname
@@ -983,17 +990,39 @@
null=False)
is_active = dbmodels.BooleanField(default=False, blank=False, null=False)
is_complete = dbmodels.BooleanField(default=False, blank=False, null=False)
- time_started = dbmodels.DateTimeField(null=True)
+ time_started = dbmodels.DateTimeField(null=True, blank=True)
queue_entry = dbmodels.ForeignKey(HostQueueEntry, blank=True, null=True)
objects = model_logic.ExtendedManager()
def execution_path(self):
+ """@see HostQueueEntry.execution_path()"""
return 'hosts/%s/%s-%s' % (self.host.hostname, self.id,
self.task.lower())
+ # property to emulate HostQueueEntry.status
+ @property
+ def status(self):
+ """
+ Return a host queue entry status appropriate for this task. Although
+ SpecialTasks are not HostQueueEntries, it is helpful to the user to
+ present similar statuses.
+ """
+ if self.is_complete:
+ return HostQueueEntry.Status.COMPLETED
+ if self.is_active:
+ return HostQueueEntry.Status.RUNNING
+ return HostQueueEntry.Status.QUEUED
+
+
+ # property to emulate HostQueueEntry.started_on
+ @property
+ def started_on(self):
+ return self.time_started
+
+
@classmethod
def schedule_special_task(cls, hosts, task):
"""\
diff --git a/frontend/afe/models_test.py b/frontend/afe/models_test.py
index bb20103..7094881 100644
--- a/frontend/afe/models_test.py
+++ b/frontend/afe/models_test.py
@@ -16,12 +16,26 @@
self._frontend_common_teardown()
- def test_execution_path(self):
- task = models.SpecialTask.objects.create(
+ def _create_task(self):
+ return models.SpecialTask.objects.create(
host=self.hosts[0], task=models.SpecialTask.Task.VERIFY)
+
+ def test_execution_path(self):
+ task = self._create_task()
self.assertEquals(task.execution_path(), 'hosts/host1/1-verify')
+ def test_status(self):
+ task = self._create_task()
+ self.assertEquals(task.status, 'Queued')
+
+ task.update_object(is_active=True)
+ self.assertEquals(task.status, 'Running')
+
+ task.update_object(is_active=False, is_complete=True)
+ self.assertEquals(task.status, 'Completed')
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/frontend/afe/rpc_interface.py b/frontend/afe/rpc_interface.py
index 3184665..5712f0e 100644
--- a/frontend/afe/rpc_interface.py
+++ b/frontend/afe/rpc_interface.py
@@ -650,6 +650,40 @@
return float(complete_count) / total_count
+# support for host detail view
+
+def get_host_queue_entries_and_special_tasks(hostname, query_start=None,
+ query_limit=None):
+ """
+ @returns an interleaved list of HostQueueEntries and SpecialTasks,
+ in approximate run order. each dict contains keys for type, host,
+ job, status, started_on, execution_path, and ID.
+ """
+ total_limit = None
+ if query_limit is not None:
+ total_limit = query_start + query_limit
+ filter_data = {'host__hostname': hostname,
+ 'query_limit': total_limit,
+ 'sort_by': ['-id']}
+
+ queue_entries = list(models.HostQueueEntry.query_objects(filter_data))
+ special_tasks = list(models.SpecialTask.query_objects(filter_data))
+
+ interleaved_entries = rpc_utils.interleave_entries(queue_entries,
+ special_tasks)
+ if query_start is not None:
+ interleaved_entries = interleaved_entries[query_start:]
+ if query_limit is not None:
+ interleaved_entries = interleaved_entries[:query_limit]
+ return rpc_utils.prepare_for_serialization(interleaved_entries)
+
+
+def get_num_host_queue_entries_and_special_tasks(hostname):
+ filter_data = {'host__hostname': hostname}
+ return (models.HostQueueEntry.query_count(filter_data)
+ + models.SpecialTask.query_count(filter_data))
+
+
# recurring run
def get_recurring(**filter_data):
diff --git a/frontend/afe/rpc_interface_unittest.py b/frontend/afe/rpc_interface_unittest.py
index 8e3ac25..a1cab5e 100644
--- a/frontend/afe/rpc_interface_unittest.py
+++ b/frontend/afe/rpc_interface_unittest.py
@@ -37,7 +37,7 @@
def test_get_jobs_summary(self):
- job = self._create_job(xrange(3))
+ job = self._create_job(hosts=xrange(1, 4))
entries = list(job.hostqueueentry_set.all())
entries[1].status = _hqe_status.FAILED
entries[1].save()
@@ -61,5 +61,58 @@
self.assertEquals(host.aclgroup_set.count(), 0)
+ def _common_entry_check(self, entry_dict):
+ self.assertEquals(entry_dict['host']['hostname'], 'host1')
+ self.assertEquals(entry_dict['job']['id'], 2)
+
+
+
+ def test_get_host_queue_entries_and_special_tasks(self):
+ host = self.hosts[0]
+
+ job1 = self._create_job(hosts=[1])
+ job2 = self._create_job(hosts=[1])
+
+ entry1 = job1.hostqueueentry_set.all()[0]
+ entry1.update_object(started_on=datetime.datetime(2009, 1, 2),
+ execution_subdir='1-myuser/host1')
+ entry2 = job2.hostqueueentry_set.all()[0]
+ entry2.update_object(started_on=datetime.datetime(2009, 1, 3),
+ execution_subdir='2-myuser/host1')
+
+ task1 = models.SpecialTask.objects.create(
+ host=host, task=models.SpecialTask.Task.VERIFY,
+ time_started=datetime.datetime(2009, 1, 1), # ran before job 1
+ is_complete=True)
+ task2 = models.SpecialTask.objects.create(
+ host=host, task=models.SpecialTask.Task.VERIFY,
+ queue_entry=entry2, # ran with job 2
+ is_active=True)
+ task3 = models.SpecialTask.objects.create(
+ host=host, task=models.SpecialTask.Task.VERIFY) # not yet run
+
+ entries_and_tasks = (
+ rpc_interface.get_host_queue_entries_and_special_tasks('host1'))
+
+ paths = [entry['execution_path'] for entry in entries_and_tasks]
+ self.assertEquals(paths, ['hosts/host1/3-verify',
+ '2-myuser/host1',
+ 'hosts/host1/2-verify',
+ '1-myuser/host1',
+ 'hosts/host1/1-verify'])
+
+ verify2 = entries_and_tasks[2]
+ self._common_entry_check(verify2)
+ self.assertEquals(verify2['type'], 'Verify')
+ self.assertEquals(verify2['status'], 'Running')
+ self.assertEquals(verify2['execution_path'], 'hosts/host1/2-verify')
+
+ entry2 = entries_and_tasks[1]
+ self._common_entry_check(entry2)
+ self.assertEquals(entry2['type'], 'Job')
+ self.assertEquals(entry2['status'], 'Queued')
+ self.assertEquals(entry2['started_on'], '2009-01-03 00:00:00')
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/frontend/afe/rpc_utils.py b/frontend/afe/rpc_utils.py
index a82345e..da34daa 100644
--- a/frontend/afe/rpc_utils.py
+++ b/frontend/afe/rpc_utils.py
@@ -463,3 +463,88 @@
# error but should not trip up the RPC interface. monitor_db_cleanup
# deals with it. This just returns the first one found.
return platform, atomic_group_name
+
+
+# support for get_host_queue_entries_and_special_tasks()
+
+def _common_entry_to_dict(entry, type, job_dict):
+ return dict(type=type,
+ host=entry.host.get_object_dict(),
+ job=job_dict,
+ execution_path=entry.execution_path(),
+ status=entry.status,
+ started_on=entry.started_on,
+ id=entry.id)
+
+
+def _special_task_to_dict(special_task):
+ job_dict = None
+ if special_task.queue_entry:
+ job_dict = special_task.queue_entry.job.get_object_dict()
+ return _common_entry_to_dict(special_task, special_task.task, job_dict)
+
+
+def _queue_entry_to_dict(queue_entry):
+ return _common_entry_to_dict(queue_entry, 'Job',
+ queue_entry.job.get_object_dict())
+
+
+def _compute_next_job_for_tasks(queue_entries, special_tasks):
+ """
+ For each task, try to figure out the next job that ran after that task.
+ This is done using two pieces of information:
+ * if the task has a queue entry, we can use that entry's job ID.
+ * if the task has a time_started, we can try to compare that against the
+ started_on field of queue_entries. this isn't guaranteed to work perfectly
+ since queue_entries may also have null started_on values.
+ * if the task has neither, or if use of time_started fails, just use the
+ last computed job ID.
+ """
+ next_job_id = None # most recently computed next job
+ hqe_index = 0 # index for scanning by started_on times
+ for task in special_tasks:
+ if task.queue_entry:
+ next_job_id = task.queue_entry.job.id
+ elif task.time_started is not None:
+ for queue_entry in queue_entries[hqe_index:]:
+ if queue_entry.started_on is None:
+ continue
+ if queue_entry.started_on < task.time_started:
+ break
+ next_job_id = queue_entry.job.id
+
+ task.next_job_id = next_job_id
+
+ # advance hqe_index to just after next_job_id
+ if next_job_id is not None:
+ for queue_entry in queue_entries[hqe_index:]:
+ if queue_entry.job.id < next_job_id:
+ break
+ hqe_index += 1
+
+
+def interleave_entries(queue_entries, special_tasks):
+ """
+ Both lists should be ordered by descending ID.
+ """
+ _compute_next_job_for_tasks(queue_entries, special_tasks)
+
+ # start with all special tasks that've run since the last job
+ interleaved_entries = []
+ for task in special_tasks:
+ if task.next_job_id is not None:
+ break
+ interleaved_entries.append(_special_task_to_dict(task))
+
+ # now interleave queue entries with the remaining special tasks
+ special_task_index = len(interleaved_entries)
+ for queue_entry in queue_entries:
+ interleaved_entries.append(_queue_entry_to_dict(queue_entry))
+ # add all tasks that ran between this job and the previous one
+ for task in special_tasks[special_task_index:]:
+ if task.next_job_id < queue_entry.job.id:
+ break
+ interleaved_entries.append(_special_task_to_dict(task))
+ special_task_index += 1
+
+ return interleaved_entries
diff --git a/frontend/client/src/autotest/afe/HostDetailView.java b/frontend/client/src/autotest/afe/HostDetailView.java
index c53890c..14ac1ef 100644
--- a/frontend/client/src/autotest/afe/HostDetailView.java
+++ b/frontend/client/src/autotest/afe/HostDetailView.java
@@ -12,6 +12,7 @@
import autotest.common.table.DataSource.DataCallback;
import autotest.common.table.DataSource.SortDirection;
import autotest.common.table.DynamicTable.DynamicTableListener;
+import autotest.common.table.SelectionManager.SelectableRowFilter;
import autotest.common.ui.ContextMenu;
import autotest.common.ui.DetailView;
import autotest.common.ui.NotifyManager;
@@ -23,14 +24,17 @@
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
-public class HostDetailView extends DetailView implements DataCallback, TableActionsListener {
+public class HostDetailView extends DetailView
+ implements DataCallback, TableActionsListener, SelectableRowFilter {
private static final String[][] HOST_JOBS_COLUMNS = {
- {DataTable.WIDGET_COLUMN, ""}, {"job_id", "Job ID"}, {"job_owner", "Job Owner"},
- {"job_name", "Job Name"}, {"status", "Status"}
+ {DataTable.WIDGET_COLUMN, ""}, {"type", "Type"}, {"job_id", "Job ID"},
+ {"job_owner", "Job Owner"}, {"job_name", "Job Name"}, {"started_on", "Time started"},
+ {"status", "Status"}
};
public static final int JOBS_PER_PAGE = 20;
@@ -39,35 +43,84 @@
}
static class HostJobsTable extends DynamicTable {
- public HostJobsTable(String[][] columns, DataSource dataSource) {
- super(columns, dataSource);
+ private static final DataSource normalDataSource =
+ new RpcDataSource("get_host_queue_entries", "get_num_host_queue_entries");
+ private static final DataSource dataSourceWithSpecialTasks =
+ new RpcDataSource("get_host_queue_entries_and_special_tasks",
+ "get_num_host_queue_entries_and_special_tasks");
+
+ private SimpleFilter hostFilter = new SimpleFilter();
+ private String hostname;
+
+ public HostJobsTable() {
+ super(HOST_JOBS_COLUMNS, normalDataSource);
+ addFilter(hostFilter);
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ updateFilter();
+ }
+
+ private void updateFilter() {
+ String key;
+ if (getDataSource() == normalDataSource) {
+ key = "host__hostname";
+ sortOnColumn("job_id", SortDirection.DESCENDING);
+ } else {
+ key = "hostname";
+ clearSorts();
+ }
+
+ hostFilter.clear();
+ hostFilter.setParameter(key, new JSONString(hostname));
+ }
+
+ public void setSpecialTasksEnabled(boolean enabled) {
+ if (enabled) {
+ setDataSource(dataSourceWithSpecialTasks);
+ } else {
+ setDataSource(normalDataSource);
+ }
+
+ updateFilter();
}
@Override
protected void preprocessRow(JSONObject row) {
JSONObject job = row.get("job").isObject();
- int jobId = (int) job.get("id").isNumber().doubleValue();
- row.put("job_id", new JSONString(Integer.toString(jobId)));
- row.put("job_owner", job.get("owner"));
- row.put("job_name", job.get("name"));
+ JSONString blank = new JSONString("");
+ JSONString jobId = blank, owner = blank, name = blank;
+ if (job != null) {
+ int id = (int) job.get("id").isNumber().doubleValue();
+ jobId = new JSONString(Integer.toString(id));
+ owner = job.get("owner").isString();
+ name = job.get("name").isString();
+ }
+
+ row.put("job_id", jobId);
+ row.put("job_owner", owner);
+ row.put("job_name", name);
+
+ // get_host_queue_entries() doesn't return type, so fill it in for consistency
+ if (!row.containsKey("type")) {
+ row.put("type", new JSONString("Job"));
+ }
}
}
- protected String hostname = "";
- protected DataSource hostDataSource = new HostDataSource();
- protected DynamicTable jobsTable =
- new HostJobsTable(HOST_JOBS_COLUMNS,
- new RpcDataSource("get_host_queue_entries",
- "get_num_host_queue_entries"));
- protected TableDecorator tableDecorator = new TableDecorator(jobsTable);
- protected SimpleFilter hostFilter = new SimpleFilter();
- protected HostDetailListener listener = null;
+ private String hostname = "";
+ private DataSource hostDataSource = new HostDataSource();
+ private HostJobsTable jobsTable = new HostJobsTable();
+ private TableDecorator tableDecorator = new TableDecorator(jobsTable);
+ private HostDetailListener listener = null;
private SelectionManager selectionManager;
private JSONObject currentHostObject;
private Button lockButton = new Button();
private Button reverifyButton = new Button("Reverify");
+ private CheckBox showSpecialTasks = new CheckBox();
public HostDetailView(HostDetailListener listener) {
this.listener = listener;
@@ -144,7 +197,7 @@
DOM.setElementProperty(DOM.getElementById("view_host_logs_link"), "href",
getLogLink(hostname));
- hostFilter.setParameter("host__hostname", new JSONString(hostname));
+ jobsTable.setHostname(hostname);
jobsTable.refresh();
}
@@ -157,24 +210,37 @@
super.initialize();
jobsTable.setRowsPerPage(JOBS_PER_PAGE);
- jobsTable.sortOnColumn("job_id", SortDirection.DESCENDING);
- jobsTable.addFilter(hostFilter);
jobsTable.setClickable(true);
jobsTable.addListener(new DynamicTableListener() {
public void onRowClicked(int rowIndex, JSONObject row) {
- JSONObject job = row.get("job").isObject();
- int jobId = (int) job.get("id").isNumber().doubleValue();
- listener.onJobSelected(jobId);
+ if (isJobRow(row)) {
+ JSONObject job = row.get("job").isObject();
+ int jobId = (int) job.get("id").isNumber().doubleValue();
+ listener.onJobSelected(jobId);
+ } else {
+ String resultsPath = "/results/"
+ + Utils.jsonToString(row.get("execution_path"));
+ Utils.openUrlInNewWindow(resultsPath);
+ }
}
public void onTableRefreshed() {}
});
tableDecorator.addPaginators();
selectionManager = tableDecorator.addSelectionManager(false);
+ selectionManager.setSelectableRowFilter(this);
jobsTable.setWidgetFactory(selectionManager);
tableDecorator.addTableActionsPanel(this, true);
+ tableDecorator.addControl("Show verifies, repairs and cleanups", showSpecialTasks);
RootPanel.get("view_host_jobs_table").add(tableDecorator);
+ showSpecialTasks.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ jobsTable.setSpecialTasksEnabled(showSpecialTasks.isChecked());
+ jobsTable.refresh();
+ }
+ });
+
lockButton.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
boolean locked = currentHostObject.get("locked").isBoolean().booleanValue();
@@ -239,4 +305,13 @@
}
});
}
+
+ private boolean isJobRow(JSONObject row) {
+ String type = Utils.jsonToString(row.get("type"));
+ return type.equals("Job");
+ }
+
+ public boolean isRowSelectable(JSONObject row) {
+ return isJobRow(row);
+ }
}
diff --git a/frontend/client/src/autotest/common/Utils.java b/frontend/client/src/autotest/common/Utils.java
index 0dc7c50..00e5439 100644
--- a/frontend/client/src/autotest/common/Utils.java
+++ b/frontend/client/src/autotest/common/Utils.java
@@ -6,6 +6,7 @@
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.Window;
import java.util.ArrayList;
import java.util.Collection;
@@ -240,4 +241,8 @@
destination.put(key, source.get(key));
}
}
+
+ public static void openUrlInNewWindow(String url) {
+ Window.open(url, "_blank", "");
+ }
}
diff --git a/frontend/client/src/autotest/common/table/DynamicTable.java b/frontend/client/src/autotest/common/table/DynamicTable.java
index a10eb62..e95e479 100644
--- a/frontend/client/src/autotest/common/table/DynamicTable.java
+++ b/frontend/client/src/autotest/common/table/DynamicTable.java
@@ -67,7 +67,7 @@
public DynamicTable(String[][] columns, DataSource dataSource) {
super(columns);
- this.dataSource = dataSource;
+ setDataSource(dataSource);
}
// SORTING
@@ -145,6 +145,11 @@
sortOnColumn(columnField, SortDirection.ASCENDING);
}
+ public void clearSorts() {
+ sortColumns.clear();
+ updateSortIndicators();
+ }
+
// PAGINATION
/**
@@ -263,6 +268,10 @@
return dataSource;
}
+ public void setDataSource(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
// INPUT
diff --git a/frontend/client/src/autotest/common/table/SelectionManager.java b/frontend/client/src/autotest/common/table/SelectionManager.java
index 517e879..e402edd 100644
--- a/frontend/client/src/autotest/common/table/SelectionManager.java
+++ b/frontend/client/src/autotest/common/table/SelectionManager.java
@@ -25,32 +25,52 @@
*/
public class SelectionManager implements TableWidgetFactory, TableWidgetClickListener,
SelectionPanelListener {
- protected Set<JSONObject> selectedObjects = new JSONObjectSet<JSONObject>();
- protected boolean selectOnlyOne = false;
- protected DataTable attachedTable;
- protected List<SelectionListener> listeners =
- new ArrayList<SelectionListener>();
+ private Set<JSONObject> selectedObjects = new JSONObjectSet<JSONObject>();
+ private boolean selectOnlyOne = false;
+ private DataTable attachedTable;
+ private List<SelectionListener> listeners = new ArrayList<SelectionListener>();
+ private SelectableRowFilter selectableRowFilter;
public interface SelectionListener {
public void onAdd(Collection<JSONObject> objects);
public void onRemove(Collection<JSONObject> objects);
}
+ public interface SelectableRowFilter {
+ public boolean isRowSelectable(JSONObject row);
+ }
+
public SelectionManager(DataTable table, boolean selectOnlyOne) {
attachedTable = table;
this.selectOnlyOne = selectOnlyOne;
}
+ public void setSelectableRowFilter(SelectableRowFilter filter) {
+ selectableRowFilter = filter;
+ }
+
public void refreshSelection() {
for (int i = 0; i < attachedTable.getRowCount(); i++) {
- if (selectedObjects.contains(attachedTable.getRow(i)))
+ JSONObject row = attachedTable.getRow(i);
+ if (!isSelectable(row)) {
+ continue;
+ }
+ if (selectedObjects.contains(row)) {
attachedTable.highlightRow(i);
- else
+ } else {
attachedTable.unhighlightRow(i);
+ }
}
attachedTable.refreshWidgets();
}
+ private boolean isSelectable(JSONObject row) {
+ if (selectableRowFilter != null) {
+ return selectableRowFilter.isRowSelectable(row);
+ }
+ return true;
+ }
+
public void selectObject(JSONObject object) {
selectObjects(Utils.wrapObjectWithList(object));
}
@@ -75,13 +95,18 @@
boolean add) {
List<JSONObject> actuallyUsed = new ArrayList<JSONObject>();
for (JSONObject object : objects) {
+ if (!isSelectable(object)) {
+ continue;
+ }
boolean used = false;
- if (add)
+ if (add) {
used = selectedObjects.add(object);
- else
+ } else {
used = selectedObjects.remove(object);
- if (used)
+ }
+ if (used) {
actuallyUsed.add(object);
+ }
}
notifyListeners(actuallyUsed, add);
}
@@ -134,12 +159,6 @@
listeners.remove(listener);
}
- protected void notifyListeners(JSONObject object, boolean add) {
- List<JSONObject> objectList = new ArrayList<JSONObject>();
- objectList.add(object);
- notifyListeners(objectList, add);
- }
-
protected void notifyListeners(Collection<JSONObject> objects,
boolean add) {
refreshSelection();
@@ -151,9 +170,13 @@
}
}
- // code for acting as a TableWidgetFactory follows
+ // code for acting as a TableWidgetFactory/TableWidgetClickListener
public Widget createWidget(int row, int cell, JSONObject rowObject) {
+ if (!isSelectable(rowObject)) {
+ return null;
+ }
+
CheckBox checkBox = new CheckBox();
if(selectedObjects.contains(rowObject)) {
checkBox.setChecked(true);
@@ -165,6 +188,8 @@
toggleSelected(attachedTable.getRow(widget.getRow()));
refreshSelection();
}
+
+ // code for acting as a SelectionPanelListener
public void onSelectAll(boolean visibleOnly) {
if (visibleOnly) {
diff --git a/frontend/client/src/autotest/common/table/SimpleFilter.java b/frontend/client/src/autotest/common/table/SimpleFilter.java
index 8738176..aaafde1 100644
--- a/frontend/client/src/autotest/common/table/SimpleFilter.java
+++ b/frontend/client/src/autotest/common/table/SimpleFilter.java
@@ -22,7 +22,7 @@
}
public void setAllParameters(JSONObject params) {
- parameters = new JSONObject();
+ clear();
updateObject(parameters, params);
}
@@ -41,4 +41,8 @@
return true;
}
+ public void clear() {
+ parameters = new JSONObject();
+ }
+
}
diff --git a/frontend/client/src/autotest/common/table/TableDecorator.java b/frontend/client/src/autotest/common/table/TableDecorator.java
index b1ecab8..5a3f938 100644
--- a/frontend/client/src/autotest/common/table/TableDecorator.java
+++ b/frontend/client/src/autotest/common/table/TableDecorator.java
@@ -62,7 +62,7 @@
addControl(text, filter.getWidget());
}
- protected void addControl(String text, Widget widget) {
+ public void addControl(String text, Widget widget) {
int row = numFilters;
numFilters++;
filterTable.setText(row, 0, text);
diff --git a/frontend/client/src/autotest/common/ui/TabView.java b/frontend/client/src/autotest/common/ui/TabView.java
index 08e4165..0bc4047 100644
--- a/frontend/client/src/autotest/common/ui/TabView.java
+++ b/frontend/client/src/autotest/common/ui/TabView.java
@@ -1,6 +1,7 @@
package autotest.common.ui;
import autotest.common.CustomHistory;
+import autotest.common.Utils;
import autotest.common.CustomHistory.HistoryToken;
import com.google.gwt.http.client.URL;
@@ -100,7 +101,7 @@
protected void openHistoryToken(HistoryToken historyToken) {
if (isOpenInNewWindowEvent()) {
String newUrl = Window.Location.getPath() + "#" + historyToken;
- Window.open(URL.encode(newUrl), "_blank", "");
+ Utils.openUrlInNewWindow(URL.encode(newUrl));
} else {
History.newItem(historyToken.toString());
}
diff --git a/frontend/client/src/autotest/tko/EmbeddedTkoClient.java b/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
index 3a35f92..0ed0a0b 100644
--- a/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
+++ b/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
@@ -1,6 +1,7 @@
package autotest.tko;
import autotest.common.JsonRpcProxy;
+import autotest.common.Utils;
import autotest.common.CustomHistory.HistoryToken;
import autotest.tko.TableView.TableSwitchListener;
import autotest.tko.TableView.TableViewConfig;
@@ -10,7 +11,6 @@
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.Window;
public class EmbeddedTkoClient implements EntryPoint, TableSwitchListener {
private String autotestServerUrl;
@@ -84,7 +84,7 @@
public void onSelectTest(int testId) {
String fullUrl = autotestServerUrl + "/new_tko/#" + getSelectTestHistoryToken(testId);
- Window.open(fullUrl, "_blank", "");
+ Utils.openUrlInNewWindow(fullUrl);
}
public void onSwitchToTable(TableViewConfig config) {
diff --git a/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java b/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java
index 6d5824a..2ab11ef 100644
--- a/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java
+++ b/frontend/client/src/autotest/tko/ExistingGraphsFrontend.java
@@ -10,7 +10,6 @@
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.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
@@ -246,7 +245,7 @@
args.put("benchmark", benchmarkStr);
args.put("key", getKey(benchmarkStr));
}
- Window.open(url + Utils.encodeUrlArguments(args), "_blank", "");
+ Utils.openUrlInNewWindow(url + Utils.encodeUrlArguments(args));
}
private String getKey(String benchmark) {
diff --git a/frontend/client/src/autotest/tko/TkoUtils.java b/frontend/client/src/autotest/tko/TkoUtils.java
index f68cfd7..25945a1 100644
--- a/frontend/client/src/autotest/tko/TkoUtils.java
+++ b/frontend/client/src/autotest/tko/TkoUtils.java
@@ -12,7 +12,6 @@
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Widget;
@@ -110,7 +109,7 @@
}
String url = JsonRpcProxy.TKO_BASE_URL + "csv/?" + request.toString();
- Window.open(url, "_blank", "");
+ Utils.openUrlInNewWindow(url);
}
static void doCsvRequest(RpcDataSource dataSource) {