Home | History | Annotate | Download | only in create
      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 {
     64         public CheckBoxPanel.Display getCheckBoxPanelDisplay();
     65         public ControlTypeSelect.Display getControlTypeSelectDisplay();
     66         public TestSelector.Display getTestSelectorDisplay();
     67         public HasText getJobName();
     68         public ExtendedListBox getPriorityList();
     69         public HasText getTimeout();
     70         public HasText getMaxRuntime();
     71         public HasText getTestRetry();
     72         public HasText getEmailList();
     73         public ICheckBox getSkipVerify();
     74         public ICheckBox getSkipReset();
     75         public RadioChooser.Display getRebootBefore();
     76         public RadioChooser.Display getRebootAfter();
     77         public HasValue<Boolean> getParseFailedRepair();
     78         public ICheckBox getHostless();
     79         public ICheckBox getRequireSSP();
     80         public HasText getPool();
     81         public ITextBox getArgs();
     82         public HostSelector.Display getHostSelectorDisplay();
     83         public SimplifiedList getDroneSet();
     84         public ITextBox getSynchCountInput();
     85         public ITextArea getControlFile();
     86         public void setControlFilePanelOpen(boolean isOpen);
     87         public ICheckBox getRunNonProfiledIteration();
     88         public ITextBox getImageUrl();
     89         public HasText getViewLink();
     90         public HasCloseHandlers<DisclosurePanel> getControlFilePanelClose();
     91         public HasOpenHandlers<DisclosurePanel> getControlFilePanelOpen();
     92         public IButton getSubmitJobButton();
     93         public HasClickHandlers getCreateTemplateJobButton();
     94         public HasClickHandlers getResetButton();
     95         public ICheckBox getFetchTestsFromBuildCheckBox();
     96         public ITextBox getFirmwareRWBuild();
     97         public ITextBox getFirmwareROBuild();
     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 {
    110         public void onJobCreated(int jobId);
    111     }
    112 
    113     private interface IPredicate<T> {
    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 
    153     public void bindDisplay(Display display) {
    154         this.display = display;
    155     }
    156 
    157     public CreateJobViewPresenter(JobCreateListener listener) {
    158         this.listener = listener;
    159     }
    160 
    161     public void setCloning(boolean cloning) {
    162       this.cloning = cloning;
    163     }
    164 
    165     public boolean isCloning() {
    166       return cloning;
    167     }
    168 
    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 
    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 
    292     private void resetPriorityToDefault() {
    293         JSONValue defaultValue = staticData.getData("default_priority");
    294         String defaultPriority = defaultValue.isString().stringValue();
    295         display.getPriorityList().selectByName(defaultPriority);
    296     }
    297 
    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 
    325     private void updateNonProfiledRunControl() {
    326         boolean anyProfilersChecked = !profilersPanel.getChecked().isEmpty();
    327         display.getRunNonProfiledIteration().setVisible(anyProfilersChecked);
    328     }
    329 
    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      */
    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 
    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 
    410     protected void generateControlFile(boolean readyForSubmit) {
    411         generateControlFile(readyForSubmit, null, null);
    412     }
    413 
    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 
    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 
    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 
    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 
    515     protected void setInputsEnabled() {
    516         testSelector.setEnabled(true);
    517         profilersPanel.setEnabled(true);
    518         handleSkipVerify();
    519         handleSkipReset();
    520         display.getImageUrl().setEnabled(true);
    521     }
    522 
    523     protected void disableInputs() {
    524         testSelector.setEnabled(false);
    525         profilersPanel.setEnabled(false);
    526         display.getImageUrl().setEnabled(false);
    527     }
    528 
    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 
    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 
    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 
    847     private JSONArray getSelectedDependencies() {
    848         return dependencies;
    849     }
    850 
    851     private void setSelectedDependencies(JSONArray dependencies) {
    852         this.dependencies = dependencies;
    853     }
    854 
    855     private int parseNonnegativeIntegerInput(String input, String fieldName) {
    856         return parsePredicatedIntegerInput(input, fieldName,
    857                                            nonnegativeIntegerPredicate,
    858                                            "non-negative");
    859     }
    860 
    861     private int parsePositiveIntegerInput(String input, String fieldName) {
    862         return parsePredicatedIntegerInput(input, fieldName,
    863                                            positiveIntegerPredicate,
    864                                            "positive");
    865     }
    866 
    867     private int parseAnyIntegerInput(String input, String fieldName) {
    868         return parsePredicatedIntegerInput(input, fieldName,
    869                                            anyIntegerPredicate,
    870                                            "integer");
    871     }
    872 
    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 
    892     public void refresh() {
    893         hostSelector.refresh();
    894     }
    895 
    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 
    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 
    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 
    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 
    936     public void onPreferencesChanged() {
    937         setRebootSelectorDefault(rebootBefore, "reboot_before");
    938         setRebootSelectorDefault(rebootAfter, "reboot_after");
    939         selectPreferredDroneSet();
    940         testSelector.reset();
    941     }
    942 
    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