Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2cc6a6c authored by Mingjun Yang's avatar Mingjun Yang
Browse files

Implement test discovery agent for test zip discovery

BUG: 371603869

Change-Id: Ia9f2e620db7625bccc848324f494e43e4374bcfe
Test: Verified in presubmit, partially built test zips by referring to test discovery result in presubmit build, and presubmit test passed.
parent d91c7bab
Loading
Loading
Loading
Loading
+80 −5
Original line number Diff line number Diff line
@@ -11,18 +11,33 @@
# 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.
"""Test discovery agent that uses TradeFed to discover test artifacts."""
import glob
import json
import logging
import os
import subprocess
import buildbot


class TestDiscoveryAgent:
  """Test discovery agent."""

  _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (
      "tools/tradefederation/prebuilts/filegroups/tradefed/"
  _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (
      "vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/"
  )

  _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = "NoPossibleTestDiscovery"

  _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes"

  _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt"

  def __init__(
      self,
      tradefed_args: list[str],
      test_mapping_zip_path: str,
      tradefed_jar_revelant_files_path: str = _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH,
      test_mapping_zip_path: str = "",
      tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH,
  ):
    self.tradefed_args = tradefed_args
    self.test_mapping_zip_path = test_mapping_zip_path
@@ -34,7 +49,46 @@ class TestDiscoveryAgent:
    Returns:
      A list of test zip regexes that TF is going to try to pull files from.
    """
    return []
    test_discovery_output_file_name = os.path.join(
        buildbot.OutDir(), self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME
    )
    with open(
        test_discovery_output_file_name, mode="w+t"
    ) as test_discovery_output_file:
      java_args = []
      java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java")
      java_args.append("-cp")
      java_args.append(
          self.create_classpath(self.tradefed_jar_relevant_files_path)
      )
      java_args.append(
          "com.android.tradefed.observatory.TestZipDiscoveryExecutor"
      )
      java_args.extend(self.tradefed_args)
      env = os.environ.copy()
      env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name})
      logging.info(f"Calling test discovery with args: {java_args}")
      try:
        result = subprocess.run(args=java_args, env=env, text=True, check=True)
        logging.info(f"Test zip discovery output: {result.stdout}")
      except subprocess.CalledProcessError as e:
        raise TestDiscoveryError(
            f"Failed to run test discovery, strout: {e.stdout}, strerr:"
            f" {e.stderr}, returncode: {e.returncode}"
        )
      data = json.loads(test_discovery_output_file.read())
      logging.info(f"Test discovery result file content: {data}")
      if (
          self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data
          and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY]
      ):
        raise TestDiscoveryError("No possible test discovery")
      if (
          data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None
          or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is []
      ):
        raise TestDiscoveryError("No test zip regexes returned")
      return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY]

  def discover_test_modules(self) -> list[str]:
    """Discover test modules from TradeFed.
@@ -44,3 +98,24 @@ class TestDiscoveryAgent:
      TradeFed test args.
    """
    return []

  def create_classpath(self, directory):
    """Creates a classpath string from all .jar files in the given directory.

    Args:
      directory: The directory to search for .jar files.

    Returns:
      A string representing the classpath, with jar files separated by the
      OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows).
    """
    jar_files = glob.glob(os.path.join(directory, "*.jar"))
    return os.pathsep.join(jar_files)


class TestDiscoveryError(Exception):
  """A TestDiscoveryErrorclass."""

  def __init__(self, message):
    super().__init__(message)
    self.message = message