Enable YAML config to be loaded and start setting up default objects

Since the new config is meant to minimize the required config,
we will by default load some default objects.

Test: unit tests
./tradefed.sh run /tmp/test-config.tf_yaml
Bug: 157091025

Change-Id: I5d085e2c7b8f134d7a17e23b0a642df4c81e8e3e
diff --git a/res/META-INF/services/com.android.tradefed.config.yaml.IDefaultObjectLoader b/res/META-INF/services/com.android.tradefed.config.yaml.IDefaultObjectLoader
new file mode 100644
index 0000000..0bdf165
--- /dev/null
+++ b/res/META-INF/services/com.android.tradefed.config.yaml.IDefaultObjectLoader
@@ -0,0 +1 @@
+com.android.tradefed.config.yaml.OpenObjectLoader
diff --git a/src/com/android/tradefed/command/CommandOptions.java b/src/com/android/tradefed/command/CommandOptions.java
index a8f23de..8316863 100644
--- a/src/com/android/tradefed/command/CommandOptions.java
+++ b/src/com/android/tradefed/command/CommandOptions.java
@@ -67,7 +67,9 @@
             "the minimum invocation time in ms when in loop mode.")
     private Long mMinLoopTime = 10L * 60L * 1000L;
 
-    @Option(name = "test-tag", description = "Identifier for the invocation during reporting.")
+    public static final String TEST_TAG_OPTION = "test-tag";
+
+    @Option(name = TEST_TAG_OPTION, 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 "
diff --git a/src/com/android/tradefed/config/ConfigurationFactory.java b/src/com/android/tradefed/config/ConfigurationFactory.java
index 98f5f5e..c682694 100644
--- a/src/com/android/tradefed/config/ConfigurationFactory.java
+++ b/src/com/android/tradefed/config/ConfigurationFactory.java
@@ -18,6 +18,7 @@
 
 import com.android.ddmlib.Log;
 import com.android.tradefed.command.CommandOptions;
+import com.android.tradefed.config.yaml.ConfigurationYamlParser;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.util.ClassPathScanner;
 import com.android.tradefed.util.ClassPathScanner.IClassPathFilter;
@@ -419,6 +420,10 @@
                             new ConfigurationXmlParser(this, deviceTagObject);
                     parser.parse(def, name, bufStream, templateMap, templateSeen);
                     break;
+                case ".tf_yaml":
+                    ConfigurationYamlParser yamlParser = new ConfigurationYamlParser();
+                    yamlParser.parse(def, name, bufStream);
+                    break;
                 default:
                     throw new ConfigurationException(
                             String.format("The config format for %s is not supported.", name));
diff --git a/src/com/android/tradefed/config/yaml/ConfigurationYamlParser.java b/src/com/android/tradefed/config/yaml/ConfigurationYamlParser.java
index 8b4f205..bf4a307 100644
--- a/src/com/android/tradefed/config/yaml/ConfigurationYamlParser.java
+++ b/src/com/android/tradefed/config/yaml/ConfigurationYamlParser.java
@@ -15,6 +15,7 @@
  */
 package com.android.tradefed.config.yaml;
 
+import com.android.tradefed.command.CommandOptions;
 import com.android.tradefed.config.Configuration;
 import com.android.tradefed.config.ConfigurationDef;
 import com.android.tradefed.config.ConfigurationException;
@@ -30,10 +31,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.ServiceLoader;
 import java.util.Set;
 
 /** Parser for YAML style Tradefed configurations */
-class ConfigurationYamlParser {
+public final class ConfigurationYamlParser {
 
     private static final String DESCRIPTION_KEY = "description";
     public static final String DEPENDENCIES_KEY = "dependencies";
@@ -56,6 +58,12 @@
         configDef.setMultiDeviceMode(false);
         Yaml yaml = new Yaml();
         try {
+            configDef.addOptionDef(
+                    CommandOptions.TEST_TAG_OPTION,
+                    null,
+                    source,
+                    source,
+                    Configuration.CMD_OPTIONS_TYPE_NAME);
             Map<String, Object> yamlObjects = (Map<String, Object>) yaml.load(yamlInput);
             translateYamlInTradefed(configDef, yamlObjects);
         } catch (YAMLException e) {
@@ -91,6 +99,13 @@
             throw new ConfigurationException(
                     String.format("'%s' keys are required and were not found.", missingKeys));
         }
+
+        // Add default configured objects
+        ServiceLoader<IDefaultObjectLoader> serviceLoader =
+                ServiceLoader.load(IDefaultObjectLoader.class);
+        for (IDefaultObjectLoader loader : serviceLoader) {
+            loader.addDefaultObjects(configDef);
+        }
     }
 
     /**
diff --git a/src/com/android/tradefed/config/yaml/IDefaultObjectLoader.java b/src/com/android/tradefed/config/yaml/IDefaultObjectLoader.java
new file mode 100644
index 0000000..a32473d
--- /dev/null
+++ b/src/com/android/tradefed/config/yaml/IDefaultObjectLoader.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tradefed.config.yaml;
+
+import com.android.tradefed.config.ConfigurationDef;
+
+/**
+ * Interface for loading the default objects that should be part of our YAML configuration. This
+ * allows to customize the YAML configuration with any objects we need based on the context.
+ */
+public interface IDefaultObjectLoader {
+
+    /** Allows to add any default objects as necessary. */
+    public void addDefaultObjects(ConfigurationDef configDef);
+}
diff --git a/src/com/android/tradefed/config/yaml/OpenObjectLoader.java b/src/com/android/tradefed/config/yaml/OpenObjectLoader.java
new file mode 100644
index 0000000..2c5d0c1
--- /dev/null
+++ b/src/com/android/tradefed/config/yaml/OpenObjectLoader.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tradefed.config.yaml;
+
+import com.android.tradefed.config.Configuration;
+import com.android.tradefed.config.ConfigurationDef;
+import com.android.tradefed.result.suite.SuiteResultReporter;
+
+/** Loader for the default objects available in AOSP. */
+public class OpenObjectLoader implements IDefaultObjectLoader {
+
+    @Override
+    public void addDefaultObjects(ConfigurationDef configDef) {
+        configDef.addConfigObjectDef(
+                Configuration.RESULT_REPORTER_TYPE_NAME, SuiteResultReporter.class.getName());
+    }
+}
diff --git a/src/com/android/tradefed/config/yaml/YamlTestDependencies.java b/src/com/android/tradefed/config/yaml/YamlTestDependencies.java
index 00a98f0..ca3d847 100644
--- a/src/com/android/tradefed/config/yaml/YamlTestDependencies.java
+++ b/src/com/android/tradefed/config/yaml/YamlTestDependencies.java
@@ -40,6 +40,9 @@
 
     public YamlTestDependencies(List<Map<String, Object>> dependencies)
             throws ConfigurationException {
+        if (dependencies == null) {
+            return;
+        }
         for (Map<String, Object> dependencyEntry : dependencies) {
             if (dependencyEntry.containsKey(APKS_KEY)) {
                 for (String apk : (List<String>) dependencyEntry.get(APKS_KEY)) {