#!/usr/bin/env vpython3
# Copyright (c) 2022 The Chromium Authors. All rights reserved.
# 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)

import subprocess2
from testing_support import fake_repos


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'])

        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()