igt: add Android compatible test runner.

Adds compatibility runner to integrate igt-gpu-tools to the android
test runner, tradefederation. Includes a python unittest runner.

Test: atest igt_gpu_tools
Fixes: 152336173
Change-Id: Ie936aea2993787bbaa9197c283e3047e866585c1
diff --git a/Android.bp b/Android.bp
index 477efe2..3eebe4e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,6 +12,15 @@
     ],
     static_libs: ["libelf", "libkmod", "libion", "liblog"],
     shared_libs: ["libdrm"],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64"
+        },
+    },
 }
 
 cc_library_static {
@@ -58,28 +67,49 @@
     name: "gem_blt",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["benchmarks/gem_blt.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "kms_flip",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/kms_flip.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "kms_atomic",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/kms_atomic.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "ion_fb",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/ion_fb.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "kms_vblank",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/kms_vblank.c"],
+    gtest: false,
+}
+
+python_test_host {
+    name: "igt_gpu_tools",
+    main: "scripts/test_igt_gpu_tools.py",
+    srcs: [ "scripts/test_igt_gpu_tools.py", ],
+    data: [ "scripts/test_igt_gpu_tools.xml" ],
+    test_config: "scripts/test_igt_gpu_tools.xml",
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
 }
diff --git a/scripts/test_igt_gpu_tools.py b/scripts/test_igt_gpu_tools.py
new file mode 100644
index 0000000..4597836
--- /dev/null
+++ b/scripts/test_igt_gpu_tools.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+
+# 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.
+
+import os
+import unittest
+from xml.etree import ElementTree
+import subprocess
+import sys
+
+VERBOSE = True
+TEST_CONFIG = os.path.join(os.path.dirname(__file__), "test_igt_gpu_tools.xml")
+
+""" Extracts tests from AndroidTest.xml compatible file
+    param: xmlfile str containing path to AndroidTest.xml compatible xml file
+"""
+def extract_tests_from_xml(xmlfile):
+    tree = ElementTree.parse(xmlfile)
+    root = tree.getroot()
+    return [(o.attrib['key'], o.attrib['value'])
+            for o in root.findall("./target_preparer/option")
+            if o.attrib['name'] == "push-file"]
+
+def run_command(command):
+    serial_number = os.environ.get("ANDROID_SERIAL", "")
+    if not serial_number:
+        raise "$ANDROID_SERIAL is empty, device must be specified"
+    full_command = ["adb", "-s", serial_number, "shell"] + command
+    if VERBOSE:
+        print("+" + " ".join(full_command))
+    ret = subprocess.run(full_command, capture_output=True, universal_newlines=True)
+    if VERBOSE:
+        print(ret.stdout)
+    return ret.stdout
+
+class IGTGpuToolsBinary():
+    """Harness object for a specific IGT GPU test binary. """
+
+    """ param: ondevice_test_path The IGT binary path on device """
+    def __init__(self, ondevice_test_path):
+        self._path = ondevice_test_path
+        subtests = run_command([self._path, "--list-subtests"])
+        self._subtests = list(filter(lambda x: x != "", subtests.split("\n")))
+
+    """lists subtests detected in tests.
+       return: list of strings indicating subtests found in binary.
+    """
+    def subtests(self):
+        return self._subtests;
+
+    """Runs a subtest.
+
+        param: subtest_name One of the subtests listed by self.subtest()
+        return: a subprocess.CompletedProcess
+    """
+    def run_subtest(self, subtest_name):
+        if subtest_name not in self._subtests:
+            error
+        return run_command([self._path, "--run-subtest", subtest_name])
+
+class IGTGpuToolsTest(unittest.TestCase):
+    """Tests for DRM/KMS using Intel Graphics Tests suite"""
+
+    """ param: subtest A valid subtest for the binary
+        param: the IGT binary to run as a unit test.
+    """
+    def __init__(self, subtest, binary):
+        self._subtest_name = subtest
+        self._test_binary = binary
+        setattr(self, self._subtest_name, self.runTest)
+        super(IGTGpuToolsTest, self).__init__(methodName=self._subtest_name)
+
+    """ Runs the test set in the constructor """
+    def runTest(self):
+        output = self._test_binary.run_subtest(self._subtest_name)
+        for line in output.split("\n"):
+            if "Subtest" not in line:
+                continue;
+            if "SKIP" in line:
+                self.skipTest("{} - skipped".format(self._subtest_name))
+                break
+            if "SUCCESS" in line:
+                break
+            if "FAIL" in line:
+                self.fail(output)
+                break
+            self.fail("could not parse test output; test runner failure")
+
+ANDROID_RUNNER_REQUIRED_VERBOSITY = 2
+
+def main():
+    """main entrypoint for test runner"""
+
+    runner = unittest.TextTestRunner(stream=sys.stderr, verbosity=ANDROID_RUNNER_REQUIRED_VERBOSITY)
+    suite = unittest.TestSuite()
+    for test, test_path in extract_tests_from_xml(TEST_CONFIG):
+        print("Found IGT-GPU-tools test {}".format(test))
+        subsuite = unittest.TestSuite()
+        binary = IGTGpuToolsBinary(test_path)
+        for subtest in binary.subtests():
+            print("\tFound subtest {}".format(subtest))
+            subsuite.addTest(IGTGpuToolsTest(subtest, binary))
+        suite.addTest(subsuite)
+    runner.run(suite)
+
+if __name__=="__main__":
+    main()
diff --git a/scripts/test_igt_gpu_tools.xml b/scripts/test_igt_gpu_tools.xml
new file mode 100644
index 0000000..63a5feb
--- /dev/null
+++ b/scripts/test_igt_gpu_tools.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config to run IGT GPU tests">
+    <option name="logcat-on-failure" value="false" />
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="stop" />
+        <option name="run-command" value="stop $(grep -rn composer /vendor/etc/init | grep service | cut -d ' ' -f 2)" />
+        <option name="teardown-command" value="stop $(grep -rn composer /vendor/etc/init | grep service | cut -d ' ' -f 2)" />
+        <option name="teardown-command" value="start" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true"/>
+        <option name="append-bitness" value="true"/>
+        <option name="push-file"
+            key="kms_atomic" value="/data/local/tmp/kms_atomic"/>
+        <option name="push-file"
+            key="kms_flip" value="/data/local/tmp/kms_flip"/>
+        <option name="push-file"
+            key="kms_vblank" value="/data/local/tmp/kms_vblank"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="igt_gpu_tools" />
+        <option name="inject-android-serial" value="true" />
+        <option name="test-timeout" value="30m" />
+    </test>
+</configuration>