#!/usr/bin/env vpython3
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Smoke tests for gclient.py and the no-sync experiment

Shell out 'gclient' and run git tests.
"""

import json
import logging
import os
import sys
import unittest

import gclient_smoketest_base
import gclient

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT_DIR)

from testing_support import fake_repos  # noqa: E402


def write(filename, content):
    """Writes the content of a file and create the directories as needed."""
    filename = os.path.abspath(filename)
    dirname = os.path.dirname(filename)
    if not os.path.isdir(dirname):
        os.makedirs(dirname)
    with open(filename, "w") as f:
        f.write(content)


class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
    """Smoke tests for the no-sync experiment."""

    FAKE_REPOS_CLASS = fake_repos.FakeRepoNoSyncDEPS

    def setUp(self):
        super(GClientSmokeGIT, self).setUp()
        self.env["PATH"] = (
            os.path.join(ROOT_DIR, "testing_support")
            + os.pathsep
            + self.env["PATH"]
        )
        self.enabled = self.FAKE_REPOS.set_up_git()
        if not self.enabled:
            self.skipTest("git fake repos not available")

    def testNoSync_SkipSyncNoDEPSChange(self):
        """No DEPS changes will skip sync"""
        config_template = "".join(
            [
                "solutions = [{"
                '  "name"        : "src",'
                '  "url"         : %(git_base)r + "repo_1",'
                '  "deps_file"   : "DEPS",'
                '  "managed"     : True,'
                '  "custom_vars" : %(custom_vars)s,'
                "}]"
            ]
        )
        self.gclient(
            [
                "config",
                "--spec",
                config_template
                % {"git_base": self.git_base, "custom_vars": {"mac": True}},
            ]
        )

        output_json = os.path.join(self.root_dir, "output.json")

        revision_1 = self.FAKE_REPOS.git_hashes["repo_1"][1][0]  # DEPS 1
        revision_2 = self.FAKE_REPOS.git_hashes["repo_1"][2][0]  # DEPS 1
        patch_ref = self.FAKE_REPOS.git_hashes["repo_1"][3][0]  # DEPS 2

        # Previous run did a sync at revision_1
        write(
            os.path.join(self.root_dir, gclient.PREVIOUS_SYNC_COMMITS_FILE),
            json.dumps({"src": revision_1}),
        )
        write(
            os.path.join(self.root_dir, gclient.PREVIOUS_CUSTOM_VARS_FILE),
            json.dumps({"src": {"mac": True}}),
        )

        # We checkout src at revision_2 which has a different DEPS
        # but that should not matter because patch_ref and revision_1
        # have the same DEPS
        self.gclient(
            [
                "sync",
                "--output-json",
                output_json,
                "--revision",
                "src@%s" % revision_2,
                "--patch-ref",
                "%srepo_1@refs/heads/main:%s" % (self.git_base, patch_ref),
                "--experiment",
                "no-sync",
            ]
        )

        with open(output_json) as f:
            output_json = json.load(f)
        expected = {
            "solutions": {
                "src/": {
                    "revision": revision_2,
                    "scm": "git",
                    "url": "%srepo_1" % self.git_base,
                    "was_processed": True,
                    "was_synced": False,
                },
            },
        }
        self.assertEqual(expected, output_json)

    def testNoSync_NoSyncNotEnablted(self):
        """No DEPS changes will skip sync"""
        config_template = "".join(
            [
                "solutions = [{"
                '  "name"        : "src",'
                '  "url"         : %(git_base)r + "repo_1",'
                '  "deps_file"   : "DEPS",'
                '  "managed"     : True,'
                '  "custom_vars" : %(custom_vars)s,'
                "}]"
            ]
        )
        self.gclient(
            [
                "config",
                "--spec",
                config_template
                % {"git_base": self.git_base, "custom_vars": {"mac": True}},
            ]
        )

        output_json = os.path.join(self.root_dir, "output.json")

        revision_1 = self.FAKE_REPOS.git_hashes["repo_1"][1][0]  # DEPS 1
        revision_2 = self.FAKE_REPOS.git_hashes["repo_1"][2][0]  # DEPS 1
        patch_ref = self.FAKE_REPOS.git_hashes["repo_1"][3][0]  # DEPS 2

        # Previous run did a sync at revision_1
        write(
            os.path.join(self.root_dir, gclient.PREVIOUS_SYNC_COMMITS_FILE),
            json.dumps({"src": revision_1}),
        )
        write(
            os.path.join(self.root_dir, gclient.PREVIOUS_CUSTOM_VARS_FILE),
            json.dumps({"src": {"mac": True}}),
        )

        self.gclient(
            [
                "sync",
                "--output-json",
                output_json,
                "--revision",
                "src@%s" % revision_2,
                "--patch-ref",
                "%srepo_1@refs/heads/main:%s" % (self.git_base, patch_ref),
            ]
        )

        with open(output_json) as f:
            output_json = json.load(f)
        repo2_rev = self.FAKE_REPOS.git_hashes["repo_2"][1][0]
        expected = {
            "solutions": {
                "src/": {
                    "revision": revision_2,
                    "scm": "git",
                    "url": "%srepo_1" % self.git_base,
                    "was_processed": True,
                    "was_synced": True,
                },
                "src/repo2/": {
                    "revision": repo2_rev,
                    "scm": "git",
                    "url": "%srepo_2@%s" % (self.git_base, repo2_rev[:7]),
                    "was_processed": True,
                    "was_synced": True,
                },
            },
        }
        self.assertEqual(expected, output_json)

    def testNoSync_CustomVarsDiff(self):
        """We do not skip syncs if there are different custom_vars"""
        config_template = "".join(
            [
                "solutions = [{"
                '  "name"        : "src",'
                '  "url"         : %(git_base)r + "repo_1",'
                '  "deps_file"   : "DEPS",'
                '  "managed"     : True,'
                '  "custom_vars" : %(custom_vars)s,'
                "}]"
            ]
        )
        self.gclient(
            [
                "config",
                "--spec",
                config_template
                % {"git_base": self.git_base, "custom_vars": {"mac": True}},
            ]
        )

        output_json = os.path.join(self.root_dir, "output.json")

        revision_1 = self.FAKE_REPOS.git_hashes["repo_1"][1][0]  # DEPS 1
        revision_2 = self.FAKE_REPOS.git_hashes["repo_1"][2][0]  # DEPS 2
        patch_ref = self.FAKE_REPOS.git_hashes["repo_1"][3][0]  # DEPS 1

        # Previous run did a sync at revision_1
        write(
            os.path.join(self.root_dir, gclient.PREVIOUS_SYNC_COMMITS_FILE),
            json.dumps({"src": revision_1}),
        )
        # No PREVIOUS_CUSTOM_VARS

        # We checkout src at revision_2 which has a different DEPS
        # but that should not matter because patch_ref and revision_1
        # have the same DEPS
        self.gclient(
            [
                "sync",
                "--output-json",
                output_json,
                "--revision",
                "src@%s" % revision_2,
                "--patch-ref",
                "%srepo_1@refs/heads/main:%s" % (self.git_base, patch_ref),
                "--experiment",
                "no-sync",
            ]
        )

        with open(output_json) as f:
            output_json = json.load(f)
        repo2_rev = self.FAKE_REPOS.git_hashes["repo_2"][1][0]
        expected = {
            "solutions": {
                "src/": {
                    "revision": revision_2,
                    "scm": "git",
                    "url": "%srepo_1" % self.git_base,
                    "was_processed": True,
                    "was_synced": True,
                },
                "src/repo2/": {
                    "revision": repo2_rev,
                    "scm": "git",
                    "url": "%srepo_2@%s" % (self.git_base, repo2_rev[:7]),
                    "was_processed": True,
                    "was_synced": True,
                },
            },
        }
        self.assertEqual(expected, output_json)

    def testNoSync_DEPSDiff(self):
        """We do not skip syncs if there are DEPS changes."""
        self.gclient(
            ["config", self.git_base + "repo_1", "--name", "src", "--managed"]
        )

        output_json = os.path.join(self.root_dir, "output.json")

        revision_1 = self.FAKE_REPOS.git_hashes["repo_1"][1][0]  # DEPS 1
        revision_2 = self.FAKE_REPOS.git_hashes["repo_1"][2][0]  # DEPS 2
        patch_ref = self.FAKE_REPOS.git_hashes["repo_1"][3][0]  # DEPS 1

        # Previous run did a sync at revision_1
        write(
            os.path.join(self.root_dir, gclient.PREVIOUS_SYNC_COMMITS_FILE),
            json.dumps({"src": revision_2}),
        )

        # We checkout src at revision_1 which has the same DEPS
        # but that should not matter because patch_ref and revision_2
        # have different DEPS
        self.gclient(
            [
                "sync",
                "--output-json",
                output_json,
                "--revision",
                "src@%s" % revision_1,
                "--patch-ref",
                "%srepo_1@refs/heads/main:%s" % (self.git_base, patch_ref),
                "--experiment",
                "no-sync",
            ]
        )

        with open(output_json) as f:
            output_json = json.load(f)
        repo2_rev = self.FAKE_REPOS.git_hashes["repo_2"][1][0]
        expected = {
            "solutions": {
                "src/": {
                    "revision": revision_1,
                    "scm": "git",
                    "url": "%srepo_1" % self.git_base,
                    "was_processed": True,
                    "was_synced": True,
                },
                "src/repo2/": {
                    "revision": repo2_rev,
                    "scm": "git",
                    "url": "%srepo_2@%s" % (self.git_base, repo2_rev[:7]),
                    "was_processed": True,
                    "was_synced": True,
                },
            },
        }
        self.assertEqual(expected, output_json)


if __name__ == "__main__":
    if "-v" in sys.argv:
        logging.basicConfig(level=logging.DEBUG)
    unittest.main()
