[autotest]  Display only tests supported by build

The 'Create Job' tab was updated so that an 'Update Tests'
button was placed next to the entry for the image URL.

With this change, pressing the 'Update Tests' button when
a valid build is specified updates the test selection to
only contain tests that are specified in that image.

BUG=chromium:486221
DEPLOY=afe
TEST=Tested changes on a panther moblab.

Change-Id: I9817c3939c9454e7e2b558fa525496cc201fa03a
Reviewed-on: https://chromium-review.googlesource.com/272285
Reviewed-by: Dan Shi <dshi@chromium.org>
Tested-by: Matthew Sartori <msartori@chromium.org>
Reviewed-by: Simran Basi <sbasi@chromium.org>
Commit-Queue: Aviv Keshet <akeshet@chromium.org>
diff --git a/frontend/afe/site_rpc_interface.py b/frontend/afe/site_rpc_interface.py
index dd991a8..0cd3739 100644
--- a/frontend/afe/site_rpc_interface.py
+++ b/frontend/afe/site_rpc_interface.py
@@ -14,6 +14,7 @@
 import shutil
 
 from autotest_lib.frontend.afe import models
+from autotest_lib.client.common_lib import control_data
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.common_lib import global_config
 from autotest_lib.client.common_lib import priorities
@@ -546,3 +547,72 @@
         return rpc_utils.route_rpc_to_master('delete_stable_version',
                                              board=board)
     stable_version_utils.delete(board=board)
+
+
+def get_tests_by_build(build):
+    """Get the tests that are available for the specified build.
+
+    @param build: unique name by which to refer to the image.
+
+    @return: A sorted list of all tests that are in the build specified.
+    """
+    # Stage the test artifacts.
+    try:
+        ds = dev_server.ImageServer.resolve(build)
+        build = ds.translate(build)
+    except dev_server.DevServerException as e:
+        raise ValueError('Could not resolve build %s: %s' % (build, e))
+
+    try:
+        ds.stage_artifacts(build, ['test_suites'])
+    except dev_server.DevServerException as e:
+        raise error.StageControlFileFailure(
+                'Failed to stage %s: %s' % (build, e))
+
+    # Collect the control files specified in this build
+    cfile_getter = control_file_getter.DevServerGetter.create(build, ds)
+    control_file_list = cfile_getter.get_control_file_list()
+
+    test_objects = []
+    _id = 0
+    for control_file_path in control_file_list:
+        # Read and parse the control file
+        control_file = cfile_getter.get_control_file_contents(
+                control_file_path)
+        control_obj = control_data.parse_control_string(control_file)
+
+        # Extract the values needed for the AFE from the control_obj.
+        # The keys list represents attributes in the control_obj that
+        # are required by the AFE
+        keys = ['author', 'doc', 'name', 'time', 'test_type', 'experimental',
+                'test_category', 'test_class', 'dependencies', 'run_verify',
+                'sync_count', 'job_retries', 'retries', 'path']
+
+        test_object = {}
+        for key in keys:
+            test_object[key] = getattr(control_obj, key) if hasattr(
+                    control_obj, key) else ''
+
+        # Unfortunately, the AFE expects different key-names for certain
+        # values, these must be corrected to avoid the risk of tests
+        # being omitted by the AFE.
+        # The 'id' is an additional value used in the AFE.
+        test_object['id'] = _id
+        test_object['description'] = test_object.get('doc', '')
+        test_object['test_time'] = test_object.get('time', 0)
+        test_object['test_retry'] = test_object.get('retries', 0)
+
+        # Fix the test name to be consistent with the current presentation
+        # of test names in the AFE.
+        testpath, subname = os.path.split(control_file_path)
+        testname = os.path.basename(testpath)
+        subname = subname.split('.')[1:]
+        if subname:
+            testname = '%s:%s' % (testname, ':'.join(subname))
+
+        test_object['name'] = testname
+
+        _id += 1
+        test_objects.append(test_object)
+
+    return rpc_utils.prepare_for_serialization(test_objects)
diff --git a/frontend/client/src/autotest/afe/TestSelector.java b/frontend/client/src/autotest/afe/TestSelector.java
index a97b9ec..feab81b 100644
--- a/frontend/client/src/autotest/afe/TestSelector.java
+++ b/frontend/client/src/autotest/afe/TestSelector.java
@@ -141,6 +141,7 @@
     private TestSelectorListener listener;
     private StaticDataRepository staticData = StaticDataRepository.getRepository();
     private List<JSONObject> selectedTests = new ArrayList<JSONObject>();
+    private JSONArray imageTests = new JSONArray();
 
     private Display display;
 
@@ -165,12 +166,17 @@
         display.getTestTable().clear();
 
         JSONArray tests = staticData.getData("tests").isArray();
+
+        if (imageTests.size() > 0) {
+            tests = imageTests;
+        }
+
         for (JSONObject test : new JSONArrayList<JSONObject>(tests)) {
             if (!includeExperimentalTests()
                     && test.get("experimental").isBoolean().booleanValue()) {
                 continue;
             }
-            String testType = test.get("test_type").isString().stringValue();
+            String testType = test.get("test_type").isString().stringValue().toLowerCase();
             String testName = test.get("name").isString().stringValue().toLowerCase();
             if (testType.equals(getSelectedTestType()) &&
                 testName.contains(getTestNameFilterText())) {
@@ -201,7 +207,7 @@
     }
 
     public String getSelectedTestType() {
-        return display.getTestTypeSelect().getSelectedName();
+        return display.getTestTypeSelect().getSelectedName().toLowerCase();
     }
 
     public String getTestNameFilterText() {
@@ -249,4 +255,8 @@
         selectedTests.removeAll(objects);
         notifyListener();
     }
+
+    public void setImageTests(JSONArray tests) {
+        imageTests = tests;
+    }
 }
diff --git a/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java b/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java
index 661399c..881bde1 100644
--- a/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java
+++ b/frontend/client/src/autotest/afe/create/CreateJobViewDisplay.java
@@ -64,6 +64,7 @@
         "?",
         "Name of the test image to use. Example: \"x86-alex-release/R27-3837.0.0\". " +
         "If no image is specified, the latest ToT image is used.");
+    private Button fetchImageTestsButton = new Button("Fetch Tests from Build");
     private TextBox timeout = new TextBox();
     private ToolTip timeoutToolTip = new ToolTip(
         "?",
@@ -299,6 +300,7 @@
         panel.add(jobNameToolTip, "create_job_name");
         panel.add(image_url, "create_image_url");
         panel.add(image_urlToolTip, "create_image_url");
+        panel.add(fetchImageTestsButton, "fetch_image_tests");
         panel.add(testSelector, "create_tests");
         panel.add(controlFilePanel, "create_edit_control");
         panel.add(hostSelector, "create_host_selector");
@@ -445,4 +447,8 @@
     public void setControlFilePanelOpen(boolean isOpen) {
         controlFilePanel.setOpen(isOpen);
     }
+
+    public HasClickHandlers getFetchImageTestsButton() {
+        return fetchImageTestsButton;
+    }
 }
diff --git a/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java b/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
index 0f7b405..104b815 100644
--- a/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
+++ b/frontend/client/src/autotest/afe/create/CreateJobViewPresenter.java
@@ -93,6 +93,7 @@
         public IButton getSubmitJobButton();
         public HasClickHandlers getCreateTemplateJobButton();
         public HasClickHandlers getResetButton();
+        public HasClickHandlers getFetchImageTestsButton();
     }
 
     private static final String EDIT_CONTROL_STRING = "Edit control file";
@@ -574,6 +575,17 @@
             }
         });
 
+        display.getFetchImageTestsButton().addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                String imageUrl = display.getImageUrl().getText();
+                if (imageUrl == null || imageUrl.isEmpty()) {
+                    NotifyManager.getInstance().showMessage(
+                        "No build was specified for fetching tests.");
+                }
+                fetchImageTests();
+            }
+        });
+
         reset();
 
         if (staticData.getData("drone_sets_enabled").isBoolean().booleanValue()) {
@@ -623,6 +635,8 @@
         dependencies = new JSONArray();
         display.getPool().setText("");
         display.getArgs().setText("");
+        display.getImageUrl().setText("");
+        fetchImageTests();
     }
 
     private void submitJob(final boolean isTemplate) {
@@ -862,4 +876,34 @@
     private boolean parameterizedJobsEnabled() {
         return staticData.getData("parameterized_jobs").isBoolean().booleanValue();
     }
+
+    private void fetchImageTests() {
+        testSelector.setImageTests(new JSONArray());
+
+        String imageUrl = display.getImageUrl().getText();
+        if (imageUrl == null || imageUrl.isEmpty()) {
+            testSelector.reset();
+            return;
+        }
+
+        JSONObject params = new JSONObject();
+        params.put("build", new JSONString(imageUrl));
+
+        rpcProxy.rpcCall("get_tests_by_build", params, new JsonRpcCallback() {
+            @Override
+            public void onSuccess(JSONValue result) {
+                JSONArray tests = result.isArray();
+                testSelector.setImageTests(tests);
+                testSelector.reset();
+            }
+
+            @Override
+            public void onError(JSONObject errorObject) {
+                super.onError(errorObject);
+                NotifyManager.getInstance().showError(
+                    "Failed to update tests for given build.");
+                testSelector.reset();
+            }
+        });
+    }
 }
diff --git a/frontend/client/src/autotest/public/AfeClient.html b/frontend/client/src/autotest/public/AfeClient.html
index 2d02b54..9d3d9b3 100644
--- a/frontend/client/src/autotest/public/AfeClient.html
+++ b/frontend/client/src/autotest/public/AfeClient.html
@@ -180,10 +180,13 @@
           <tr class="data-row">
             <td class="field-name">Job name:</td>
             <td class="has-tooltip" id="create_job_name"></td>
+            <td><!-- Dummy cell so background colour fills entire row !--></td>
           </tr>
           <tr class="data-row data-row-alternate">
             <td class="field-name">Image URL/Build: (optional)</td>
-            <td class="has-tooltip" id="create_image_url"></td></tr>
+            <td class="has-tooltip" id="create_image_url"></td>
+            <td class="button" id="fetch_image_tests"></td>
+          </tr>
         </table>
         <br>