• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package autotest.afe.create;
2 
3 import autotest.afe.AfeUtils;
4 import autotest.afe.CheckBoxPanel;
5 import autotest.afe.ControlTypeSelect;
6 import autotest.afe.HostSelector;
7 import autotest.afe.IButton;
8 import autotest.afe.ICheckBox;
9 import autotest.afe.ITextArea;
10 import autotest.afe.ITextBox;
11 import autotest.afe.RadioChooser;
12 import autotest.afe.TestSelector;
13 import autotest.afe.TestSelector.TestSelectorListener;
14 import autotest.common.JSONArrayList;
15 import autotest.common.JsonRpcCallback;
16 import autotest.common.JsonRpcProxy;
17 import autotest.common.SimpleCallback;
18 import autotest.common.StaticDataRepository;
19 import autotest.common.Utils;
20 import autotest.common.ui.NotifyManager;
21 import autotest.common.ui.SimplifiedList;
22 import autotest.common.ui.ExtendedListBox;
23 
24 import com.google.gwt.event.dom.client.BlurEvent;
25 import com.google.gwt.event.dom.client.BlurHandler;
26 import com.google.gwt.event.dom.client.ChangeEvent;
27 import com.google.gwt.event.dom.client.ChangeHandler;
28 import com.google.gwt.event.dom.client.ClickEvent;
29 import com.google.gwt.event.dom.client.ClickHandler;
30 import com.google.gwt.event.dom.client.HasClickHandlers;
31 import com.google.gwt.event.dom.client.KeyCodes;
32 import com.google.gwt.event.dom.client.KeyPressEvent;
33 import com.google.gwt.event.dom.client.KeyPressHandler;
34 import com.google.gwt.event.logical.shared.CloseEvent;
35 import com.google.gwt.event.logical.shared.CloseHandler;
36 import com.google.gwt.event.logical.shared.HasCloseHandlers;
37 import com.google.gwt.event.logical.shared.HasOpenHandlers;
38 import com.google.gwt.event.logical.shared.OpenEvent;
39 import com.google.gwt.event.logical.shared.OpenHandler;
40 import com.google.gwt.event.logical.shared.ValueChangeEvent;
41 import com.google.gwt.event.logical.shared.ValueChangeHandler;
42 import com.google.gwt.json.client.JSONArray;
43 import com.google.gwt.json.client.JSONBoolean;
44 import com.google.gwt.json.client.JSONNull;
45 import com.google.gwt.json.client.JSONNumber;
46 import com.google.gwt.json.client.JSONObject;
47 import com.google.gwt.json.client.JSONString;
48 import com.google.gwt.json.client.JSONValue;
49 import com.google.gwt.user.client.DOM;
50 import com.google.gwt.user.client.Window;
51 import com.google.gwt.user.client.ui.DisclosurePanel;
52 import com.google.gwt.user.client.ui.HasText;
53 import com.google.gwt.user.client.ui.HasValue;
54 
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59 
60 import java.lang.Math;
61 
62 public class CreateJobViewPresenter implements TestSelectorListener {
63     public static interface Display {
getCheckBoxPanelDisplay()64         public CheckBoxPanel.Display getCheckBoxPanelDisplay();
getControlTypeSelectDisplay()65         public ControlTypeSelect.Display getControlTypeSelectDisplay();
getTestSelectorDisplay()66         public TestSelector.Display getTestSelectorDisplay();
getJobName()67         public HasText getJobName();
getPriorityList()68         public ExtendedListBox getPriorityList();
getTimeout()69         public HasText getTimeout();
getMaxRuntime()70         public HasText getMaxRuntime();
getTestRetry()71         public HasText getTestRetry();
getEmailList()72         public HasText getEmailList();
getSkipVerify()73         public ICheckBox getSkipVerify();
getSkipReset()74         public ICheckBox getSkipReset();
getRebootBefore()75         public RadioChooser.Display getRebootBefore();
getRebootAfter()76         public RadioChooser.Display getRebootAfter();
getParseFailedRepair()77         public HasValue<Boolean> getParseFailedRepair();
getHostless()78         public ICheckBox getHostless();
getRequireSSP()79         public ICheckBox getRequireSSP();
getPool()80         public HasText getPool();
getArgs()81         public ITextBox getArgs();
getHostSelectorDisplay()82         public HostSelector.Display getHostSelectorDisplay();
getDroneSet()83         public SimplifiedList getDroneSet();
getSynchCountInput()84         public ITextBox getSynchCountInput();
getControlFile()85         public ITextArea getControlFile();
setControlFilePanelOpen(boolean isOpen)86         public void setControlFilePanelOpen(boolean isOpen);
getRunNonProfiledIteration()87         public ICheckBox getRunNonProfiledIteration();
getImageUrl()88         public ITextBox getImageUrl();
getViewLink()89         public HasText getViewLink();
getControlFilePanelClose()90         public HasCloseHandlers<DisclosurePanel> getControlFilePanelClose();
getControlFilePanelOpen()91         public HasOpenHandlers<DisclosurePanel> getControlFilePanelOpen();
getSubmitJobButton()92         public IButton getSubmitJobButton();
getCreateTemplateJobButton()93         public HasClickHandlers getCreateTemplateJobButton();
getResetButton()94         public HasClickHandlers getResetButton();
getFetchTestsFromBuildCheckBox()95         public ICheckBox getFetchTestsFromBuildCheckBox();
getFirmwareRWBuild()96         public ITextBox getFirmwareRWBuild();
getFirmwareROBuild()97         public ITextBox getFirmwareROBuild();
getTestSourceBuildList()98         public ExtendedListBox getTestSourceBuildList();
99     }
100 
101     private static final String VIEW_CONTROL_STRING =
102             "View control file (editing support dropped: crbug.com/753607)";
103     private static final String HIDE_CONTROL_STRING =
104             "Hide control file (editing support dropped: crbug.com/753607)";
105     private static final String FIRMWARE_RW_BUILD = "firmware_rw_build";
106     private static final String FIRMWARE_RO_BUILD = "firmware_ro_build";
107     private static final String TEST_SOURCE_BUILD = "test_source_build";
108 
109     public interface JobCreateListener {
onJobCreated(int jobId)110         public void onJobCreated(int jobId);
111     }
112 
113     private interface IPredicate<T> {
apply(T item)114         boolean apply(T item);
115     }
116 
117     private IPredicate<Integer> positiveIntegerPredicate = new IPredicate<Integer>() {
118         public boolean apply(Integer item) {
119             return item > 0;
120         }
121     };
122 
123     private IPredicate<Integer> nonnegativeIntegerPredicate = new IPredicate<Integer>() {
124         public boolean apply(Integer item) {
125             return item >= 0;
126         }
127     };
128 
129     private IPredicate<Integer> anyIntegerPredicate = new IPredicate<Integer>() {
130         public boolean apply(Integer item) {
131             return true;
132         }
133     };
134 
135     private JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
136     private JobCreateListener listener;
137 
138     private StaticDataRepository staticData = StaticDataRepository.getRepository();
139 
140     private CheckBoxPanel profilersPanel = new CheckBoxPanel();
141     private ControlTypeSelect controlTypeSelect = new ControlTypeSelect();
142     protected TestSelector testSelector = new TestSelector();
143     private RadioChooser rebootBefore = new RadioChooser();
144     private RadioChooser rebootAfter = new RadioChooser();
145     private HostSelector hostSelector;
146 
147     private boolean controlReadyForSubmit = false;
148     private JSONArray dependencies = new JSONArray();
149 
150     private Display display;
151     private boolean cloning;
152 
bindDisplay(Display display)153     public void bindDisplay(Display display) {
154         this.display = display;
155     }
156 
CreateJobViewPresenter(JobCreateListener listener)157     public CreateJobViewPresenter(JobCreateListener listener) {
158         this.listener = listener;
159     }
160 
setCloning(boolean cloning)161     public void setCloning(boolean cloning) {
162       this.cloning = cloning;
163     }
164 
isCloning()165     public boolean isCloning() {
166       return cloning;
167     }
168 
cloneJob(JSONValue cloneInfo)169     public void cloneJob(JSONValue cloneInfo) {
170         setCloning(true);
171         // reset() fires the TestSelectorListener, which will generate a new control file. We do
172         // no want this, so we'll stop listening to it for a bit.
173         testSelector.setListener(null);
174         reset();
175         testSelector.setListener(this);
176 
177         disableInputs();
178         JSONObject cloneObject = cloneInfo.isObject();
179         JSONObject jobObject = cloneObject.get("job").isObject();
180 
181         display.getJobName().setText(jobObject.get("name").isString().stringValue());
182 
183         ArrayList<String> builds = new ArrayList<String>();
184         if (jobObject.containsKey("image")) {
185             String image = jobObject.get("image").isString().stringValue();
186             builds.add(image);
187             display.getImageUrl().setText(image);
188             display.getFirmwareRWBuild().setEnabled(true);
189             display.getFirmwareROBuild().setEnabled(true);
190             display.getTestSourceBuildList().setEnabled(true);
191         }
192 
193         if (jobObject.containsKey(FIRMWARE_RW_BUILD)) {
194             String firmwareRWBuild = jobObject.get(FIRMWARE_RW_BUILD).isString().stringValue();
195             builds.add(firmwareRWBuild);
196             display.getFirmwareRWBuild().setText(firmwareRWBuild);
197         }
198 
199         if (jobObject.containsKey(FIRMWARE_RO_BUILD)) {
200             String firmwareROBuild = jobObject.get(FIRMWARE_RO_BUILD).isString().stringValue();
201             builds.add(firmwareROBuild);
202             display.getFirmwareROBuild().setText(firmwareROBuild);
203         }
204 
205         for (String build : builds) {
206             display.getTestSourceBuildList().addItem(build);
207         }
208 
209         if (jobObject.containsKey(TEST_SOURCE_BUILD)) {
210             String testSourceBuild = jobObject.get(TEST_SOURCE_BUILD).isString().stringValue();
211             if (builds.indexOf(testSourceBuild) >= 0) {
212                 display.getTestSourceBuildList().setSelectedIndex(builds.indexOf(testSourceBuild));
213             }
214         }
215 
216         Double priorityValue = jobObject.get("priority").isNumber().getValue();
217         Double maxPriority = staticData.getData("max_schedulable_priority").isNumber().getValue();
218         if (priorityValue > maxPriority) {
219             priorityValue = maxPriority;
220         }
221         String priorityName = staticData.getPriorityName(priorityValue);
222         display.getPriorityList().selectByName(priorityName);
223 
224         display.getTimeout().setText(Utils.jsonToString(jobObject.get("timeout_mins")));
225         display.getMaxRuntime().setText(Utils.jsonToString(jobObject.get("max_runtime_mins")));
226         display.getTestRetry().setText(Utils.jsonToString(jobObject.get("test_retry")));
227         display.getEmailList().setText(
228                 jobObject.get("email_list").isString().stringValue());
229 
230         display.getSkipVerify().setValue(!jobObject.get("run_verify").isBoolean().booleanValue());
231         display.getSkipReset().setValue(!jobObject.get("run_reset").isBoolean().booleanValue());
232         rebootBefore.setSelectedChoice(Utils.jsonToString(jobObject.get("reboot_before")));
233         rebootAfter.setSelectedChoice(Utils.jsonToString(jobObject.get("reboot_after")));
234         display.getParseFailedRepair().setValue(
235                 jobObject.get("parse_failed_repair").isBoolean().booleanValue());
236         display.getHostless().setValue(cloneObject.get("hostless").isBoolean().booleanValue());
237         if (display.getHostless().getValue()) {
238             hostSelector.setEnabled(false);
239         }
240         if (jobObject.get("require_ssp").isNull() != null)
241             display.getRequireSSP().setValue(false);
242         else
243             display.getRequireSSP().setValue(jobObject.get("require_ssp").isBoolean().booleanValue());
244         if (staticData.getData("drone_sets_enabled").isBoolean().booleanValue()) {
245             if (cloneObject.get("drone_set").isNull() == null) {
246                 display.getDroneSet().selectByName(Utils.jsonToString(cloneObject.get("drone_set")));
247             }
248         }
249 
250         controlTypeSelect.setControlType(
251                 jobObject.get("control_type").isString().stringValue());
252         display.getSynchCountInput().setText(Utils.jsonToString(jobObject.get("synch_count")));
253         setSelectedDependencies(jobObject.get("dependencies").isArray());
254         display.getControlFile().setText(
255                 jobObject.get("control_file").isString().stringValue());
256         controlReadyForSubmit = true;
257 
258         JSONArray hostInfo = cloneObject.get("hosts").isArray();
259         List<String> hostnames = new ArrayList<String>();
260         for (JSONObject host : new JSONArrayList<JSONObject>(hostInfo)) {
261             hostnames.add(Utils.jsonToString(host.get("hostname")));
262         }
263         hostSelector.setSelectedHostnames(hostnames, true);
264 
265         JSONObject metaHostCounts = cloneObject.get("meta_host_counts").isObject();
266 
267         for (String label : metaHostCounts.keySet()) {
268             String number = Integer.toString(
269                 (int) metaHostCounts.get(label).isNumber().doubleValue());
270             hostSelector.addMetaHosts(label, number);
271         }
272 
273         hostSelector.refresh();
274     }
275 
populatePriorities(JSONArray priorities)276     private void populatePriorities(JSONArray priorities) {
277         JSONValue maxSchedulableValue = staticData.getData("max_schedulable_priority");
278         double maxPriority = maxSchedulableValue.isNumber().getValue();
279         for(int i = 0; i < priorities.size(); i++) {
280             JSONArray priorityData = priorities.get(i).isArray();
281             String priority = priorityData.get(1).isString().stringValue();
282             double priorityValue = priorityData.get(0).isNumber().getValue();
283             String strPriorityValue = Double.toString(priorityValue);
284             if (priorityValue <= maxPriority) {
285                 display.getPriorityList().addItem(priority, strPriorityValue);
286             }
287         }
288 
289         resetPriorityToDefault();
290     }
291 
resetPriorityToDefault()292     private void resetPriorityToDefault() {
293         JSONValue defaultValue = staticData.getData("default_priority");
294         String defaultPriority = defaultValue.isString().stringValue();
295         display.getPriorityList().selectByName(defaultPriority);
296     }
297 
populateProfilers()298     private void populateProfilers() {
299         JSONArray tests = staticData.getData("profilers").isArray();
300 
301         for(JSONObject profiler : new JSONArrayList<JSONObject>(tests)) {
302             String name = profiler.get("name").isString().stringValue();
303             ICheckBox checkbox = profilersPanel.generateCheckBox();
304             checkbox.setText(name);
305             checkbox.addClickHandler(new ClickHandler() {
306                 public void onClick(ClickEvent event) {
307                     updateNonProfiledRunControl();
308                     generateControlFile(false);
309                     setInputsEnabled();
310                 }
311             });
312             profilersPanel.add(checkbox);
313         }
314 
315         display.getRunNonProfiledIteration().addClickHandler(new ClickHandler() {
316             @Override
317             public void onClick(ClickEvent event) {
318                 generateControlFile(false);
319             }
320         });
321         // default to checked -- run a non-profiled iteration by default
322         display.getRunNonProfiledIteration().setValue(true);
323     }
324 
updateNonProfiledRunControl()325     private void updateNonProfiledRunControl() {
326         boolean anyProfilersChecked = !profilersPanel.getChecked().isEmpty();
327         display.getRunNonProfiledIteration().setVisible(anyProfilersChecked);
328     }
329 
populateRebootChoices()330     private void populateRebootChoices() {
331         AfeUtils.populateRadioChooser(rebootBefore, "reboot_before");
332         AfeUtils.populateRadioChooser(rebootAfter, "reboot_after");
333     }
334 
335 
336     /**
337      * Get parameters to submit to the generate_control_file RPC.
338      * @param readyForSubmit are we getting a control file that's ready to submit for a job, or just
339      * an intermediate control file to be viewed by the user?
340      */
getControlFileParams(boolean readyForSubmit)341     protected JSONObject getControlFileParams(boolean readyForSubmit) {
342         JSONObject params = new JSONObject();
343 
344         boolean testsFromBuild = testSelector.usingTestsFromBuild();
345         params.put("db_tests", JSONBoolean.getInstance(!testsFromBuild));
346         if (testsFromBuild) {
347           String  testSourceBuild = display.getTestSourceBuildList().getSelectedValue();
348           if (testSourceBuild != null) {
349             params.put("test_source_build", new JSONString(testSourceBuild));
350           }
351         }
352 
353         JSONArray tests = new JSONArray();
354         for (JSONObject test : testSelector.getSelectedTests()) {
355             if (testsFromBuild) {
356                 tests.set(tests.size(), test);
357             }
358             else {
359                 tests.set(tests.size(), test.get("id"));
360             }
361         }
362         params.put("tests", tests);
363 
364         JSONArray profilers = new JSONArray();
365         for (ICheckBox profiler : profilersPanel.getChecked()) {
366             profilers.set(profilers.size(), new JSONString(profiler.getText()));
367         }
368         params.put("profilers", profilers);
369 
370         if (display.getRunNonProfiledIteration().isVisible()) {
371             boolean profileOnly = !display.getRunNonProfiledIteration().getValue();
372             params.put("profile_only", JSONBoolean.getInstance(profileOnly));
373         }
374 
375         return params;
376     }
377 
generateControlFile(final boolean readyForSubmit, final SimpleCallback finishedCallback, final SimpleCallback errorCallback)378     private void generateControlFile(final boolean readyForSubmit,
379                                        final SimpleCallback finishedCallback,
380                                        final SimpleCallback errorCallback) {
381         JSONObject params = getControlFileParams(readyForSubmit);
382         rpcProxy.rpcCall("generate_control_file", params, new JsonRpcCallback() {
383             @Override
384             public void onSuccess(JSONValue result) {
385                 JSONObject controlInfo = result.isObject();
386                 String controlFileText = controlInfo.get("control_file").isString().stringValue();
387                 boolean isServer = controlInfo.get("is_server").isBoolean().booleanValue();
388                 String synchCount = Utils.jsonToString(controlInfo.get("synch_count"));
389                 setSelectedDependencies(controlInfo.get("dependencies").isArray());
390                 display.getControlFile().setText(controlFileText);
391                 controlTypeSelect.setControlType(isServer ? TestSelector.SERVER_TYPE :
392                                                             TestSelector.CLIENT_TYPE);
393                 display.getSynchCountInput().setText(synchCount);
394                 controlReadyForSubmit = readyForSubmit;
395                 if (finishedCallback != null) {
396                     finishedCallback.doCallback(this);
397                 }
398             }
399 
400             @Override
401             public void onError(JSONObject errorObject) {
402                 super.onError(errorObject);
403                 if (errorCallback != null) {
404                     errorCallback.doCallback(this);
405                 }
406             }
407         });
408     }
409 
generateControlFile(boolean readyForSubmit)410     protected void generateControlFile(boolean readyForSubmit) {
411         generateControlFile(readyForSubmit, null, null);
412     }
413 
handleSkipVerify()414     public void handleSkipVerify() {
415         boolean shouldSkipVerify = false;
416         for (JSONObject test : testSelector.getSelectedTests()) {
417             boolean runVerify = test.get("run_verify").isBoolean().booleanValue();
418             if (!runVerify) {
419                 shouldSkipVerify = true;
420                 break;
421             }
422         }
423 
424         if (shouldSkipVerify) {
425             display.getSkipVerify().setValue(true);
426             display.getSkipVerify().setEnabled(false);
427         } else {
428             display.getSkipVerify().setEnabled(true);
429         }
430     }
431 
handleSkipReset()432     public void handleSkipReset() {
433         boolean shouldSkipReset = false;
434         for (JSONObject test : testSelector.getSelectedTests()) {
435             boolean runReset = test.get("run_reset").isBoolean().booleanValue();
436             if (!runReset) {
437                 shouldSkipReset = true;
438                 break;
439             }
440         }
441 
442         if (shouldSkipReset) {
443             display.getSkipReset().setValue(true);
444             display.getSkipReset().setEnabled(false);
445         } else {
446             display.getSkipReset().setEnabled(true);
447         }
448     }
449 
getMaximumRetriesCount()450     protected int getMaximumRetriesCount() {
451         int maxRetries = 0;
452         for (JSONObject test : testSelector.getSelectedTests()) {
453             maxRetries = (int) Math.max(maxRetries, test.get("test_retry").isNumber().getValue());
454         }
455         return maxRetries;
456     }
457 
handleBuildChange()458     protected void handleBuildChange() {
459         ChangeHandler handler = new ChangeHandler() {
460             @Override
461             public void onChange(ChangeEvent event) {
462                 String image = display.getImageUrl().getText();
463                 ICheckBox fetchTestsFromBuildCheckBox = display.getFetchTestsFromBuildCheckBox();
464                 Boolean fetchedFromBuild = fetchTestsFromBuildCheckBox.getValue();
465                 String currentTestSourceBuild = display.getTestSourceBuildList().getSelectedValue();
466                 boolean sourceBuildChanged = false;
467                 if (image.isEmpty()) {
468                     display.getFirmwareRWBuild().setText("");
469                     display.getFirmwareROBuild().setText("");
470                     display.getTestSourceBuildList().clear();
471                     display.getFirmwareRWBuild().setEnabled(false);
472                     display.getFirmwareROBuild().setEnabled(false);
473                     display.getTestSourceBuildList().setEnabled(false);
474                     sourceBuildChanged = (currentTestSourceBuild != null);
475                 }
476                 else {
477                     display.getFirmwareRWBuild().setEnabled(true);
478                     display.getFirmwareROBuild().setEnabled(true);
479                     display.getTestSourceBuildList().setEnabled(true);
480                     ArrayList<String> builds = new ArrayList<String>();
481                     builds.add(image);
482                     if (!display.getFirmwareRWBuild().getText().isEmpty())
483                         builds.add(display.getFirmwareRWBuild().getText());
484                     if (!display.getFirmwareROBuild().getText().isEmpty())
485                         builds.add(display.getFirmwareROBuild().getText());
486                     int testSourceBuildIndex = builds.indexOf(currentTestSourceBuild);
487                     display.getTestSourceBuildList().clear();
488                     for (String build : builds) {
489                         display.getTestSourceBuildList().addItem(build);
490                     }
491                     if (testSourceBuildIndex >= 0) {
492                         display.getTestSourceBuildList().setSelectedIndex(testSourceBuildIndex);
493                     } else {
494                       sourceBuildChanged = true;
495                     }
496                 }
497 
498                 // Updates the fetch test checkbox UI
499                 fetchTestsFromBuildCheckBox.setEnabled(!image.isEmpty());
500                 if (sourceBuildChanged) {
501                   fetchTestsFromBuildCheckBox.setValue(false);
502                   // Fetch the test again from default Moblab device if necessary
503                   if (fetchedFromBuild) {
504                     fetchImageTests();
505                   }
506                 }
507             }
508         };
509 
510         display.getImageUrl().addChangeHandler(handler);
511         display.getFirmwareRWBuild().addChangeHandler(handler);
512         display.getFirmwareROBuild().addChangeHandler(handler);
513     }
514 
setInputsEnabled()515     protected void setInputsEnabled() {
516         testSelector.setEnabled(true);
517         profilersPanel.setEnabled(true);
518         handleSkipVerify();
519         handleSkipReset();
520         display.getImageUrl().setEnabled(true);
521     }
522 
disableInputs()523     protected void disableInputs() {
524         testSelector.setEnabled(false);
525         profilersPanel.setEnabled(false);
526         display.getImageUrl().setEnabled(false);
527     }
528 
initialize()529     public void initialize() {
530         profilersPanel.bindDisplay(display.getCheckBoxPanelDisplay());
531         controlTypeSelect.bindDisplay(display.getControlTypeSelectDisplay());
532         testSelector.bindDisplay(display.getTestSelectorDisplay());
533         rebootBefore.bindDisplay(display.getRebootBefore());
534         rebootAfter.bindDisplay(display.getRebootAfter());
535 
536         display.getViewLink().setText(VIEW_CONTROL_STRING);
537 
538         hostSelector = new HostSelector();
539         hostSelector.initialize();
540         hostSelector.bindDisplay(display.getHostSelectorDisplay());
541 
542         populatePriorities(staticData.getData("priorities").isArray());
543 
544         BlurHandler kernelBlurHandler = new BlurHandler() {
545             public void onBlur(BlurEvent event) {
546                 generateControlFile(false);
547             }
548         };
549 
550         KeyPressHandler kernelKeyPressHandler = new KeyPressHandler() {
551             public void onKeyPress(KeyPressEvent event) {
552                 if (event.getCharCode() == (char) KeyCodes.KEY_ENTER) {
553                     generateControlFile(false);
554                 }
555             }
556         };
557 
558         populateProfilers();
559         updateNonProfiledRunControl();
560 
561         populateRebootChoices();
562         onPreferencesChanged();
563 
564         display.getControlFilePanelClose().addCloseHandler(new CloseHandler<DisclosurePanel>() {
565             public void onClose(CloseEvent<DisclosurePanel> event) {
566                 display.getViewLink().setText(VIEW_CONTROL_STRING);
567             }
568         });
569 
570         display.getControlFilePanelOpen().addOpenHandler(new OpenHandler<DisclosurePanel>() {
571             public void onOpen(OpenEvent<DisclosurePanel> event) {
572                 display.getViewLink().setText(HIDE_CONTROL_STRING);
573             }
574         });
575 
576         display.getSubmitJobButton().addClickHandler(new ClickHandler() {
577             public void onClick(ClickEvent event) {
578                 submitJob(false);
579             }
580         });
581 
582         display.getCreateTemplateJobButton().addClickHandler(new ClickHandler() {
583             public void onClick(ClickEvent event) {
584                 submitJob(true);
585             }
586         });
587 
588         display.getResetButton().addClickHandler(new ClickHandler() {
589             public void onClick(ClickEvent event) {
590                 reset();
591             }
592         });
593 
594         display.getHostless().addClickHandler(new ClickHandler() {
595             @Override
596             public void onClick(ClickEvent event) {
597                 hostSelector.setEnabled(!display.getHostless().getValue());
598             }
599         });
600 
601         display.getTestSourceBuildList().addChangeHandler(new ChangeHandler() {
602           @Override
603           public void onChange(ChangeEvent event) {
604               Boolean fetchedTests = display.getFetchTestsFromBuildCheckBox().getValue();
605               display.getFetchTestsFromBuildCheckBox().setValue(false);
606               if (fetchedTests) {
607                 fetchImageTests();
608               }
609           }
610         });
611 
612         display.getFetchTestsFromBuildCheckBox()
613             .addValueChangeHandler(new ValueChangeHandler<Boolean>() {
614               @Override
615               public void onValueChange(ValueChangeEvent<Boolean> event) {
616                   String imageUrl = display.getImageUrl().getText();
617                   if (imageUrl == null || imageUrl.isEmpty()) {
618                       NotifyManager.getInstance().showMessage(
619                           "No build was specified for fetching tests.");
620                   }
621                   fetchImageTests();
622               }
623             });
624 
625         handleBuildChange();
626 
627         reset();
628 
629         if (staticData.getData("drone_sets_enabled").isBoolean().booleanValue()) {
630             AfeUtils.populateListBox(display.getDroneSet(), "drone_sets");
631         } else {
632             AfeUtils.removeElement("create_drone_set_wrapper");
633         }
634 
635         testSelector.setListener(this);
636     }
637 
reset()638     public void reset() {
639         StaticDataRepository repository = StaticDataRepository.getRepository();
640 
641         display.getJobName().setText("");
642         resetPriorityToDefault();
643         rebootBefore.reset();
644         rebootAfter.reset();
645         display.getParseFailedRepair().setValue(
646                 repository.getData("parse_failed_repair_default").isBoolean().booleanValue());
647         display.getHostless().setValue(false);
648         // Default require_ssp to False, since it's not applicable to client side test.
649         display.getRequireSSP().setValue(false);
650         display.getImageUrl().setText("");
651         display.getTimeout().setText(Utils.jsonToString(repository.getData("job_timeout_mins_default")));
652         display.getMaxRuntime().setText(
653                 Utils.jsonToString(repository.getData("job_max_runtime_mins_default")));
654         display.getTestRetry().setText("");
655         display.getEmailList().setText("");
656         testSelector.reset();
657         display.getSkipVerify().setValue(true);
658         display.getSkipReset().setValue(false);
659         profilersPanel.reset();
660         setInputsEnabled();
661         controlTypeSelect.setControlType(TestSelector.CLIENT_TYPE);
662         controlTypeSelect.setEnabled(false);
663         display.getSynchCountInput().setEnabled(false);
664         display.getSynchCountInput().setText("1");
665         display.getControlFile().setText("");
666         display.getControlFile().setReadOnly(true);
667         display.setControlFilePanelOpen(false);
668         hostSelector.reset();
669         dependencies = new JSONArray();
670         display.getPool().setText("");
671         display.getArgs().setText("");
672         display.getImageUrl().setText("");
673         fetchImageTests();
674     }
675 
submitJob(final boolean isTemplate)676     private void submitJob(final boolean isTemplate) {
677         final int timeoutValue, maxRuntimeValue, testRetryValue;
678         final JSONValue synchCount;
679         try {
680             timeoutValue = parsePositiveIntegerInput(display.getTimeout().getText(), "timeout");
681             maxRuntimeValue = parsePositiveIntegerInput(
682                     display.getMaxRuntime().getText(), "max runtime");
683             String testRetryText = display.getTestRetry().getText();
684             if (testRetryText == "")
685                 testRetryValue = getMaximumRetriesCount();
686             else
687                 testRetryValue = parseNonnegativeIntegerInput(testRetryText, "test retries");
688 
689             if (display.getHostless().getValue()) {
690                 synchCount = JSONNull.getInstance();
691             } else {
692                 synchCount = new JSONNumber(parsePositiveIntegerInput(
693                     display.getSynchCountInput().getText(),
694                     "number of machines used per execution"));
695             }
696         } catch (IllegalArgumentException exc) {
697             return;
698         }
699 
700         // disallow accidentally clicking submit twice
701         display.getSubmitJobButton().setEnabled(false);
702 
703         final SimpleCallback doSubmit = new SimpleCallback() {
704             public void doCallback(Object source) {
705                 JSONObject args = new JSONObject();
706                 args.put("name", new JSONString(display.getJobName().getText()));
707                 String priority = display.getPriorityList().getSelectedValue();
708                 args.put("priority", new JSONNumber(Double.parseDouble(priority)));
709                 args.put("control_file", new JSONString(display.getControlFile().getText()));
710                 args.put("control_type",
711                          new JSONString(controlTypeSelect.getControlType()));
712                 args.put("synch_count", synchCount);
713                 args.put("timeout_mins", new JSONNumber(timeoutValue));
714                 args.put("max_runtime_mins", new JSONNumber(maxRuntimeValue));
715                 args.put("test_retry", new JSONNumber(testRetryValue));
716                 args.put("email_list", new JSONString(display.getEmailList().getText()));
717                 args.put("run_verify", JSONBoolean.getInstance(
718                         !display.getSkipVerify().getValue()));
719                 args.put("run_reset", JSONBoolean.getInstance(
720                         !display.getSkipReset().getValue()));
721                 args.put("is_template", JSONBoolean.getInstance(isTemplate));
722                 args.put("dependencies", getSelectedDependencies());
723                 args.put("reboot_before", new JSONString(rebootBefore.getSelectedChoice()));
724                 args.put("reboot_after", new JSONString(rebootAfter.getSelectedChoice()));
725                 args.put("parse_failed_repair",
726                          JSONBoolean.getInstance(display.getParseFailedRepair().getValue()));
727                 args.put("hostless", JSONBoolean.getInstance(display.getHostless().getValue()));
728                 args.put("require_ssp", JSONBoolean.getInstance(display.getRequireSSP().getValue()));
729                 args.put("pool", new JSONString(display.getPool().getText()));
730 
731                 if (staticData.getData("drone_sets_enabled").isBoolean().booleanValue()) {
732                     args.put("drone_set", new JSONString(display.getDroneSet().getSelectedName()));
733                 }
734 
735                 HostSelector.HostSelection hosts = hostSelector.getSelectedHosts();
736                 args.put("hosts", Utils.stringsToJSON(hosts.hosts));
737                 args.put("meta_hosts", Utils.stringsToJSON(hosts.metaHosts));
738                 args.put("one_time_hosts",
739                     Utils.stringsToJSON(hosts.oneTimeHosts));
740 
741                 String imageUrlString = display.getImageUrl().getText();
742                 if (!imageUrlString.equals("")) {
743                     args.put("image", new JSONString(imageUrlString));
744                 }
745                 else if (display.getHostless().getValue() &&
746                          testSuiteSelected()) {
747                     display.getSubmitJobButton().setEnabled(true);
748                     NotifyManager.getInstance().showError("Please specify an image to use.");
749                     return;
750                 }
751 
752 
753                 String argsString = display.getArgs().getText().trim();
754                 if (!argsString.equals("")) {
755                     JSONArray argsArray = new JSONArray();
756                     for (String arg : argsString.split(",")) {
757                         argsArray.set(argsArray.size(), new JSONString(arg.trim()));
758                     }
759                     args.put("args", argsArray);
760                 }
761 
762                 // TODO(crbug.com/464962): Fall back to build in cros-version label if possible.
763                 // Validate server-side packaging requirements
764                 if (display.getRequireSSP().getValue()) {
765                     String error = "";
766                     if (controlTypeSelect.getControlType() == "Client") {
767                         error = "Client side test does not need server-side packaging.";
768                     }
769                     else if (imageUrlString.equals("")) {
770                       boolean has_cros_version_dependency = false;
771                       for (int i = 0; i < getSelectedDependencies().size(); i++) {
772                         String dep = getSelectedDependencies().get(i).toString();
773                         if (dep.startsWith("\"cros-version:")) {
774                           has_cros_version_dependency = true;
775                           break;
776                         }
777                       }
778                       if (!has_cros_version_dependency)
779                         error = "You must specify an image to run test with server-side packaging.";
780                     }
781 
782                     if (error != "") {
783                         display.getSubmitJobButton().setEnabled(true);
784                         NotifyManager.getInstance().showError(error);
785                         return;
786                     }
787                 }
788 
789                 String firmwareRWBuild = display.getFirmwareRWBuild().getText();
790                 String firmwareROBuild = display.getFirmwareROBuild().getText();
791                 String testSourceBuild = display.getTestSourceBuildList().getSelectedValue();
792                 if (!firmwareRWBuild.isEmpty() || !firmwareROBuild.isEmpty()) {
793                     String error = "";
794                     if (testSourceBuild.isEmpty())
795                         error = "You must specify which build should be used to retrieve test code.";
796 
797                     // TODO(crbug.com/502638): Enable create test job with updating firmware.
798                     if (!display.getHostless().getValue())
799                         error = "Only suite job supports updating both ChromeOS build and firmware build.";
800                     if (error != "") {
801                         display.getSubmitJobButton().setEnabled(true);
802                         NotifyManager.getInstance().showError(error);
803                         return;
804                     }
805                     if (!firmwareRWBuild.isEmpty())
806                         args.put(FIRMWARE_RW_BUILD, new JSONString(firmwareRWBuild));
807                     if (!firmwareROBuild.isEmpty())
808                         args.put(FIRMWARE_RO_BUILD, new JSONString(firmwareROBuild));
809                     args.put(TEST_SOURCE_BUILD, new JSONString(testSourceBuild));
810                 }
811 
812                 args.put("is_cloning", JSONBoolean.getInstance(isCloning()));
813                 rpcProxy.rpcCall("create_job_page_handler", args, new JsonRpcCallback() {
814                     @Override
815                     public void onSuccess(JSONValue result) {
816                         int id = (int) result.isNumber().doubleValue();
817                         NotifyManager.getInstance().showMessage(
818                                     "Job " + Integer.toString(id) + " created");
819                         reset();
820                         if (listener != null) {
821                             listener.onJobCreated(id);
822                         }
823                         display.getSubmitJobButton().setEnabled(true);
824                     }
825 
826                     @Override
827                     public void onError(JSONObject errorObject) {
828                         super.onError(errorObject);
829                         display.getSubmitJobButton().setEnabled(true);
830                     }
831                 });
832             }
833         };
834 
835         // ensure control file is ready for submission
836         if (!controlReadyForSubmit) {
837             generateControlFile(true, doSubmit, new SimpleCallback() {
838                 public void doCallback(Object source) {
839                     display.getSubmitJobButton().setEnabled(true);
840                 }
841             });
842         } else {
843             doSubmit.doCallback(this);
844         }
845     }
846 
getSelectedDependencies()847     private JSONArray getSelectedDependencies() {
848         return dependencies;
849     }
850 
setSelectedDependencies(JSONArray dependencies)851     private void setSelectedDependencies(JSONArray dependencies) {
852         this.dependencies = dependencies;
853     }
854 
parseNonnegativeIntegerInput(String input, String fieldName)855     private int parseNonnegativeIntegerInput(String input, String fieldName) {
856         return parsePredicatedIntegerInput(input, fieldName,
857                                            nonnegativeIntegerPredicate,
858                                            "non-negative");
859     }
860 
parsePositiveIntegerInput(String input, String fieldName)861     private int parsePositiveIntegerInput(String input, String fieldName) {
862         return parsePredicatedIntegerInput(input, fieldName,
863                                            positiveIntegerPredicate,
864                                            "positive");
865     }
866 
parseAnyIntegerInput(String input, String fieldName)867     private int parseAnyIntegerInput(String input, String fieldName) {
868         return parsePredicatedIntegerInput(input, fieldName,
869                                            anyIntegerPredicate,
870                                            "integer");
871     }
872 
parsePredicatedIntegerInput(String input, String fieldName, IPredicate<Integer> predicate, String domain)873     private int parsePredicatedIntegerInput(String input, String fieldName,
874                                             IPredicate<Integer> predicate,
875                                             String domain) {
876         final int parsedInt;
877         try {
878             if (input.equals("") ||
879                 !predicate.apply(parsedInt = Integer.parseInt(input))) {
880                     String error = "Please enter a " + domain + " " + fieldName;
881                     NotifyManager.getInstance().showError(error);
882                     throw new IllegalArgumentException();
883             }
884         } catch (NumberFormatException e) {
885             String error = "Invalid " + fieldName + ": \"" + input + "\"";
886             NotifyManager.getInstance().showError(error);
887             throw new IllegalArgumentException();
888         }
889         return parsedInt;
890     }
891 
refresh()892     public void refresh() {
893         hostSelector.refresh();
894     }
895 
onTestSelectionChanged()896     public void onTestSelectionChanged() {
897         generateControlFile(false);
898         setInputsEnabled();
899 
900         // Set hostless selection to true if the test name contains test_suites
901         display.getHostless().setValue(false);
902         hostSelector.setEnabled(true);
903 
904         if (testSuiteSelected()) {
905             display.getHostless().setValue(true);
906             hostSelector.setEnabled(false);
907         }
908     }
909 
testSuiteSelected()910     private boolean testSuiteSelected() {
911         for (JSONObject test : testSelector.getSelectedTests()) {
912             if (Utils.jsonToString(test.get("name")).toLowerCase().contains("test_suites:"))
913             {
914                 return true;
915             }
916         }
917 
918         return false;
919     }
920 
setRebootSelectorDefault(RadioChooser chooser, String name)921     private void setRebootSelectorDefault(RadioChooser chooser, String name) {
922         JSONObject user = staticData.getData("current_user").isObject();
923         String defaultOption = Utils.jsonToString(user.get(name));
924         chooser.setDefaultChoice(defaultOption);
925     }
926 
selectPreferredDroneSet()927     private void selectPreferredDroneSet() {
928         JSONObject user = staticData.getData("current_user").isObject();
929         JSONValue droneSet = user.get("drone_set");
930         if (droneSet.isNull() == null) {
931             String preference = Utils.jsonToString(user.get("drone_set"));
932             display.getDroneSet().selectByName(preference);
933         }
934     }
935 
onPreferencesChanged()936     public void onPreferencesChanged() {
937         setRebootSelectorDefault(rebootBefore, "reboot_before");
938         setRebootSelectorDefault(rebootAfter, "reboot_after");
939         selectPreferredDroneSet();
940         testSelector.reset();
941     }
942 
fetchImageTests()943     private void fetchImageTests() {
944         testSelector.setImageTests(new JSONArray());
945 
946         String currentTestSourceBuild = display.getTestSourceBuildList().getSelectedValue();
947         Boolean fetchedFromBuild = display.getFetchTestsFromBuildCheckBox().getValue();
948         if (!fetchedFromBuild ||
949             currentTestSourceBuild == null || currentTestSourceBuild.isEmpty()) {
950           // Tests are from static Moblab build.
951           testSelector.setImageTests(new JSONArray());
952           testSelector.reset();
953           return;
954         }
955 
956         JSONObject params = new JSONObject();
957         params.put("build", new JSONString(currentTestSourceBuild));
958         params.put("ignore_invalid_tests", JSONBoolean.getInstance(true));
959         rpcProxy.rpcCall("get_tests_by_build", params, new JsonRpcCallback() {
960             @Override
961             public void onSuccess(JSONValue result) {
962                 JSONArray tests = result.isArray();
963                 testSelector.setImageTests(tests);
964                 testSelector.reset();
965             }
966 
967             @Override
968             public void onError(JSONObject errorObject) {
969                 super.onError(errorObject);
970                 NotifyManager.getInstance().showError(
971                     "Failed to update tests for given build.");
972                 testSelector.reset();
973             }
974         });
975     }
976 }
977