Atest: Unit tests prebuilt release in Python2.

Test: $ source build/envsetup.sh ; lunch
      $ make clean atest atest_unittests
      ### source code run ###
      $ atest hello_world_test
      $ atest -v FrameworksServicesTests CtsJankDeviceTestCases
      $ atest RunBluetoothRoboTests
      $ atest ScreenDecorWindowTests
      $ atest FrameworksServicesTests:ScreenDecorWindowTests#testFlagChange,testRemoval
      $ atest ahat-tests --host

      ### binary run ###
      $ m atest
      and run above the same test types.

      ### unittests ###
      $ tools/tradefederation/core/atest/atest_run_unittests.py
      $ atest -mbt atest_unittests

Change-Id: I56468e9e1d64d31c94cceca7e8494089ca9d3cc9
diff --git a/atest/Android.bp b/atest/Android.bp
index a53d3f9..d948d1f 100644
--- a/atest/Android.bp
+++ b/atest/Android.bp
@@ -53,6 +53,9 @@
         "*_unittest.py",
         "*/*_unittest.py",
     ],
+    libs: [
+        "py-mock",
+    ],
     defaults: ["atest_py2_default"],
 }
 
@@ -91,3 +94,19 @@
         "asuite_metrics.py",
     ],
 }
+
+python_test_host {
+    name: "atest_unittests",
+    main: "atest_run_unittests.py",
+    pkg_path: "atest",
+    srcs: [
+        "**/*.py",
+    ],
+    data: [
+        "unittest_data/**/*",
+        "unittest_data/**/.*",
+    ],
+    test_config: "atest_unittests.xml",
+    test_suites: ["general-tests"],
+    defaults: ["atest_py2_default"],
+}
diff --git a/atest/atest_run_unittests.py b/atest/atest_run_unittests.py
new file mode 100755
index 0000000..f23c59d
--- /dev/null
+++ b/atest/atest_run_unittests.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+#
+# Copyright 2018 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.
+"""Main entrypoint for all of atest's unittest."""
+
+import logging
+import os
+import sys
+import unittest
+from importlib import import_module
+
+# Setup logging to be silent so unittests can pass through TF.
+logging.disable(logging.ERROR)
+
+def get_test_modules():
+    """Returns a list of testable modules.
+
+    Finds all the test files (*_unittest.py) and get their relative
+    path (internal/lib/utils_test.py) and translate it to an import path and
+    strip the py ext (internal.lib.utils_test).
+
+    Returns:
+        List of strings (the testable module import path).
+    """
+    testable_modules = []
+    base_path = os.path.dirname(os.path.realpath(__file__))
+
+    for dirpath, _, files in os.walk(base_path):
+        for f in files:
+            if f.endswith("_unittest.py"):
+                # Now transform it into a relative import path.
+                full_file_path = os.path.join(dirpath, f)
+                rel_file_path = os.path.relpath(full_file_path, base_path)
+                rel_file_path, _ = os.path.splitext(rel_file_path)
+                rel_file_path = rel_file_path.replace(os.sep, ".")
+                testable_modules.append(rel_file_path)
+
+    return testable_modules
+
+def main(_):
+    """Main unittest entry.
+
+    Args:
+        argv: A list of system arguments. (unused)
+
+    Returns:
+        0 if success. None-zero if fails.
+    """
+    test_modules = get_test_modules()
+    for mod in test_modules:
+        import_module(mod)
+
+    loader = unittest.defaultTestLoader
+    test_suite = loader.loadTestsFromNames(test_modules)
+    runner = unittest.TextTestRunner(verbosity=2)
+    result = runner.run(test_suite)
+    sys.exit(not result.wasSuccessful())
+
+
+if __name__ == '__main__':
+    main(sys.argv[1:])
diff --git a/atest/atest_unittests.xml b/atest/atest_unittests.xml
new file mode 100644
index 0000000..2f8b3af
--- /dev/null
+++ b/atest/atest_unittests.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 atest unittests">
+    <option name="test-suite-tag" value="atest_unittests" />
+
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="atest_unittests" />
+        <option name="test-timeout" value="2m" />
+    </test>
+</configuration>
diff --git a/atest/cli_translator_unittest.py b/atest/cli_translator_unittest.py
index 3b0bf6f..69cae6c 100755
--- a/atest/cli_translator_unittest.py
+++ b/atest/cli_translator_unittest.py
@@ -76,6 +76,10 @@
         self.ctr.mod_info = mock.Mock
         self.ctr.mod_info.name_to_module_info = {}
 
+    def tearDown(self):
+        """Run after execution of every test"""
+        reload(uc)
+
     @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
     def test_get_test_infos(self, mock_getfindmethods):
         """Test _get_test_infos method."""