class VCSProjectTests(TestCase):
    def setUp(self):
        # Force the checkout path to point to a test directory to make
        # resource file loading pass during tests.
        checkout_path_patch = patch.object(
            Project,
            'checkout_path',
            new_callable=PropertyMock,
            return_value=TEST_CHECKOUT_PATH
        )
        checkout_path_patch.start()
        self.addCleanup(checkout_path_patch.stop)

        self.project = ProjectFactory.create()
        self.vcs_project = VCSProject(self.project)

    def test_relative_resource_paths(self):
        self.vcs_project.source_directory_path = Mock(return_value='/root/')
        self.vcs_project.resources_for_path = Mock(return_value=[
            '/root/foo.po',
            '/root/meh/bar.po'
        ])

        assert_equal(
            list(self.vcs_project.relative_resource_paths()),
            ['foo.po', 'meh/bar.po']
        )

    def test_relative_resource_paths_pot(self):
        """
        If a resource ends in .pot, replace the extension with .po since
        relative paths are used within non-source locales that do not
        have .pot files.
        """
        self.vcs_project.source_directory_path = Mock(return_value='/root/')
        self.vcs_project.resources_for_path = Mock(return_value=[
            '/root/foo.pot',
            '/root/meh/bar.pot'
        ])

        assert_equal(
            list(self.vcs_project.relative_resource_paths()),
            ['foo.po', 'meh/bar.po']
        )

    def test_source_directory_path_no_resource(self):
        """
        When searching for source directories, do not match directories that
        do not contain resource files.
        """
        assert_equal(
            self.vcs_project.source_directory_path(),
            os.path.join(TEST_CHECKOUT_PATH, 'real_resources', 'templates')
        )
    def setUp(self):
        # Force the checkout path to point to a test directory to make
        # resource file loading pass during tests.
        checkout_path_patch = patch.object(
            Project,
            'checkout_path',
            new_callable=PropertyMock,
            return_value=TEST_CHECKOUT_PATH
        )
        checkout_path_patch.start()
        self.addCleanup(checkout_path_patch.stop)

        self.project = ProjectFactory.create()
        self.vcs_project = VCSProject(self.project)
    def handle_project(self, db_project):
        # Pull changes from VCS and update what we know about the files.
        if not self.no_pull:
            update_from_repository(db_project)
        vcs_project = VCSProject(db_project)
        self.update_resources(db_project, vcs_project)

        # Collect all entities across VCS and the database and get their
        # keys so we can match up matching entities.
        vcs_entities = self.get_vcs_entities(vcs_project)
        db_entities = self.get_db_entities(db_project)
        entity_keys = set().union(db_entities.keys(), vcs_entities.keys())

        changeset = ChangeSet(db_project, vcs_project)
        for key in entity_keys:
            db_entity = db_entities.get(key, None)
            vcs_entity = vcs_entities.get(key, None)
            self.handle_entity(changeset, db_project, key, db_entity,
                               vcs_entity)

        # Apply the changeset to the files, commit them, and update stats
        # entries in the DB.
        changeset.execute()
        if not self.no_commit:
            self.commit_changes(db_project, vcs_project, changeset)
        self.update_stats(db_project, vcs_project, changeset)

        # Clear out the list of changed locales for entity in this
        # project now that we've finished syncing.
        (ChangedEntityLocale.objects.filter(
            entity__resource__project=db_project).delete())

        self.log(u'Synced project {0}', db_project.slug)
    def setUp(self):
        timezone_patch = patch.object(sync_projects, 'timezone')
        self.mock_timezone = timezone_patch.start()
        self.addCleanup(timezone_patch.stop)
        self.mock_timezone.now.return_value = aware_datetime(1970, 1, 1)

        self.translated_locale = LocaleFactory.create(code='translated-locale')
        self.inactive_locale = LocaleFactory.create(code='inactive-locale')

        self.db_project = ProjectFactory.create(
            name='db-project',
            locales=[self.translated_locale],
            repository_type='git',
            repository_url='https://example.com/git'
        )
        self.main_db_resource = ResourceFactory.create(
            project=self.db_project,
            path='main.lang',
            format='lang'
        )
        self.other_db_resource = ResourceFactory.create(
            project=self.db_project,
            path='other.lang',
            format='lang'
        )
        self.missing_db_resource = ResourceFactory.create(
            project=self.db_project,
            path='missing.lang',
            format='lang'
        )

        # Load paths from the fake locale directory.
        checkout_path_patch = patch.object(
            Project,
            'checkout_path',
            new_callable=PropertyMock,
            return_value=FAKE_CHECKOUT_PATH
        )
        checkout_path_patch.start()
        self.addCleanup(checkout_path_patch.stop)

        self.vcs_project = VCSProject(self.db_project)
        self.main_vcs_resource = self.vcs_project.resources[self.main_db_resource.path]
        self.other_vcs_resource = self.vcs_project.resources[self.other_db_resource.path]
        self.missing_vcs_resource = self.vcs_project.resources[self.missing_db_resource.path]
        self.main_vcs_entity = self.main_vcs_resource.entities['Source String']
        self.main_vcs_translation = self.main_vcs_entity.translations['translated-locale']

        # Mock VCSResource.save() for each resource to avoid altering
        # the filesystem.
        for resource in self.vcs_project.resources.values():
            save_patch = patch.object(resource, 'save')
            save_patch.start()
            self.addCleanup(save_patch.stop)

        self.changeset = sync_projects.ChangeSet(self.db_project, self.vcs_project)
class VCSProjectTests(TestCase):
    def setUp(self):
        # Force the checkout path to point to a test directory to make
        # resource file loading pass during tests.
        checkout_path_patch = patch.object(
            Project,
            'checkout_path',
            new_callable=PropertyMock,
            return_value=os.path.join(TEST_CHECKOUT_PATH, 'no_resources_test')
        )
        self.mock_checkout_path = checkout_path_patch.start()
        self.addCleanup(checkout_path_patch.stop)

        self.project = ProjectFactory.create()
        self.vcs_project = VCSProject(self.project)

    def test_relative_resource_paths(self):
        self.vcs_project.source_directory_path = Mock(return_value='/root/')
        self.vcs_project.resources_for_path = Mock(return_value=[
            '/root/foo.po',
            '/root/meh/bar.po'
        ])

        assert_equal(
            list(self.vcs_project.relative_resource_paths()),
            ['foo.po', 'meh/bar.po']
        )

    def test_relative_resource_paths_pot(self):
        """
        If a resource ends in .pot, replace the extension with .po since
        relative paths are used within non-source locales that do not
        have .pot files.
        """
        self.vcs_project.source_directory_path = Mock(return_value='/root/')
        self.vcs_project.resources_for_path = Mock(return_value=[
            '/root/foo.pot',
            '/root/meh/bar.pot'
        ])

        assert_equal(
            list(self.vcs_project.relative_resource_paths()),
            ['foo.po', 'meh/bar.po']
        )

    def test_source_directory_path_no_resource(self):
        """
        When searching for source directories, do not match directories that
        do not contain resource files.
        """
        checkout_path = os.path.join(TEST_CHECKOUT_PATH, 'no_resources_test')
        self.mock_checkout_path.return_value = checkout_path

        assert_equal(
            self.vcs_project.source_directory_path(),
            os.path.join(checkout_path, 'real_resources', 'templates')
        )

    def test_source_directory_scoring_templates(self):
        """
        When searching for source directories, prefer directories named
        `templates` over all others.
        """
        checkout_path = os.path.join(TEST_CHECKOUT_PATH, 'scoring_templates_test')
        self.mock_checkout_path.return_value = checkout_path

        assert_equal(
            self.vcs_project.source_directory_path(),
            os.path.join(checkout_path, 'templates')
        )

    def test_source_directory_scoring_en_US(self):
        """
        When searching for source directories, prefer directories named
        `en-US` over others besides `templates`.
        """
        checkout_path = os.path.join(TEST_CHECKOUT_PATH, 'scoring_en_US_test')
        self.mock_checkout_path.return_value = checkout_path

        assert_equal(
            self.vcs_project.source_directory_path(),
            os.path.join(checkout_path, 'en-US')
        )

    def test_source_directory_scoring_source_files(self):
        """
        When searching for source directories, prefer directories with
        source-only formats over all others.
        """
        checkout_path = os.path.join(TEST_CHECKOUT_PATH, 'scoring_source_files_test')
        self.mock_checkout_path.return_value = checkout_path

        assert_equal(
            self.vcs_project.source_directory_path(),
            os.path.join(checkout_path, 'en')  # en has pot files in it
        )

    def test_resources_parse_error(self):
        """
        If VCSResource() raises a ParseError while loading, log an error
        and skip the resource.
        """
        self.vcs_project.relative_resource_paths = Mock(return_value=['failure', 'success'])

        # Fail only if the path is failure so we can test the ignore.
        def vcs_resource_constructor(project, path):
            if path == 'failure':
                raise ParseError('error message')
            else:
                return 'successful resource'

        with patch('pontoon.base.vcs_models.VCSResource') as MockVCSResource, \
             patch('pontoon.base.vcs_models.log') as mock_log:
            MockVCSResource.side_effect = vcs_resource_constructor

            assert_equal(self.vcs_project.resources, {'success': 'successful resource'})
            mock_log.error.assert_called_with(CONTAINS('failure', 'error message'))

    def test_resource_for_path_region_properties(self):
        """
        If a project has a repository_url in pontoon.base.MOZILLA_REPOS,
        resources_for_path should ignore files named
        "region.properties".
        """
        url = 'https://moz.example.com'
        self.project.repositories.all().delete()
        self.project.repositories.add(RepositoryFactory.build(url=url))

        with patch('pontoon.base.vcs_models.os', wraps=os) as mock_os, \
             patch('pontoon.base.vcs_models.MOZILLA_REPOS', [url]):
            mock_os.walk.return_value = [
                ('/root', [], ['foo.pot', 'region.properties'])
            ]

            assert_equal(
                list(self.vcs_project.resources_for_path('/root')),
                [os.path.join('/root', 'foo.pot')]
            )