Add acloud test build target.

Bug: 110430047
Test: m acloud_test && acloud_test
./run_tests.sh
Change-Id: I1d18db2a4363271a1422e3516ee603547b8942e0
diff --git a/Android.bp b/Android.bp
index 6efe74a..13283de 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,12 +49,33 @@
     ],
 }
 
-python_library_host{
+python_test_host {
+    name: "acloud_test",
+    main: "acloud_test.py",
+    defaults: ["acloud_default"],
+    srcs: [
+        "acloud_test.py",
+        "public/*_test.py",
+        "public/actions/*_test.py",
+        "internal/lib/*_test.py",
+    ],
+    libs: [
+        "acloud_public",
+        "acloud_internal",
+        "acloud_proto",
+        "py-apitools",
+        "py-dateutil",
+        "py-google-api-python-client",
+        "py-oauth2client",
+    ],
+}
+
+python_library_host {
     name: "acloud_public",
     defaults: ["acloud_default"],
     srcs: [
-         "public/*.py",
-         "public/actions/*.py",
+        "public/*.py",
+        "public/actions/*.py",
     ],
     exclude_srcs: [
         "public/*_test.py",
@@ -63,20 +84,19 @@
     ]
 }
 
-python_library_host{
+python_library_host {
     name: "acloud_internal",
     defaults: ["acloud_default"],
     srcs: [
-         "internal/*.py",
-         "internal/lib/*.py",
+        "internal/*.py",
+        "internal/lib/*.py",
     ],
     exclude_srcs: [
-        "internal/*_test.py",
         "internal/lib/*_test.py",
     ]
 }
 
-python_library_host{
+python_library_host {
     name: "acloud_proto",
     defaults: ["acloud_default"],
     srcs: [
diff --git a/acloud_test.py b/acloud_test.py
new file mode 100644
index 0000000..fb8bf05
--- /dev/null
+++ b/acloud_test.py
@@ -0,0 +1,72 @@
+#!/usr/bin/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 entry point for all of acloud's unittest."""
+
+from importlib import import_module
+import os
+import sys
+import unittest
+
+
+def GetTestModules():
+    """Return list of testable modules.
+
+    We need to find all the test files (*_test.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__))
+
+    # Get list of all python files that end in _test.py (except for __file__).
+    for dirpath, _, files in os.walk(base_path):
+        for f in files:
+            if f.endswith("_test.py") and f != os.path.basename(__file__):
+                # 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 = GetTestModules()
+    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/internal/lib/gcompute_client_test.py b/internal/lib/gcompute_client_test.py
index 2ce4c90..2f8aa03 100644
--- a/internal/lib/gcompute_client_test.py
+++ b/internal/lib/gcompute_client_test.py
@@ -23,8 +23,8 @@
 import mock
 
 # pylint: disable=import-error
-import apiclient.http
 from absl.testing import parameterized
+import apiclient.http
 
 from acloud.internal.lib import driver_test_lib
 from acloud.internal.lib import gcompute_client
@@ -937,7 +937,6 @@
 
         self.Patch(os.path, "exists", return_value=True)
         m = mock.mock_open(read_data=sshkey)
-        self.Patch(__builtins__, "open", m, create=True)
         self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
         self.Patch(
             gcompute_client.ComputeClient, "GetProject", return_value=project)
@@ -946,9 +945,10 @@
             return_value=resource_mock)
         resource_mock.setCommonInstanceMetadata = mock.MagicMock()
 
-        self.compute_client.AddSshRsa(fake_user, "/path/to/test_rsa.pub")
-        resource_mock.setCommonInstanceMetadata.assert_called_with(
-            project=PROJECT, body=expected)
+        with mock.patch("__builtin__.open", m):
+            self.compute_client.AddSshRsa(fake_user, "/path/to/test_rsa.pub")
+            resource_mock.setCommonInstanceMetadata.assert_called_with(
+                project=PROJECT, body=expected)
 
     def testAddSshRsaInvalidKey(self):
         """Test AddSshRsa.."""
@@ -968,13 +968,13 @@
         }
         self.Patch(os.path, "exists", return_value=True)
         m = mock.mock_open(read_data=sshkey)
-        self.Patch(__builtins__, "open", m, create=True)
         self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
         self.Patch(
             gcompute_client.ComputeClient, "GetProject", return_value=project)
-        self.assertRaisesRegexp(errors.DriverError, "rsa key is invalid:*",
-                                self.compute_client.AddSshRsa, fake_user,
-                                "/path/to/test_rsa.pub")
+        with mock.patch("__builtin__.open", m):
+            self.assertRaisesRegexp(errors.DriverError, "rsa key is invalid:*",
+                                    self.compute_client.AddSshRsa, fake_user,
+                                    "/path/to/test_rsa.pub")
 
     @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
     def testDeleteDisks(self, mock_wait):
diff --git a/internal/lib/utils_test.py b/internal/lib/utils_test.py
index 02cf8aa..aad23c1 100644
--- a/internal/lib/utils_test.py
+++ b/internal/lib/utils_test.py
@@ -153,10 +153,10 @@
         self.Patch(os.path, "exists", side_effect=[False, True, True])
         self.Patch(os, "makedirs", return_value=True)
         mock_open = mock.mock_open(read_data=public_key)
-        self.Patch(__builtins__, "open", mock_open, create=True)
         self.Patch(subprocess, "check_output")
         self.Patch(os, "rename")
-        utils.CreateSshKeyPairIfNotExist(private_key, public_key)
+        with mock.patch("__builtin__.open", mock_open):
+            utils.CreateSshKeyPairIfNotExist(private_key, public_key)
         self.assertEqual(subprocess.check_output.call_count, 1)  #pylint: disable=no-member
         subprocess.check_output.assert_called_with(  #pylint: disable=no-member
             utils.SSH_KEYGEN_PUB_CMD +["-f", private_key])
diff --git a/run_tests.sh b/run_tests.sh
index c937702..fbe9ee2 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -28,7 +28,7 @@
     PYTHONPATH=$(get_python_path) python -m coverage erase
 
     # Runs all unit tests under tools/acloud.
-    for t in $(find $ACLOUD_DIR -type f -name "*_test.py");
+    for t in $(find $ACLOUD_DIR -type f -name "*_test.py" ! -name "acloud_test.py");
     do
         if ! PYTHONPATH=$(get_python_path):$PYTHONPATH $run_cmd $t; then
             rc=1