Generalize test-tag usage

If test-tag was set by build_provider then we don't
override it in TestInvocation for now.

Bug: 19801407
Change-Id: I4ac59af6bbebe9a30a47b35d833336935645bc0b
diff --git a/src/com/android/tradefed/build/BootstrapBuildProvider.java b/src/com/android/tradefed/build/BootstrapBuildProvider.java
index 2153c0e..552e667 100644
--- a/src/com/android/tradefed/build/BootstrapBuildProvider.java
+++ b/src/com/android/tradefed/build/BootstrapBuildProvider.java
@@ -50,9 +50,6 @@
 @OptionClass(alias = "bootstrap-build")
 public class BootstrapBuildProvider implements IDeviceBuildProvider {
 
-    @Option(name="test-tag", description="test tag name to supply.")
-    private String mTestTag = "stub";
-
     @Option(name="build-target", description="build target name to supply.")
     private String mBuildTargetName = "bootstrapped";
 
@@ -87,7 +84,7 @@
     public IBuildInfo getBuild(ITestDevice device) throws BuildRetrievalError,
             DeviceNotAvailableException {
         String buildId = device.getBuildId();
-        IBuildInfo info = new DeviceBuildInfo(buildId, mTestTag, mBuildTargetName);
+        IBuildInfo info = new DeviceBuildInfo(buildId, mBuildTargetName);
         if (!device.waitForDeviceShell(mShellAvailableTimeout * 1000)) {
             throw new DeviceNotAvailableException(
                     String.format("Shell did not become available in %d seconds",
diff --git a/src/com/android/tradefed/build/BuildInfo.java b/src/com/android/tradefed/build/BuildInfo.java
index 9ead2b7..a88b462 100644
--- a/src/com/android/tradefed/build/BuildInfo.java
+++ b/src/com/android/tradefed/build/BuildInfo.java
@@ -52,6 +52,18 @@
      * Creates a {@link BuildInfo}
      *
      * @param buildId the build id
+     * @param buildTargetName the build target name
+     */
+    public BuildInfo(String buildId, String buildTargetName) {
+        mBuildId = buildId;
+        mBuildTargetName = buildTargetName;
+        mVersionedFileMap = new Hashtable<String, VersionedFile>();
+    }
+
+    /**
+     * Creates a {@link BuildInfo}
+     *
+     * @param buildId the build id
      * @param testTag the test tag name
      * @param buildTargetName the build target name
      */
@@ -97,6 +109,14 @@
      * {@inheritDoc}
      */
     @Override
+    public void setTestTag(String testTag) {
+        mTestTag = testTag;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public String getTestTag() {
         return mTestTag;
     }
diff --git a/src/com/android/tradefed/build/DeviceBuildInfo.java b/src/com/android/tradefed/build/DeviceBuildInfo.java
index 63023bc..706fc9e 100644
--- a/src/com/android/tradefed/build/DeviceBuildInfo.java
+++ b/src/com/android/tradefed/build/DeviceBuildInfo.java
@@ -37,6 +37,10 @@
         super();
     }
 
+    public DeviceBuildInfo(String buildId, String buildTargetName) {
+        super(buildId, buildTargetName);
+    }
+
     public DeviceBuildInfo(String buildId, String testTag, String buildTargetName) {
         super(buildId, testTag, buildTargetName);
     }
diff --git a/src/com/android/tradefed/build/IBuildInfo.java b/src/com/android/tradefed/build/IBuildInfo.java
index 274ed41..385e1bf 100644
--- a/src/com/android/tradefed/build/IBuildInfo.java
+++ b/src/com/android/tradefed/build/IBuildInfo.java
@@ -48,6 +48,11 @@
     public String getTestTag();
 
     /**
+     * Sets the unique name for the tests being run.
+     */
+    public void setTestTag(String testTag);
+
+    /**
      * Return complete name for the build being tested.
      * <p/>
      * A common implementation is to construct the build target name from a combination of
diff --git a/src/com/android/tradefed/build/OtaDeviceBuildInfo.java b/src/com/android/tradefed/build/OtaDeviceBuildInfo.java
index 135302c..0011c32 100644
--- a/src/com/android/tradefed/build/OtaDeviceBuildInfo.java
+++ b/src/com/android/tradefed/build/OtaDeviceBuildInfo.java
@@ -77,6 +77,14 @@
      * {@inheritDoc}
      */
     @Override
+    public void setTestTag(String testTag) {
+        mBaselineBuild.setTestTag(testTag);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public String getBuildTargetName() {
         return mBaselineBuild.getBuildTargetName();
     }
diff --git a/src/com/android/tradefed/build/OtaZipfileBuildProvider.java b/src/com/android/tradefed/build/OtaZipfileBuildProvider.java
index 8302b85..14617c7 100644
--- a/src/com/android/tradefed/build/OtaZipfileBuildProvider.java
+++ b/src/com/android/tradefed/build/OtaZipfileBuildProvider.java
@@ -50,7 +50,7 @@
                     "Error processing build.prop contents from file: " + getOtaPath(), e);
         }
         String bid = buildProp.getProperty("ro.build.version.incremental");
-        IDeviceBuildInfo buildInfo = new DeviceBuildInfo(bid, "flasher", bid);
+        IDeviceBuildInfo buildInfo = new DeviceBuildInfo(bid, bid);
         buildInfo.setOtaPackageFile(new File(getOtaPath()), bid);
         return buildInfo;
 
diff --git a/src/com/android/tradefed/build/StubBuildProvider.java b/src/com/android/tradefed/build/StubBuildProvider.java
index 6d6f552..b1829c1 100644
--- a/src/com/android/tradefed/build/StubBuildProvider.java
+++ b/src/com/android/tradefed/build/StubBuildProvider.java
@@ -33,9 +33,6 @@
     @Option(name="build-id", description="build id to supply.")
     private String mBuildId = "0";
 
-    @Option(name="test-tag", description="test tag name to supply.")
-    private String mTestTag = "stub";
-
     @Option(name="build-target", description="build target name to supply.")
     private String mBuildTargetName = "stub";
 
@@ -54,7 +51,7 @@
     @Override
     public IBuildInfo getBuild() throws BuildRetrievalError {
         Log.d("BuildProvider", "skipping build provider step");
-        BuildInfo stubBuild = new BuildInfo(mBuildId, mTestTag, mBuildTargetName);
+        BuildInfo stubBuild = new BuildInfo(mBuildId, mBuildTargetName);
         stubBuild.setBuildBranch(mBranch);
         stubBuild.setBuildFlavor(mBuildFlavor);
         for (Map.Entry<String, String> attributeEntry : mBuildAttributes.entrySet()) {
diff --git a/src/com/android/tradefed/command/CommandOptions.java b/src/com/android/tradefed/command/CommandOptions.java
index 5daa4a3..7da1c8a 100644
--- a/src/com/android/tradefed/command/CommandOptions.java
+++ b/src/com/android/tradefed/command/CommandOptions.java
@@ -62,6 +62,13 @@
             updateRule = OptionUpdateRule.LEAST)
     private Long mMaxRandomLoopTime = null;
 
+    @Option(name = "test-tag", description = "Identifier for the invocation during reporting.")
+    private String mTestTag = "stub";
+
+    @Option(name = "test-tag-suffix", description = "suffix for test-tag. appended to test-tag to "
+            + "represents some variants of one test.")
+    private String mTestTagSuffix = null;
+
     @Option(name = "loop", description = "keep running continuously.",
             importance = Importance.ALWAYS)
     private boolean mLoopMode = false;
@@ -261,7 +268,7 @@
     }
 
     /**
-     * {@inheritDoc)
+     * {@inheritDoc}
      */
     @Override
     public void setShardCount(Integer shardCount) {
@@ -283,4 +290,28 @@
     public void setShardIndex(Integer shardIndex) {
         mShardIndex = shardIndex;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setTestTag(String testTag) {
+       mTestTag = testTag;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getTestTag() {
+        return mTestTag;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getTestTagSuffix() {
+        return mTestTagSuffix;
+    }
 }
diff --git a/src/com/android/tradefed/command/ICommandOptions.java b/src/com/android/tradefed/command/ICommandOptions.java
index 0305abb..d4f9aef 100644
--- a/src/com/android/tradefed/command/ICommandOptions.java
+++ b/src/com/android/tradefed/command/ICommandOptions.java
@@ -74,6 +74,23 @@
     public void setLoopMode(boolean loopMode);
 
     /**
+     * Return the test-tag for the invocation. Default is 'stub' if unspecified.
+     */
+    public String getTestTag();
+
+    /**
+     * Sets the test-tag for the invocation.
+     *
+     * @param testTag
+     */
+    public void setTestTag(String testTag);
+
+    /**
+     * Return the test-tag suffix, appended to test-tag to represents some variants of one test.
+     */
+    public String getTestTagSuffix();
+
+    /**
      * Creates a copy of the {@link ICommandOptions} object.
      */
     public ICommandOptions clone();
diff --git a/src/com/android/tradefed/invoker/TestInvocation.java b/src/com/android/tradefed/invoker/TestInvocation.java
index 8dc6bfd..d5933bc 100644
--- a/src/com/android/tradefed/invoker/TestInvocation.java
+++ b/src/com/android/tradefed/invoker/TestInvocation.java
@@ -21,8 +21,6 @@
 import com.android.tradefed.build.ExistingBuildProvider;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IDeviceBuildProvider;
-import com.android.tradefed.command.CommandOptions;
-import com.android.tradefed.command.ICommandOptions;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.ConfigurationFactory;
 import com.android.tradefed.config.IConfiguration;
@@ -50,9 +48,9 @@
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.testtype.INativeDeviceTest;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.INativeDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IResumableTest;
 import com.android.tradefed.testtype.IRetriableTest;
@@ -391,12 +389,29 @@
             info.addBuildAttribute("shard_index",
                     config.getCommandOptions().getShardIndex().toString());
         }
+        // TODO: update all the configs to only use test-tag from CommandOption and not build
+        // providers.
+        // When CommandOption is set, it overrides any test-tag from build_providers
+        if (!"stub".equals(config.getCommandOptions().getTestTag())) {
+            String testTag = config.getCommandOptions().getTestTag();
+            if (config.getCommandOptions().getTestTagSuffix() != null) {
+                testTag = String.format("%s-%s", testTag,
+                        config.getCommandOptions().getTestTagSuffix());
+            }
+            info.setTestTag(testTag);
+        } else if (info.getTestTag() == null || info.getTestTag().isEmpty()) {
+            // We ensure that that a default test-tag is always available.
+            info.setTestTag("stub");
+        } else {
+            CLog.w("Using the test-tag from the build_provider. Consider updating your config to"
+                    + " have no alias/namespace in front of test-tag.");
+        }
     }
 
     /**
      * Updates the {@link IConfiguration} to run a single shard if a shard index is set.
      *
-     * @see IStrctiShardableTest
+     * @see IStrictShardableTest
      *
      * @param config the {@link IConfiguration}.
      */
diff --git a/tests/res/testconfigs/multi-device-outside-tag.xml b/tests/res/testconfigs/multi-device-outside-tag.xml
index 2e93b78..91ac236 100644
--- a/tests/res/testconfigs/multi-device-outside-tag.xml
+++ b/tests/res/testconfigs/multi-device-outside-tag.xml
@@ -20,15 +20,12 @@
     <device name="device1">
     </device>
     <device name="device2" >
-        <build_provider class="com.android.tradefed.build.StubBuildProvider">
-        </build_provider>
+        <build_provider class="com.android.tradefed.build.StubBuildProvider" />
         <target_preparer class="com.android.tradefed.targetprep.StubTargetPreparer" />
     </device>
 
     <device name="device3" >
-        <build_provider class="com.android.tradefed.build.StubBuildProvider">
-            <option name="test-tag" value="test-tag3" />
-        </build_provider>
+        <build_provider class="com.android.tradefed.build.StubBuildProvider" />
         <target_preparer class="com.android.tradefed.targetprep.StubTargetPreparer" />
     </device>
 
diff --git a/tests/res/testconfigs/multi-device.xml b/tests/res/testconfigs/multi-device.xml
index b14e4fa..f329cad 100644
--- a/tests/res/testconfigs/multi-device.xml
+++ b/tests/res/testconfigs/multi-device.xml
@@ -21,14 +21,13 @@
     </device>
 
     <device name="device2" >
-        <build_provider class="com.android.tradefed.build.StubBuildProvider">
-        </build_provider>
+        <build_provider class="com.android.tradefed.build.StubBuildProvider" />
         <target_preparer class="com.android.tradefed.targetprep.StubTargetPreparer" />
     </device>
 
     <device name="device3" >
         <build_provider class="com.android.tradefed.build.StubBuildProvider">
-            <option name="test-tag" value="test-tag3" />
+            <option name="build-flavor" value="build-flavor3" />
         </build_provider>
         <target_preparer class="com.android.tradefed.targetprep.StubTargetPreparer" />
     </device>
diff --git a/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java b/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
index 0ec54d4..43eb83d 100644
--- a/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
+++ b/tests/src/com/android/tradefed/config/ConfigurationFactoryTest.java
@@ -872,8 +872,8 @@
         assertNotNull(config.getDeviceConfigByName("device3"));
         assertEquals("0", config.getDeviceConfigByName("device3")
                 .getBuildProvider().getBuild().getBuildId());
-        assertEquals("test-tag3", config.getDeviceConfigByName("device3")
-                .getBuildProvider().getBuild().getTestTag());
+        assertEquals("build-flavor3", config.getDeviceConfigByName("device3")
+                .getBuildProvider().getBuild().getBuildFlavor());
         assertEquals(1, config.getDeviceConfigByName("device3")
                 .getTargetPreparers().size());
         assertTrue(config.getDeviceConfigByName("device3")
@@ -918,8 +918,8 @@
         assertNotNull(config.getDeviceConfigByName("device3"));
         assertEquals("30", config.getDeviceConfigByName("device3")
                 .getBuildProvider().getBuild().getBuildId());
-        assertEquals("test-tag3", config.getDeviceConfigByName("device3")
-                .getBuildProvider().getBuild().getTestTag());
+        assertEquals("build-flavor3", config.getDeviceConfigByName("device3")
+                .getBuildProvider().getBuild().getBuildFlavor());
         assertEquals(1, config.getDeviceConfigByName("device3")
                 .getTargetPreparers().size());
         assertTrue(config.getDeviceConfigByName("device3")
@@ -960,8 +960,8 @@
         assertNotNull(config.getDeviceConfigByName("device3"));
         assertEquals("20", config.getDeviceConfigByName("device3")
                 .getBuildProvider().getBuild().getBuildId());
-        assertEquals("test-tag3", config.getDeviceConfigByName("device3")
-                .getBuildProvider().getBuild().getTestTag());
+        assertEquals("build-flavor3", config.getDeviceConfigByName("device3")
+                .getBuildProvider().getBuild().getBuildFlavor());
         assertEquals(1, config.getDeviceConfigByName("device3")
                 .getTargetPreparers().size());
         assertTrue(config.getDeviceConfigByName("device3")
diff --git a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
index 09aeb7a..053266c 100644
--- a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
+++ b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
@@ -136,7 +136,7 @@
         EasyMock.expect(mMockBuildInfo.getBuildAttributes()).andStubReturn(EMPTY_MAP);
         EasyMock.expect(mMockBuildInfo.getBuildBranch()).andStubReturn("branch");
         EasyMock.expect(mMockBuildInfo.getBuildFlavor()).andStubReturn("flavor");
-        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("");
+
         // always expect logger initialization and cleanup calls
         mMockLogRegistry.registerLogger(mMockLogger);
         mMockLogger.init();
@@ -208,6 +208,7 @@
     public void testInvoke_buildFailed() throws Throwable  {
         BuildRetrievalError exception = new BuildRetrievalError("error", null, mMockBuildInfo);
         EasyMock.expect(mMockBuildProvider.getBuild()).andThrow(exception);
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("");
         setupMockFailureListeners(exception);
         setupInvoke();
         IRemoteTest test = EasyMock.createMock(IRemoteTest.class);
@@ -390,6 +391,7 @@
         mMockDevice.clearLastConnectedWifiNetwork();
         mMockDevice.setOptions((TestDeviceOptions)EasyMock.anyObject());
         mMockBuildInfo.setDeviceSerial(SERIAL);
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("");
         mMockDevice.startLogcat();
         mMockPreparer.setUp(mMockDevice, mMockBuildInfo);
 
@@ -430,6 +432,8 @@
         mMockDevice.clearLastConnectedWifiNetwork();
         mMockDevice.setOptions((TestDeviceOptions)EasyMock.anyObject());
         mMockBuildInfo.setDeviceSerial(SERIAL);
+        mMockBuildInfo.setTestTag(EasyMock.eq("stub"));
+        EasyMock.expectLastCall().times(2);
         mMockDevice.startLogcat();
         mMockPreparer.setUp(mMockDevice, mMockBuildInfo);
         mMockLogSaver.invocationStarted(mMockBuildInfo);
@@ -628,9 +632,12 @@
         mStubConfiguration.setTest(test);
         mStubConfiguration.setCommandLine(commandLine);
         mStubConfiguration.getCommandOptions().setShardCount(shardCount);
+        mMockBuildInfo.setTestTag(EasyMock.eq("stub"));
+        EasyMock.expectLastCall();
 
         setupInvoke();
         EasyMock.expect(mMockBuildProvider.getBuild()).andReturn(mMockBuildInfo);
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("");
         mMockBuildInfo.addBuildAttribute("command_line_args", "config arg");
         mMockBuildInfo.addBuildAttribute("shard_count", "10");
         IConfiguration shardConfig = new Configuration("foo", "bar");
@@ -742,6 +749,84 @@
     }
 
     /**
+     * Test the test-tag is set when the IBuildInfo's test-tag is not.
+     */
+    public void testInvoke_testtag() throws Throwable {
+        String[] commandLine = {"run", "empty"};
+        mStubConfiguration.setCommandLine(commandLine);
+        mStubConfiguration.getCommandOptions().setTestTag("not-default");
+
+        setupInvoke();
+        EasyMock.expect(mMockDevice.getLogcat())
+                .andReturn(new ByteArrayInputStreamSource(new byte[0])).times(2);
+        EasyMock.expect(mMockLogger.getLog())
+                .andReturn(new ByteArrayInputStreamSource(new byte[0]));
+        mMockBuildInfo.setDeviceSerial(SERIAL);
+        mMockBuildProvider.cleanUp(mMockBuildInfo);
+        setupMockSuccessListeners();
+        EasyMock.expect(mMockBuildProvider.getBuild()).andReturn(mMockBuildInfo);
+        mMockBuildInfo.addBuildAttribute("command_line_args", "run empty");
+        mMockPreparer.setUp(mMockDevice, mMockBuildInfo);
+        // Default build is "stub" so we set the test-tag
+        mMockBuildInfo.setTestTag(EasyMock.eq("not-default"));
+        EasyMock.expectLastCall();
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("stub");
+        replayMocks();
+        mTestInvocation.invoke(mMockDevice, mStubConfiguration, mockRescheduler);
+        verifyMocks();
+    }
+
+    /**
+     * Test the test-tag of the IBuildInfo is not modified when the CommandOption default test-tag
+     * is not modified.
+     */
+    public void testInvoke_testtag_notset() throws Throwable {
+        String[] commandLine = {"run", "empty"};
+        mStubConfiguration.setCommandLine(commandLine);
+        setupInvoke();
+        EasyMock.expect(mMockDevice.getLogcat())
+                .andReturn(new ByteArrayInputStreamSource(new byte[0])).times(2);
+        EasyMock.expect(mMockLogger.getLog())
+                .andReturn(new ByteArrayInputStreamSource(new byte[0]));
+        mMockBuildInfo.setDeviceSerial(SERIAL);
+        mMockBuildProvider.cleanUp(mMockBuildInfo);
+        setupMockSuccessListeners();
+        EasyMock.expect(mMockBuildProvider.getBuild()).andReturn(mMockBuildInfo);
+        mMockBuildInfo.addBuildAttribute("command_line_args", "run empty");
+        mMockPreparer.setUp(mMockDevice, mMockBuildInfo);
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("buildprovidertesttag");
+
+        replayMocks();
+        mTestInvocation.invoke(mMockDevice, mStubConfiguration, mockRescheduler);
+        verifyMocks();
+    }
+
+    /**
+     * Test the test-tag of the IBuildInfo is not set and Command Option is not set either.
+     * A default 'stub' test-tag is set to ensure reporting is done.
+     */
+    public void testInvoke_notesttag() throws Throwable {
+        String[] commandLine = {"run", "empty"};
+        mStubConfiguration.setCommandLine(commandLine);
+        setupInvoke();
+        EasyMock.expect(mMockDevice.getLogcat())
+                .andReturn(new ByteArrayInputStreamSource(new byte[0])).times(2);
+        EasyMock.expect(mMockLogger.getLog())
+                .andReturn(new ByteArrayInputStreamSource(new byte[0]));
+        mMockBuildInfo.setDeviceSerial(SERIAL);
+        mMockBuildProvider.cleanUp(mMockBuildInfo);
+        setupMockSuccessListeners();
+        EasyMock.expect(mMockBuildProvider.getBuild()).andReturn(mMockBuildInfo);
+        mMockBuildInfo.addBuildAttribute("command_line_args", "run empty");
+        mMockPreparer.setUp(mMockDevice, mMockBuildInfo);
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn(null);
+        mMockBuildInfo.setTestTag(EasyMock.eq("stub"));
+        EasyMock.expectLastCall();
+        replayMocks();
+        mTestInvocation.invoke(mMockDevice, mStubConfiguration, mockRescheduler);
+        verifyMocks();
+    }
+    /**
      * Set up expected conditions for normal run up to the part where tests are run.
      *
      * @param test the {@link Test} to use.
@@ -779,6 +864,9 @@
                 .andReturn(new ByteArrayInputStreamSource(new byte[0]));
         mMockBuildInfo.setDeviceSerial(SERIAL);
         mMockBuildProvider.cleanUp(mMockBuildInfo);
+        mMockBuildInfo.setTestTag(EasyMock.eq("stub"));
+        EasyMock.expectLastCall();
+        EasyMock.expect(mMockBuildInfo.getTestTag()).andStubReturn("");
     }
 
     /**