#!/usr/bin/env python3

"""Checks various LLVM tool symlinks behave as expected."""

__copyright__ = """
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""

import os
import sys
import time
import unittest
from bazel_tools.tools.python.runfiles import runfiles
from bazel_integration_test.py import test_base


class BazelExampleTest(test_base.TestBase):
    def setUp(self) -> None:
        test_base.TestBase.setUp(self)
        self.runfiles = runfiles.Create()
        self.install_module = self.runfiles.Rlocation(
            "carbon/toolchain/install/prefix/lib/carbon"
        )
        self.startup_flags = [
            "--ignore_all_rc_files",
            "--batch",
        ]
        self.flags = [
            f"--override_module=carbon_toolchain={self.install_module}"
        ]

    def _run_bazel(self, command: list[str]) -> str:
        """Runs bazel with retry logic for transient errors."""
        for attempt in range(5):
            exit_code, stdout, stderr = self.RunBazel(
                self.startup_flags + command + self.flags
            )

            # Several error codes are reliably permanent, break immediately.
            # `1`  -- The build failed.
            # `2`  -- Command line or environment problem.
            # `3`  -- Tests failed or timed out, we don't retry at this layer
            #         on execution timeout.
            # `4`  -- Test command but no tests found.
            # `8`  -- Explicitly interrupted build.
            #
            # Note that `36` is documented as "likely permanent", but we retry
            # it as most of our transient failures actually produce that error
            # code.
            perm_error = (1, 2, 3, 4, 8)
            if exit_code in perm_error:
                break

            for line in stderr:
                print(line, file=sys.stderr)

            if exit_code == 0:
                return stdout

            # Retry transient errors with a brief delay.
            print(f"Attempt {attempt + 1} failed with exit code {exit_code}")
            time.sleep(attempt)
        self.AssertExitCode(exit_code, 0, stderr)

    def test_compile_lib(self) -> None:
        # TODO: Can remove this in favor of always running `test_run` if we can
        # make linking a binary sufficiently efficient.
        self._run_bazel(["build", "//:example_lib"])

    @unittest.skipUnless(
        "CARBON_BAZEL_TEST_FULL" in os.environ,
        "Skipping expensive test step for minimal testing",
    )
    def test_run(self) -> None:
        stdout = self._run_bazel(["run", "//:example"])
        self.assertEqual(stdout, ["Hello World!"])


if __name__ == "__main__":
    unittest.main()
