| #!/usr/bin/env python3 | |
| # Copyright (C) 2024 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 pathlib | |
| import subprocess | |
| import sys | |
| SOURCE_ENVSETUP="source build/make/envsetup.sh && " | |
| def update_display(): | |
| sys.stderr.write("passed\n") | |
| def go_to_root(): | |
| while True: | |
| if os.path.exists("build/make/envsetup.sh"): | |
| return | |
| if os.getcwd() == "/": | |
| sys.stderr.write("Can't find root of the source tree\n"); | |
| print("\nFAILED") | |
| sys.exit(1) | |
| os.chdir("..") | |
| def is_test(name, thing): | |
| if not callable(thing): | |
| return False | |
| if name == "test": | |
| return False | |
| return name.startswith("test") | |
| def test(shell, command, expected_return, expected_stdout, expected_stderr, expected_env): | |
| command += "; _rc=$?" | |
| for env in expected_env.keys(): | |
| command += f"; echo ENV: {env}=\\\"${env}\\\"" | |
| command += "; exit $_rc" | |
| cmd = [shell, "-c", command] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| status = True | |
| if result.returncode != expected_return: | |
| print() | |
| print(f"Expected return code: {expected_return}") | |
| print(f"Actual return code: {result.returncode}") | |
| status = False | |
| printed_stdout = False | |
| if expected_stdout and expected_stdout not in result.stdout: | |
| print() | |
| print(f"Expected stdout to contain:\n{expected_stdout}") | |
| print(f"\nActual stdout:\n{result.stdout}") | |
| printed_stdout = True | |
| status = False | |
| if expected_stderr and expected_stderr not in result.stderr: | |
| print() | |
| print(f"Expected stderr to contain:\n{expected_stderr}") | |
| print(f"\nActual stderr:\n{result.stderr}") | |
| status = False | |
| env_failure = False | |
| for k, v in expected_env.items(): | |
| if f"{k}=\"{v}\"" not in result.stdout: | |
| print() | |
| print(f"Expected environment variable {k} to be: {v} --- {k}=\"{v}\"") | |
| env_failure = True | |
| status = False | |
| if env_failure and not printed_stdout: | |
| print() | |
| print("See stdout:") | |
| print(result.stdout) | |
| if not status: | |
| print() | |
| print("Command to reproduce:") | |
| print(command) | |
| print() | |
| return status | |
| NO_LUNCH = { | |
| "TARGET_PRODUCT": "", | |
| "TARGET_RELEASE": "", | |
| "TARGET_BUILD_VARIANT": "", | |
| } | |
| def test_invalid_lunch_target(shell): | |
| return test(shell, SOURCE_ENVSETUP + "lunch invalid-trunk_staging-eng", | |
| expected_return=1, expected_stdout=None, | |
| expected_stderr="Cannot locate config makefile for product", | |
| expected_env=NO_LUNCH) | |
| def test_aosp_arm(shell): | |
| return test(shell, SOURCE_ENVSETUP + "lunch aosp_arm-trunk_staging-eng", | |
| expected_return=0, expected_stdout=None, expected_stderr=None, | |
| expected_env={ | |
| "TARGET_PRODUCT": "aosp_arm", | |
| "TARGET_RELEASE": "trunk_staging", | |
| "TARGET_BUILD_VARIANT": "eng", | |
| }) | |
| def test_lunch2_empty(shell): | |
| return test(shell, SOURCE_ENVSETUP + "lunch2", | |
| expected_return=1, expected_stdout=None, | |
| expected_stderr="No target specified. See lunch --help", | |
| expected_env=NO_LUNCH) | |
| def test_lunch2_four_params(shell): | |
| return test(shell, SOURCE_ENVSETUP + "lunch2 a b c d", | |
| expected_return=1, expected_stdout=None, | |
| expected_stderr="Too many parameters given. See lunch --help", | |
| expected_env=NO_LUNCH) | |
| def test_lunch2_aosp_arm(shell): | |
| return test(shell, SOURCE_ENVSETUP + "lunch2 aosp_arm", | |
| expected_return=0, expected_stdout="=========", expected_stderr=None, | |
| expected_env={ | |
| "TARGET_PRODUCT": "aosp_arm", | |
| "TARGET_RELEASE": "trunk_staging", | |
| "TARGET_BUILD_VARIANT": "eng", | |
| }) | |
| def test_lunch2_aosp_arm_trunk_staging(shell): | |
| # Somewhat unfortunate because trunk_staging is the only config in | |
| # aosp so we can't really test that this isn't just getting the default | |
| return test(shell, SOURCE_ENVSETUP + "lunch2 aosp_arm trunk_staging", | |
| expected_return=0, expected_stdout="=========", expected_stderr=None, | |
| expected_env={ | |
| "TARGET_PRODUCT": "aosp_arm", | |
| "TARGET_RELEASE": "trunk_staging", | |
| "TARGET_BUILD_VARIANT": "eng", | |
| }) | |
| def test_lunch2_aosp_arm_trunk_staging_userdebug(shell): | |
| return test(shell, SOURCE_ENVSETUP + "lunch2 aosp_arm trunk_staging userdebug", | |
| expected_return=0, expected_stdout="=========", expected_stderr=None, | |
| expected_env={ | |
| "TARGET_PRODUCT": "aosp_arm", | |
| "TARGET_RELEASE": "trunk_staging", | |
| "TARGET_BUILD_VARIANT": "userdebug", | |
| }) | |
| def test_list_products(shell): | |
| return test(shell, "build/soong/bin/list_products", | |
| expected_return=0, expected_stdout="aosp_arm", expected_stderr=None, | |
| expected_env=NO_LUNCH) | |
| def test_list_releases_param(shell): | |
| return test(shell, "build/soong/bin/list_releases aosp_arm", | |
| expected_return=0, expected_stdout="trunk_staging", expected_stderr=None, | |
| expected_env=NO_LUNCH) | |
| def test_list_releases_env(shell): | |
| return test(shell, "TARGET_PRODUCT=aosp_arm build/soong/bin/list_releases", | |
| expected_return=0, expected_stdout="trunk_staging", expected_stderr=None, | |
| expected_env=NO_LUNCH) | |
| def test_list_releases_no_product(shell): | |
| return test(shell, "build/soong/bin/list_releases", | |
| expected_return=1, expected_stdout=None, expected_stderr=None, | |
| expected_env=NO_LUNCH) | |
| def test_list_variants(shell): | |
| return test(shell, "build/soong/bin/list_variants", | |
| expected_return=0, expected_stdout="userdebug", expected_stderr=None, | |
| expected_env=NO_LUNCH) | |
| def test_get_build_var_in_path(shell): | |
| return test(shell, SOURCE_ENVSETUP + "which get_build_var ", | |
| expected_return=0, expected_stdout="soong/bin", expected_stderr=None, | |
| expected_env=NO_LUNCH) | |
| TESTS=sorted([(name, thing) for name, thing in locals().items() if is_test(name, thing)]) | |
| def main(): | |
| if any([x.endswith("/soong/bin") for x in os.getenv("PATH").split(":")]): | |
| sys.stderr.write("run_envsetup_tests must be run in a shell that has not sourced" | |
| + " envsetup.sh\n\nFAILED\n") | |
| return 1 | |
| go_to_root() | |
| tests = TESTS | |
| if len(sys.argv) > 1: | |
| tests = [(name, func) for name, func in tests if name in sys.argv] | |
| shells = ["/usr/bin/bash", "/usr/bin/zsh"] | |
| total_count = len(tests) * len(shells) | |
| index = 1 | |
| failed_tests = 0 | |
| for name, func in tests: | |
| for shell in shells: | |
| sys.stdout.write(f"\33[2K\r{index} of {total_count}: {name} in {shell}") | |
| passed = func(shell) | |
| if not passed: | |
| failed_tests += 1 | |
| index += 1 | |
| if failed_tests > 0: | |
| print(f"\n\nFAILED: {failed_tests} of {total_count}") | |
| return 1 | |
| else: | |
| print("\n\nSUCCESS") | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) |