AIDEGen: aidegen_functional_test add upload binary test use case function.

Try to run use cases of AIDEGen when uploading binary by making use of
aidegen_functional_test. Add a special group of use cases' tests to run
by using aidegen_functional_test. We need to run aidegen_functiunal_test in
smoke_test.sh which is triggered by release_checker.py when aosp/943977 is ready.
We can easily change use cases by just modifying verify_binary_upload.json.

Bug: 137343144
Test: 1. Patch aosp/1094929.
      2. m aidegen and copy aidegen-dev to prebuilts/asuite/aidegen/linux-x86
         and rename to aidegen.
      3. m aidegen_functional_test;aidegen_functional_test-dev -b
         all tests passed!

Change-Id: I5dc73cc7a8fbb29de421632eed65db23adda36e8
diff --git a/aidegen/aidegen_main.py b/aidegen/aidegen_main.py
index 94601bc..0eb4530 100644
--- a/aidegen/aidegen_main.py
+++ b/aidegen/aidegen_main.py
@@ -84,8 +84,6 @@
 _SKIP_MSG = _SKIP_BUILD_INFO_FUTURE.format(
     common_util.COLORED_INFO('aidegen [ module(s) ] -s'))
 _TIME_EXCEED_MSG = '\n{} {}\n'.format(_INFO, _SKIP_MSG)
-_LOG_FORMAT = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
-_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
 
 
 def _parse_args(args):
@@ -156,18 +154,6 @@
     return parser.parse_args(args)
 
 
-def _configure_logging(verbose):
-    """Configure the logger.
-
-    Args:
-        verbose: A boolean. If true, display DEBUG level logs.
-    """
-    log_format = _LOG_FORMAT
-    datefmt = _DATE_FORMAT
-    level = logging.DEBUG if verbose else logging.INFO
-    logging.basicConfig(level=level, format=log_format, datefmt=datefmt)
-
-
 def _get_ide_util_instance(args):
     """Get an IdeUtil class instance for launching IDE.
 
@@ -332,7 +318,7 @@
     exit_code = constant.EXIT_CODE_NORMAL
     try:
         args = _parse_args(argv)
-        _configure_logging(args.verbose)
+        common_util.configure_logging(args.verbose)
         references = [constant.ANDROID_TREE] if _is_whole_android_tree(
             args.targets, args.android_tree) else []
         aidegen_metrics.starts_asuite_metrics(references)
diff --git a/aidegen/aidegen_main_unittest.py b/aidegen/aidegen_main_unittest.py
index c0a9b1e..fc6923c 100644
--- a/aidegen/aidegen_main_unittest.py
+++ b/aidegen/aidegen_main_unittest.py
@@ -22,9 +22,9 @@
 import unittest
 from unittest import mock
 
-import aidegen.unittest_constants as uc
 from aidegen import aidegen_main
 from aidegen import constant
+from aidegen import unittest_constants
 from aidegen.lib import aidegen_metrics
 from aidegen.lib import common_util
 from aidegen.lib import eclipse_project_file_gen
@@ -59,8 +59,9 @@
         self.assertEqual(args.ide[0], 's')
         args = aidegen_main._parse_args(['-i', 'e'])
         self.assertEqual(args.ide[0], 'e')
-        args = aidegen_main._parse_args(['-p', uc.TEST_MODULE])
-        self.assertEqual(args.ide_installed_path, uc.TEST_MODULE)
+        args = aidegen_main._parse_args(['-p', unittest_constants.TEST_MODULE])
+        self.assertEqual(args.ide_installed_path,
+                         unittest_constants.TEST_MODULE)
         args = aidegen_main._parse_args(['-n'])
         self.assertEqual(args.no_launch, True)
         args = aidegen_main._parse_args(['-r'])
@@ -68,22 +69,6 @@
         args = aidegen_main._parse_args(['-s'])
         self.assertEqual(args.skip_build, True)
 
-    @mock.patch('aidegen_main.logging.basicConfig')
-    def test_configure_logging(self, mock_log_config):
-        """Test _configure_logging with different arguments."""
-        aidegen_main._configure_logging(True)
-        log_format = aidegen_main._LOG_FORMAT
-        datefmt = aidegen_main._DATE_FORMAT
-        level = aidegen_main.logging.DEBUG
-        self.assertTrue(
-            mock_log_config.called_with(
-                level=level, format=log_format, datefmt=datefmt))
-        aidegen_main._configure_logging(False)
-        level = aidegen_main.logging.INFO
-        self.assertTrue(
-            mock_log_config.called_with(
-                level=level, format=log_format, datefmt=datefmt))
-
     @mock.patch.object(ide_util.IdeUtil, 'is_ide_installed')
     def test_get_ide_util_instance(self, mock_installed):
         """Test _get_ide_util_instance with different conditions."""
diff --git a/aidegen/lib/common_util.py b/aidegen/lib/common_util.py
index 9173685..dd3de36 100644
--- a/aidegen/lib/common_util.py
+++ b/aidegen/lib/common_util.py
@@ -30,13 +30,17 @@
 
 from aidegen import constant
 from aidegen.lib import errors
+from atest import atest_utils
 from atest import constants
 from atest import module_info
-from atest.atest_utils import colorize
 
-COLORED_INFO = partial(colorize, color=constants.MAGENTA, highlight=False)
-COLORED_PASS = partial(colorize, color=constants.GREEN, highlight=False)
-COLORED_FAIL = partial(colorize, color=constants.RED, highlight=False)
+
+COLORED_INFO = partial(
+    atest_utils.colorize, color=constants.MAGENTA, highlight=False)
+COLORED_PASS = partial(
+    atest_utils.colorize, color=constants.GREEN, highlight=False)
+COLORED_FAIL = partial(
+    atest_utils.colorize, color=constants.RED, highlight=False)
 FAKE_MODULE_ERROR = '{} is a fake module.'
 OUTSIDE_ROOT_ERROR = '{} is outside android root.'
 PATH_NOT_EXISTS_ERROR = 'The path {} doesn\'t exist.'
@@ -52,6 +56,8 @@
 _REBUILD_MODULE_INFO = '%s We should rebuild module-info.json file for it.'
 _ENVSETUP_NOT_RUN = ('Please run "source build/envsetup.sh" and "lunch" before '
                      'running aidegen.')
+_LOG_FORMAT = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s'
+_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
 
 
 def time_logged(func=None, *, message='', maximum=1):
@@ -114,8 +120,8 @@
                 rel_path = paths[0]
                 abs_path = os.path.join(get_android_root_dir(), rel_path)
         # User inputs a module path or a relative path of android root folder.
-        elif (atest_module_info.get_module_names(target) or os.path.isdir(
-                os.path.join(get_android_root_dir(), target))):
+        elif (atest_module_info.get_module_names(target)
+              or os.path.isdir(os.path.join(get_android_root_dir(), target))):
             rel_path = target.strip(os.sep)
             abs_path = os.path.join(get_android_root_dir(), rel_path)
         # User inputs a relative path of current directory.
@@ -385,8 +391,7 @@
     Returns:
         module_bp_java_deps.json path.
     """
-    return os.path.join(get_soong_out_path(),
-                        constant.BLUEPRINT_JSONFILE_NAME)
+    return os.path.join(get_soong_out_path(), constant.BLUEPRINT_JSONFILE_NAME)
 
 
 def back_to_cwd(func):
@@ -398,6 +403,7 @@
     Returns:
         The wrapper function.
     """
+
     @wraps(func)
     def wrapper(*args, **kwargs):
         """A wrapper function."""
@@ -422,8 +428,8 @@
     android_out_dir_common_base = (os.path.join(
         out_dir_common_base, os.path.basename(android_root_path))
                                    if out_dir_common_base else None)
-    return (android_out_dir or android_out_dir_common_base or
-            constant.ANDROID_DEFAULT_OUT)
+    return (android_out_dir or android_out_dir_common_base
+            or constant.ANDROID_DEFAULT_OUT)
 
 
 def get_android_root_dir():
@@ -463,3 +469,15 @@
         Out directory's soong path.
     """
     return os.path.join(get_android_root_dir(), get_android_out_dir(), 'soong')
+
+
+def configure_logging(verbose):
+    """Configure the logger.
+
+    Args:
+        verbose: A boolean. If true, display DEBUG level logs.
+    """
+    log_format = _LOG_FORMAT
+    datefmt = _DATE_FORMAT
+    level = logging.DEBUG if verbose else logging.INFO
+    logging.basicConfig(level=level, format=log_format, datefmt=datefmt)
diff --git a/aidegen/lib/common_util_unittest.py b/aidegen/lib/common_util_unittest.py
index 82e54fd..01df761 100644
--- a/aidegen/lib/common_util_unittest.py
+++ b/aidegen/lib/common_util_unittest.py
@@ -16,6 +16,7 @@
 
 """Unittests for common_util."""
 
+import logging
 import os
 import unittest
 from unittest import mock
@@ -109,8 +110,8 @@
                 unittest_constants.TEST_MODULE)
             self.assertEqual(expected, str(ctx.exception))
         self.assertEqual(common_util._check_module(mod_info, '', False), False)
-        self.assertEqual(common_util._check_module(mod_info, 'nothing', False),
-                         False)
+        self.assertEqual(
+            common_util._check_module(mod_info, 'nothing', False), False)
 
     @mock.patch.object(common_util, '_check_module')
     def test_check_modules(self, mock_check):
@@ -122,8 +123,8 @@
         self.assertEqual(mock_check.call_count, 2)
         target = 'nothing'
         mock_check.return_value = False
-        self.assertEqual(common_util._check_modules(mod_info, [target], False),
-                         False)
+        self.assertEqual(
+            common_util._check_modules(mod_info, [target], False), False)
 
     @mock.patch.object(common_util, 'get_android_root_dir')
     def test_get_abs_path(self, mock_get_root):
@@ -147,6 +148,22 @@
             common_util.is_target('packages/apps/tests/test.jar',
                                   ['.so', '.a']), False)
 
+    @mock.patch.object(logging, 'basicConfig')
+    def test_configure_logging(self, mock_log_config):
+        """Test configure_logging with different arguments."""
+        common_util.configure_logging(True)
+        log_format = common_util._LOG_FORMAT
+        datefmt = common_util._DATE_FORMAT
+        level = common_util.logging.DEBUG
+        self.assertTrue(
+            mock_log_config.called_with(
+                level=level, format=log_format, datefmt=datefmt))
+        common_util.configure_logging(False)
+        level = common_util.logging.INFO
+        self.assertTrue(
+            mock_log_config.called_with(
+                level=level, format=log_format, datefmt=datefmt))
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/aidegen_functional_test/aidegen_functional_test_main.py b/aidegen_functional_test/aidegen_functional_test_main.py
index fb16dd7..6257793 100644
--- a/aidegen_functional_test/aidegen_functional_test_main.py
+++ b/aidegen_functional_test/aidegen_functional_test_main.py
@@ -22,27 +22,24 @@
 import itertools
 import json
 import os
+import subprocess
 import sys
 import xml.etree.ElementTree
 import xml.parsers.expat
 
-import aidegen.lib.errors
-
 from aidegen import aidegen_main
 from aidegen.lib import common_util
-from aidegen.lib.common_util import COLORED_PASS
-from aidegen.lib.common_util import COLORED_FAIL
-from aidegen.lib.common_util import get_blueprint_json_path
-from aidegen.lib.common_util import get_related_paths
-from aidegen.lib.common_util import time_logged
+from aidegen.lib import errors
 from atest import module_info
 
+
 _ROOT_DIR = os.path.join(common_util.get_android_root_dir(),
                          'tools/asuite/aidegen_functional_test')
 _TEST_DATA_PATH = os.path.join(_ROOT_DIR, 'test_data')
 _ANDROID_SINGLE_PROJECT_JSON = os.path.join(_TEST_DATA_PATH,
                                             'single_module.json')
 _VERIFY_COMMANDS_JSON = os.path.join(_TEST_DATA_PATH, 'verify_commands.json')
+_VERIFY_BINARY_JSON = os.path.join(_TEST_DATA_PATH, 'verify_binary_upload.json')
 _PRODUCT_DIR = '$PROJECT_DIR$'
 _ANDROID_COMMON = 'android_common'
 _LINUX_GLIBC_COMMON = 'linux_glibc_common'
@@ -68,12 +65,12 @@
         args: A list of arguments.
 
     Returns:
-        An argparse.Namespace class instance holding parsed args.
+        An argparse.Namespace object holding parsed args.
     """
     parser = argparse.ArgumentParser(
         description=__doc__,
         formatter_class=argparse.RawDescriptionHelpFormatter,
-        usage='aidegen_functional_test [-c | -v]')
+        usage='aidegen_functional_test [-c | -u | -b] -v -r')
     group = parser.add_mutually_exclusive_group()
     parser.required = False
     group.add_argument(
@@ -83,12 +80,27 @@
         dest='create_sample',
         help=('Create aidegen project files and write data to sample json file '
               'for aidegen_functional_test to compare.'))
-    group.add_argument(
+    parser.add_argument(
         '-v',
-        '--verify',
+        '--verbose',
         action='store_true',
-        dest='verify_aidegen',
+        help='Show DEBUG level logging.')
+    parser.add_argument(
+        '-r',
+        '--remove_bp_json',
+        action='store_true',
+        help='Remove module_bp_java_deps.json for each use case test.')
+    group.add_argument(
+        '-u',
+        action='store_true',
+        dest='use_cases_verified',
         help='Verify various use cases of executing aidegen.')
+    group.add_argument(
+        '-b',
+        action='store_true',
+        dest='binary_upload_verified',
+        help=('Verify aidegen\'s use cases by executing different aidegen '
+              'commands.'))
     return parser.parse_args(args)
 
 
@@ -132,7 +144,7 @@
     data = {}
     for target, filelist in _TEST_IML_DICT.items():
         aidegen_main.main([target, '-n'])
-        _, abs_path = get_related_paths(atest_module_info, target)
+        _, abs_path = common_util.get_related_paths(atest_module_info, target)
         for filename in filelist:
             real_iml_file = os.path.join(abs_path, filename)
             item_name = os.path.basename(real_iml_file)
@@ -163,14 +175,15 @@
             if set(s_items) != set(r_items):
                 diff_iter = _compare_content(name, item, s_items, r_items)
                 if diff_iter:
-                    print('\n%s\n%s' % (COLORED_FAIL('Test error...'),
-                                        _TEST_ERROR % (name, item)))
+                    print(
+                        '\n%s\n%s' % (common_util.COLORED_FAIL('Test error...'),
+                                      _TEST_ERROR % (name, item)))
                     print('%s %s contents are different:' % (name, item))
                     for diff in diff_iter:
                         print(diff)
                     test_successful = False
     if test_successful:
-        print(COLORED_PASS(_ALL_PASS))
+        print(common_util.COLORED_PASS(_ALL_PASS))
 
 
 def _compare_content(module_name, item_type, s_items, r_items):
@@ -244,32 +257,81 @@
 
 # pylint: disable=broad-except
 # pylint: disable=eval-used
-@time_logged
-def _verify_aidegen():
-    """Verify various use cases of executing aidegen."""
-    bp_json_path = get_blueprint_json_path()
-    with open(_VERIFY_COMMANDS_JSON, 'r') as jsfile:
-        data = json.load(jsfile)
+@common_util.back_to_cwd
+@common_util.time_logged
+def _verify_aidegen(verified_file_path, forced_remove_bp_json):
+    """Verify various use cases of executing aidegen.
+
+    There are two types of running commands:
+    1. Use 'eval' to run the commands for present codes in aidegen_main.py,
+       such as:
+           aidegen_main.main(['tradefed', '-n', '-v'])
+    2. Use 'subprocess.check_call' to run the commands for the binary codes of
+       aidegen such as:
+       aidegen tradefed -n -v
+
+    Remove module_bp_java_deps.json in the beginning of running use cases. If
+    users need to remove module_bp_java_deps.json between each use case they
+    can set forced_remove_bp_json true.
+
+    args:
+        verified_file_path: The json file path to be verified.
+        forced_remove_bp_json: Remove module_bp_java_deps.json for each use case
+                               test.
+
+    raises:
+        There are two type of exceptions:
+        1. aidegen.lib.errors for projects' or modules' issues such as,
+           ProjectPathNotExistError.
+        2. Any exceptions other than aidegen.lib.errors such as,
+           subprocess.CalledProcessError.
+    """
+    os.chdir(common_util.get_android_root_dir())
+    bp_json_path = common_util.get_blueprint_json_path()
+    use_eval = (verified_file_path == _VERIFY_COMMANDS_JSON)
+    try:
+        with open(verified_file_path, 'r') as jsfile:
+            data = json.load(jsfile)
+    except IOError as err:
+        raise errors.JsonFileNotExistError(
+            '%s does not exist, error: %s.' % (verified_file_path, err))
+
+    try:
+        subprocess.check_call(
+            ['build/soong/soong_ui.bash --make-mode clean', '-j'],
+            shell=True)
+    except subprocess.CalledProcessError:
+        print('"make clean" command failed.')
+        raise
+
     for use_case in data:
-        if os.path.exists(bp_json_path):
+        print('Use case "{}" is running.'.format(use_case))
+        if forced_remove_bp_json and os.path.exists(bp_json_path):
             os.remove(bp_json_path)
         for cmd in data[use_case]:
+            print('Command "{}" is running.'.format(cmd))
             try:
-                eval(cmd)
-            except (aidegen.lib.errors.ProjectOutsideAndroidRootError,
-                    aidegen.lib.errors.ProjectPathNotExistError,
-                    aidegen.lib.errors.NoModuleDefinedInModuleInfoError,
-                    aidegen.lib.errors.IDENotExistError) as err:
-                print('{} command has raise error: {}.'.format(use_case, err))
+                if use_eval:
+                    eval(cmd)
+                else:
+                    subprocess.check_call(cmd, shell=True)
+            except (errors.ProjectOutsideAndroidRootError,
+                    errors.ProjectPathNotExistError,
+                    errors.NoModuleDefinedInModuleInfoError,
+                    errors.IDENotExistError) as err:
+                print('"{}" raises error: {}.'.format(use_case, err))
+                raise
             except BaseException:
                 exc_type, _, _ = sys.exc_info()
-                print('{}.{} command {}.'.format(
-                    use_case, cmd, COLORED_FAIL('executes failed')))
+                print('"{}.{}" command {}.'.format(
+                    use_case, cmd, common_util.COLORED_FAIL('executes failed')))
                 raise BaseException(
-                    'Unexpected command {} exception {}.'.format(
+                    'Unexpected command "{}" exception: {}.'.format(
                         use_case, exc_type))
-        print('{} command {}!'.format(use_case, COLORED_PASS('test passed')))
-    print(COLORED_PASS(_ALL_PASS))
+        print('"{}" command {}!'.format(
+            use_case, common_util.COLORED_PASS('test passed')))
+    print(common_util.COLORED_PASS(_ALL_PASS))
+
 
 def main(argv):
     """Main entry.
@@ -280,10 +342,13 @@
         argv: A list of system arguments.
     """
     args = _parse_args(argv)
+    common_util.configure_logging(args.verbose)
     if args.create_sample:
         _create_sample_json_file()
-    elif args.verify_aidegen:
-        _verify_aidegen()
+    elif args.use_cases_verified:
+        _verify_aidegen(_VERIFY_COMMANDS_JSON, args.remove_bp_json)
+    elif args.binary_upload_verified:
+        _verify_aidegen(_VERIFY_BINARY_JSON, args.remove_bp_json)
     else:
         test_some_sample_iml()
 
diff --git a/aidegen_functional_test/test_data/verify_binary_upload.json b/aidegen_functional_test/test_data/verify_binary_upload.json
new file mode 100644
index 0000000..b3fc26d
--- /dev/null
+++ b/aidegen_functional_test/test_data/verify_binary_upload.json
@@ -0,0 +1,14 @@
+{
+    "test whole android tree": ["aidegen -n"],
+    "test whole android tree with -a": ["aidegen -a -n -s"],
+    "test whole android tree with frameworks/base -a": ["aidegen frameworks/base -a -n -s"],
+    "test whole android tree in frameworks/base with -a": [
+        "cd frameworks/base",
+        "aidegen -a -n -s",
+        "cd ../.."
+    ],
+    "test Settings framework": ["aidegen Settings framework -n -s"],
+    "test framework launch Android Studio": ["aidegen framework -i s -n -s"],
+    "test framework launch Eclipse": ["aidegen framework -i e -n -s"],
+    "test help": ["aidegen -h"]
+}