import os import fnmatch import re import winreg import subprocess import sys import itertools import collections import argparse import msvcrt import random import unittest import unittest.mock as mock import build from xml.etree import ElementTree _SAMPLE_PROJECT = rb""" Library Azimuth v4.8 ..\..\..\ $(SourceRoot)\Bin\$(Configuration)\ $(SourceRoot)\Junk\$(AssemblyName)\$(Configuration)\ 3.5 3.5 3.5 False ..\..\..\Bin\Release\UnitTestSharp.dll False ..\..\..\Bin\Release\Reffed.dll ..\..\..\3rdParty\High-Speed-Priority-Queue-for-C-Sharp\3rdParty1.dll ..\..\..\3rdParty\Dir1\Dir2\Dir3\3rdParty2.dll Test Test2 call "$(SolutionDir)..\..\Bin\Release\prebuild_dependency.exe" call "$(SolutionDir)..\..\Bin\Release\postbuild_dependency.exe" """ _SAMPLE_SLN = r""" Project("") = "sample", "sample.csproj", "" EndProject """ _DEPS_SLN = r""" Project("") = "dependency1", "/testDir/dependency1.csproj", "" EndProject Project("") = "dependency2", "/testDir/dependency2.csproj", "" EndProject Project("") = "dependency3", "/testDir/dependency3.csproj", "" EndProject Project("") = "dependency4", "/testDir/dependency4.csproj", "" EndProject """ _TEST_SLN = r""" Project("") = "test", "/testDir/test.csproj", "" EndProject Project("") = "dependency1", "/testDir/dependency1.csproj", "" EndProject """ class BuildTests(unittest.TestCase): def setUp(self): pass @mock.patch('os.walk') def test_recursive_glob(self, mock_walk): mock_files = ['file1.py', 'file2.csproj', 'file3.sln', 'dir/file4.csproj'] mock_walk.return_value = (('/some_path', None, mock_files),) files = build.recursive_glob('/some_path', '*.csproj') expected = [('/some_path', 'file2.csproj'), ('/some_path', 'dir/file4.csproj')] self.assertSetEqual(set(expected), set(files)) def test_extract_third_party(self): xml = ElementTree.fromstring(_SAMPLE_PROJECT) dependencies = build.extract_third_party(xml) self.assertSetEqual( set(['3rdParty1', '3rdParty2']), set(dependencies)) def test_extract_third_party_empty(self): xml = ElementTree.fromstring("") dependencies = build.extract_third_party(xml) self.assertSetEqual(set([]), set(dependencies)) def test_extract_itemgroup_references(self): xml = ElementTree.fromstring(_SAMPLE_PROJECT) dependencies = build.extract_itemgroup_references(xml) self.assertSetEqual( set(['UnitTestSharp', 'Reffed', 'Test', 'Test2']), set(dependencies)) def test_extract_itemgroup_references_empty(self): xml = ElementTree.fromstring("") dependencies = build.extract_itemgroup_references(xml) self.assertSetEqual(set(), set(dependencies)) def test_extract_build_events_dependencies(self): xml = ElementTree.fromstring(_SAMPLE_PROJECT) dependencies = build.extract_build_events_dependencies(xml) self.assertSetEqual( set(['prebuild_dependency', 'postbuild_dependency']), set(dependencies)) def test_extract_build_events_dependencies_empty(self): xml = ElementTree.fromstring("") dependencies = build.extract_build_events_dependencies(xml) self.assertSetEqual(set([]), set(dependencies)) def test_extract_dependencies(self): xml = ElementTree.fromstring(_SAMPLE_PROJECT) dependencies = build.extract_dependencies(xml) self.assertSetEqual( set(['UnitTestSharp', 'Reffed', 'Test', 'Test2', 'prebuild_dependency', 'postbuild_dependency']), set(dependencies)) def test_extract_dependencies_empty(self): xml = ElementTree.fromstring("") dependencies = build.extract_dependencies(xml) self.assertSetEqual(set([]), set(dependencies)) def test_find_visual_studio(self): visual_studio = build.find_visual_studio('version') self.assertIn('msbuild', visual_studio) @mock.patch('builtins.open', new_callable=mock.mock_open) @mock.patch('build.recursive_glob') def test_find_projects(self, mock_glob, mock_open): test_contents = { os.path.join('test', 'sample.csproj'): _SAMPLE_PROJECT.decode(), os.path.join('test', 'sample.sln'): _SAMPLE_SLN, } def faux_open(filename, mode): return mock.mock_open(read_data=test_contents[filename]).return_value mock_open.side_effect = faux_open def faux_glob(path, filter): if filter == '*.csproj': return [('test', 'sample.csproj')] else: return [('test', 'sample.sln')] mock_glob.side_effect = faux_glob projects = build.find_projects('base') self.assertEqual(1, len(projects)) self.assertSequenceEqual(['Azimuth'], list(projects.keys())) self.assertEqual('Azimuth', projects['Azimuth'].name) self.assertEqual('sample.csproj', projects['Azimuth'].filename) self.assertEqual('test\sample.csproj', projects['Azimuth'].csproj_path) self.assertEqual(['UnitTestSharp', 'Reffed', 'Test', 'Test2'], projects['Azimuth'].dependencies) self.assertEqual(['prebuild_dependency', 'postbuild_dependency'], projects['Azimuth'].tools) self.assertEqual(['3rdParty1', '3rdParty2'], projects['Azimuth'].third_party) self.assertEqual(['test/sample.sln'], projects['Azimuth'].solutions) self.assertEqual([], projects['Azimuth'].issues) self.assertEqual('UNKNOWN', projects['Azimuth'].status) self.assertEqual('Azimuth.dll', projects['Azimuth'].binary_name) self.assertTrue(projects['Azimuth'].is_library) class ProjectTests(unittest.TestCase): def setUp(self): self.project = build.Project('test', '/testDir', 'test.csproj') self.project.dependencies = ['dependency1', 'dependency2', 'dependency3', 'dependency4'] self.project.third_party = ['1', '2', '3'] self.projects = [self.project, build.Project('dependency1', '/testDir', 'dependency1.csproj'), build.Project('dependency2', '/testDir', 'dependency2.csproj'), build.Project('dependency3', '/testDir', 'dependency3.csproj'), build.Project('dependency4', '/testDir', 'dependency4.csproj'), build.Project('neverIncluded', '/testDir', 'neverIncluded.csproj')] self.projects = collections.OrderedDict([(project.name, project) for project in self.projects]) self.projects['dependency2'].tools = ['dependency3'] self.projects['dependency3'].dependencies = ['dependency4'] self.projects['dependency1'].third_party = ['1', '2', '4'] self.projects['dependency2'].third_party = ['1', '2', '5'] self.projects['dependency3'].third_party = ['1', '2', '6'] self.projects['dependency4'].third_party = ['1', '2', '7'] def test_recursive_dependencies(self): dependencies = list(self.project.recursive_dependencies(self.projects)) expected = set(self.projects.values()) expected.remove(self.projects['neverIncluded']) self.assertSetEqual(expected, set(dependencies)) def test_recursive_dependencies_empty(self): self.project.dependencies = [] dependencies = list(self.project.recursive_dependencies(self.projects)) self.assertSetEqual(set([self.project]), set(dependencies)) def test_recursive_dependencies_cycle(self): self.projects['dependency1'].dependencies = ['test'] with self.assertRaisesRegex(RuntimeError, 'cycle'): list(self.project.recursive_dependencies(self.projects)) def test_recursive_third_party(self): third_party = list(self.project.recursive_third_party(self.projects)) self.assertSetEqual(set(['1', '2', '3', '4', '5', '6', '7']), set(third_party)) def test_recursive_third_party_cycle(self): self.projects['dependency1'].dependencies = ['test'] with self.assertRaisesRegex(RuntimeError, 'cycle'): list(self.project.recursive_third_party(self.projects)) def test_recursively_find_third_party(self): self.projects['test2'] = build.Project( 'dependency1', '/testDir', 'dependency1.csproj') self.projects['test2'].third_party = ['T'] third_party = build.recursively_find_third_party( self.projects, ['test', 'test2']) self.assertSetEqual(set(['1', '2', '3', '4', '5', '6', '7', 'T']), set(third_party)) def test_recursively_find_project_dependencies(self): self.projects['dependency3'].dependencies = [] self.projects['test2'] = build.Project( 'dependency1', '/testDir', 'dependency1.csproj') self.projects['test2'].dependencies = ['dependency4'] dependencies = build.recursively_find_project_dependencies( self.projects, ['test', 'test2']) expected = set(self.projects.values()) expected.remove(self.projects['neverIncluded']) self.assertSetEqual(expected, set(dependencies)) def test_recursively_find_project_dependencies_cycle(self): self.projects['dependency3'].dependencies = [] self.projects['test2'] = build.Project( 'dependency1', '/testDir', 'dependency1.csproj') self.projects['test2'].dependencies = ['dependency4'] self.projects['dependency1'].dependencies = ['test'] with self.assertRaisesRegex(RuntimeError, 'cycle'): build.recursively_find_project_dependencies( self.projects, ['test', 'test2']) def test_order_projects_by_build_order(self): build_order = build.order_projects_by_build_order(self.projects) build_order_names = [project.name for project in build_order] expected_build_order = [ 'dependency1', 'dependency4', 'dependency3', 'dependency2', 'neverIncluded', 'test'] self.assertSequenceEqual(expected_build_order, build_order_names) def test_order_projects_by_build_order_subset_string(self): build_order = build.order_projects_by_build_order(self.projects, 'test') build_order_names = [project.name for project in build_order] expected_build_order = [ 'dependency1', 'dependency4', 'dependency3', 'dependency2', 'test'] self.assertSequenceEqual(expected_build_order, build_order_names) def test_order_projects_by_build_order_subset_list(self): build_order = build.order_projects_by_build_order(self.projects, ['dependency2', 'neverIncluded']) build_order_names = [project.name for project in build_order] expected_build_order = [ 'dependency4', 'dependency3', 'dependency2', 'neverIncluded'] self.assertSequenceEqual(expected_build_order, build_order_names) def test_order_projects_by_build_order_cycle(self): self.projects['dependency1'].dependencies = ['test'] with self.assertRaisesRegex(RuntimeError, 'cycle'): build.order_projects_by_build_order(self.projects) def test_order_projects_by_build_order_subset_cycle_string(self): self.projects['dependency1'].dependencies = ['test'] with self.assertRaisesRegex(RuntimeError, 'cycle'): build.order_projects_by_build_order(self.projects, 'test') def test_order_projects_by_build_order_subset_cycle_list(self): self.projects['dependency1'].dependencies = ['test'] with self.assertRaisesRegex(RuntimeError, 'cycle'): build.order_projects_by_build_order(self.projects, ['test', 'neverIncluded']) @mock.patch('builtins.open', new_callable=mock.mock_open) @mock.patch('build.recursive_glob') def test_find_solutions_for_projects(self, mock_glob, mock_open): test_contents = { os.path.join('deps', 'deps.sln'): _DEPS_SLN, os.path.join('test', 'never.sln'): "", os.path.join('test', 'test.sln'): _TEST_SLN, } def fauxOpen(filename, mode): return mock.mock_open(read_data=test_contents[filename]).return_value mock_open.side_effect = fauxOpen mock_glob.return_value=[('deps', 'deps.sln'), ('test', 'never.sln'), ('test', 'test.sln')] build.find_solutions_for_projects(self.projects, 'test') expected_dict = { 'dependency1': ['deps/deps.sln', 'test/test.sln'], 'dependency4': ['deps/deps.sln'], 'dependency3': ['deps/deps.sln'], 'dependency2': ['deps/deps.sln'], 'neverIncluded': [], 'test': ['test/test.sln'], } actual_dict = {project.name: project.solutions for project in self.projects.values()} self.assertDictEqual(expected_dict, actual_dict) if __name__ == '__main__': unittest.main()