def test_cmd_pipeline(self): log_file = tempfile.mktemp(prefix='build-log') pre_graph_file = tempfile.mktemp(prefix='pre-graph') graph_file = tempfile.mktemp(prefix='graph') try: with open(log_file, 'w') as output_file: output_file.write(self._BUILD_LOG) interpreter = Interpreter() interpreter.onecmd('parse_vs_log %s' % log_file) interpreter.onecmd('store %s' % pre_graph_file) result = DependencyGraph.read(pre_graph_file) self.assertTrue(result.has_node('test.cpp')) self.assertTrue(result.has_node('test.hpp')) self.assertTrue(result.has_node('test2.hpp')) self.assertTrue(result.has_node('test-lib.hpp')) self.assertTrue(result.has_node('vector')) self.assertTrue(result.has_node('xmemory')) self.assertTrue(result.has_node('stdafx.cpp')) self.assertTrue(result.has_node('stdafx.h')) self.assertTrue(result.has_node('test-lib2.hpp')) self.assertTrue(result.has_node('memory')) interpreter_load_store = Interpreter() interpreter_load_store.onecmd(r'load %s' % pre_graph_file) interpreter_load_store.onecmd(r'remove_thirdparty_dependencies D:/work/test') interpreter_load_store.onecmd(r'store %s' % graph_file) result = DependencyGraph.read(graph_file) self.assertTrue(result.has_node('test.cpp')) self.assertTrue(result.has_node('test.hpp')) self.assertTrue(result.has_node('test2.hpp')) self.assertTrue(result.has_node('test-lib.hpp')) self.assertTrue(result.has_node('vector')) self.assertTrue(result.has_node('stdafx.cpp')) self.assertTrue(result.has_node('stdafx.h')) self.assertTrue(result.has_node('test-lib2.hpp')) self.assertTrue(result.has_node('memory')) self.assertFalse(result.has_node('xmemory')) finally: if os.path.exists(log_file): os.unlink(log_file) if os.path.exists(pre_graph_file): os.unlink(pre_graph_file) if os.path.exists(graph_file): os.unlink(graph_file)
def test_subgraph_dependencies(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp') depgraph.add_dependency_node('a.cpp', 'a.hpp') depgraph.add_dependency_node('a.cpp', 'b.hpp') depgraph.add_dependency_node('a.cpp', 'c.hpp') depgraph.add_dependency_node('b.hpp', 'c.hpp') subgraph = depgraph.get_subgraph('b.hpp', True, False) self.assertEqual( sorted(subgraph._graph.nodes()), sorted([DependencyGraph.ROOT_NODE_LABEL, 'b.hpp', 'c.hpp'])) self.assertEqual( sorted(subgraph._graph.edges()), sorted([(DependencyGraph.ROOT_NODE_LABEL, 'b.hpp'), ('b.hpp', 'c.hpp')]))
def test_adds_top_level_nodes(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp', strattr='value', intattr=2) depgraph.add_top_level_node('b.cpp', floatattr=3.5) with self.assertRaisesRegex(RuntimeError, r'^Duplicated node for label "a\.cpp"'): depgraph.add_top_level_node('a.cpp') graph = depgraph._graph nodes = graph.nodes() self.assertEqual(len(nodes), 3) self.assertIn(depgraph.ROOT_NODE_LABEL, nodes) self.assertIn('a.cpp', nodes) self.assertEqual(graph.node['a.cpp']['strattr'], 'value') self.assertEqual(graph.node['a.cpp']['intattr'], 2) self.assertIn('b.cpp', nodes) self.assertEqual(graph.node['b.cpp']['floatattr'], 3.5) self.assertEqual(len(graph.edges()), 2) self.assertTrue(graph.has_edge(depgraph.ROOT_NODE_LABEL, 'a.cpp')) self.assertTrue(graph.has_edge(depgraph.ROOT_NODE_LABEL, 'b.cpp'))
def test_attribute_access(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp', intattr=0) self.assertEqual(depgraph.get_attribute('a.cpp', 'intattr'), 0) self.assertFalse(depgraph.has_attribute('a.cpp', 'charattr')) self.assertIsNone(depgraph.get_attribute('a.cpp', 'charattr')) depgraph.set_attribute('a.cpp', 'charattr', 'a') self.assertTrue(depgraph.has_attribute('a.cpp', 'charattr')) depgraph.set_attribute('a.cpp', 'intattr', 1) self.assertEqual(depgraph.get_attribute('a.cpp', 'intattr'), 1) self.assertEqual(depgraph.get_attribute('a.cpp', 'charattr'), 'a')
def test_reads_writes_to_file(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp') depgraph.add_dependency_node('a.cpp', 'a.h', intattr=3) depgraph.add_dependency_node('a.cpp', 'aa.h') depgraph.add_top_level_node('b.cpp', floatattr=3.5) depgraph.add_dependency_node('b.cpp', 'b.h') depgraph.add_dependency_node('b.h', 'bb.h') depgraph.add_dependency_node('b.h', 'a.h', floatattr=1.2) path = tempfile.mktemp() try: depgraph.write(path) read = depgraph.read(path) finally: os.remove(path) self.assertEqual(sorted(depgraph._graph.nodes(data=True)), sorted(read._graph.nodes(data=True))) self.assertEqual(sorted(depgraph._graph.edges(data=True)), sorted(read._graph.edges(data=True)))
def test_adds_dependency_nodes(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp') depgraph.add_dependency_node('a.cpp', 'a.h', intattr=3) depgraph.add_dependency_node('a.cpp', 'aa.h') depgraph.add_top_level_node('b.cpp', floatattr=3.5) depgraph.add_dependency_node('b.cpp', 'b.h') depgraph.add_dependency_node('b.h', 'bb.h') depgraph.add_dependency_node('b.h', 'a.h', floatattr=1.2) with self.assertRaisesRegex( RuntimeError, r'Dependency node parent "c.cpp" not found ' r'for label "c.h"'): depgraph.add_dependency_node('c.cpp', 'c.h') graph = depgraph._graph nodes = graph.nodes() self.assertEqual(len(nodes), 7) self.assertIn(depgraph.ROOT_NODE_LABEL, nodes) self.assertIn('a.cpp', nodes) self.assertIn('a.h', nodes) self.assertEqual(graph.node['a.h']['intattr'], 3) self.assertEqual(graph.node['a.h']['floatattr'], 1.2) self.assertIn('aa.h', nodes) self.assertIn('b.cpp', nodes) self.assertIn('b.h', nodes) self.assertIn('bb.h', nodes) self.assertEqual(len(graph.edges()), 7) self.assertTrue(graph.has_edge(depgraph.ROOT_NODE_LABEL, 'a.cpp')) self.assertTrue(graph.has_edge(depgraph.ROOT_NODE_LABEL, 'b.cpp')) self.assertTrue(graph.has_edge('a.cpp', 'a.h')) self.assertTrue(graph.has_edge('a.cpp', 'aa.h')) self.assertTrue(graph.has_edge('b.cpp', 'b.h')) self.assertTrue(graph.has_edge('b.h', 'bb.h')) self.assertTrue(graph.has_edge('b.h', 'a.h'))
def test_removes_dependencies_by_predicate(self): depgraph = DependencyGraph() depgraph.add_top_level_node('bad-parent') depgraph.add_top_level_node('good-parent') depgraph.add_dependency_node('bad-parent', 'child-1', good=False) depgraph.add_dependency_node('good-parent', 'child-1', good=False) depgraph.add_dependency_node('bad-parent', 'child-2', good=True) depgraph.add_dependency_node('good-parent', 'child-2', good=False) depgraph.remove_dependency_by_predicate( lambda parent, child: parent == 'bad-parent' and depgraph. get_attribute(child, 'good', True) == False)
class TestAnalysis(unittest.TestCase): def _create_file(self, prefix, size): filename = tempfile.mktemp(prefix=prefix) with open(filename, 'w') as output_file: output_file.write('.' * size) self._files[prefix] = filename return filename def setUp(self): ''' The test dependency graph is setup as follows: pch.cpp [file size: 10B, build time: 10.0, create pch: pch.h] - pch.h [file size: 50B] -- lib.hpp [file size: 20B] a.cpp [file size: 100B, build time: 3.0] - other.hpp [file size: 50B] - a.hpp [file size: 10B] -- lib.hpp [file size: 20B] b.cpp [file size: 30B, build time: 5.0, uses pch: pch.h] - other.hpp [file size: 50B] - pch.h [file size: 50B] -- lib.hpp [file size: 20B] ''' self._files = {} self._dependency_graph = DependencyGraph() self._dependency_graph.add_top_level_node( 'pch.cpp', **{ Analyser.Attributes.BUILD_TIME: 10.0, Analyser.Attributes.ABSOLUTE_PATH: self._create_file('pch.cpp', 10), Analyser.Attributes.CREATED_PCH: 'pch.h' }) self._dependency_graph.add_dependency_node( 'pch.cpp', 'pch.h', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('pch.h', 50) }) self._dependency_graph.add_top_level_node( 'a.cpp', **{ Analyser.Attributes.BUILD_TIME: 3.0, Analyser.Attributes.ABSOLUTE_PATH: self._create_file('a.cpp', 100) }) self._dependency_graph.add_dependency_node( 'a.cpp', 'other.hpp', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('other.hpp', 50) }) self._dependency_graph.add_dependency_node( 'a.cpp', 'a.hpp', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('a.hpp', 10) }) libhpp_path = self._create_file('lib.hpp', 20) self._dependency_graph.add_dependency_node( 'a.hpp', 'lib.hpp', **{Analyser.Attributes.ABSOLUTE_PATH: libhpp_path}) self._dependency_graph.add_top_level_node( 'b.cpp', **{ Analyser.Attributes.BUILD_TIME: 5.0, Analyser.Attributes.ABSOLUTE_PATH: self._create_file('b.cpp', 30), Analyser.Attributes.USED_PCH: 'pch.h' }) self._dependency_graph.add_dependency_node( 'b.cpp', 'pch.h', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('pch.h', 50) }) self._dependency_graph.add_dependency_node( 'pch.h', 'lib.hpp', **{Analyser.Attributes.ABSOLUTE_PATH: libhpp_path}) self._dependency_graph.add_dependency_node( 'b.cpp', 'other.hpp', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('other.hpp', 50) }) def tearDown(self): for output_file in self._files.values(): os.unlink(output_file) def test_individual_file_sizes(self): analyser = Analyser(self._dependency_graph) analyser.calculate_file_sizes() self.assertEqual( self._dependency_graph.get_attribute( 'pch.cpp', Analyser.Attributes.FILE_SIZE), 10) self.assertEqual( self._dependency_graph.get_attribute( 'pch.h', Analyser.Attributes.FILE_SIZE), 50) self.assertEqual( self._dependency_graph.get_attribute( 'lib.hpp', Analyser.Attributes.FILE_SIZE), 20) self.assertEqual( self._dependency_graph.get_attribute( 'a.cpp', Analyser.Attributes.FILE_SIZE), 100) self.assertEqual( self._dependency_graph.get_attribute( 'a.hpp', Analyser.Attributes.FILE_SIZE), 10) self.assertEqual( self._dependency_graph.get_attribute( 'b.cpp', Analyser.Attributes.FILE_SIZE), 30) self.assertEqual( self._dependency_graph.get_attribute( 'other.hpp', Analyser.Attributes.FILE_SIZE), 50) def test_total_file_sizes(self): # adding dependency between b.cpp and a.hpp to check if a.hpp cashes in differently # when lib.hpp is added through a pch file self._dependency_graph.add_dependency_node('b.cpp', 'a.hpp') analyser = Analyser(self._dependency_graph) analyser.calculate_file_sizes() analyser.calculate_total_sizes() self.assertEqual( self._dependency_graph.get_attribute( 'pch.cpp', Analyser.Attributes.TOTAL_SIZE), 10 + 50 + 20) self.assertEqual( self._dependency_graph.get_attribute( 'pch.h', Analyser.Attributes.TOTAL_SIZE), 50 + 20) self.assertEqual( self._dependency_graph.get_attribute( 'lib.hpp', Analyser.Attributes.TOTAL_SIZE), 20 + 20) self.assertEqual( self._dependency_graph.get_attribute( 'a.cpp', Analyser.Attributes.TOTAL_SIZE), 100 + 10 + 20 + 50) self.assertEqual( self._dependency_graph.get_attribute( 'a.hpp', Analyser.Attributes.TOTAL_SIZE), 10 + 20 + 10) self.assertEqual( self._dependency_graph.get_attribute( 'b.cpp', Analyser.Attributes.TOTAL_SIZE), 30 + 10 + 50) # pch.h and lib.hpp not added self.assertEqual( self._dependency_graph.get_attribute( 'other.hpp', Analyser.Attributes.TOTAL_SIZE), 50 + 50) def test_total_file_sizes_no_redundant(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp', **{Analyser.Attributes.FILE_SIZE: 1}) depgraph.add_dependency_node('a.cpp', 'a.h', **{Analyser.Attributes.FILE_SIZE: 1}) depgraph.add_dependency_node('a.h', 'aa.h', **{Analyser.Attributes.FILE_SIZE: 1}) depgraph.add_dependency_node('a.cpp', 'aa.h', **{Analyser.Attributes.FILE_SIZE: 1}) analyser = Analyser(depgraph) analyser.calculate_total_sizes() self.assertEqual( depgraph.get_attribute(DependencyGraph.ROOT_NODE_LABEL, Analyser.Attributes.TOTAL_SIZE), 3) self.assertEqual( depgraph.get_attribute('a.cpp', Analyser.Attributes.TOTAL_SIZE), 3) self.assertEqual( depgraph.get_attribute('a.h', Analyser.Attributes.TOTAL_SIZE), 2) self.assertEqual( depgraph.get_attribute('aa.h', Analyser.Attributes.TOTAL_SIZE), 1) def test_total_build_times(self): analyser = Analyser(self._dependency_graph) analyser.calculate_total_build_times() self.assertEqual( self._dependency_graph.get_attribute( 'pch.h', Analyser.Attributes.BUILD_TIME), 10.0) # b.cpp not added self.assertEqual( self._dependency_graph.get_attribute( 'lib.hpp', Analyser.Attributes.BUILD_TIME), 10.0 + 3.0) # b.cpp not added self.assertAlmostEqual( self._dependency_graph.get_attribute( 'a.hpp', Analyser.Attributes.BUILD_TIME), 3.0) self.assertEqual( self._dependency_graph.get_attribute( 'other.hpp', Analyser.Attributes.BUILD_TIME), 3.0 + 5.0) def test_translation_units(self): analyser = Analyser(self._dependency_graph) analyser.calculate_translation_units() self.assertEqual( self._dependency_graph.get_attribute( DependencyGraph.ROOT_NODE_LABEL, Analyser.Attributes.TRANSLATION_UNITS), 3) self.assertFalse( self._dependency_graph.has_attribute( 'pch.cpp', Analyser.Attributes.TRANSLATION_UNITS)) self.assertEqual( self._dependency_graph.get_attribute( 'pch.h', Analyser.Attributes.TRANSLATION_UNITS), 1) # b.cpp not added self.assertEqual( self._dependency_graph.get_attribute( 'lib.hpp', Analyser.Attributes.TRANSLATION_UNITS), 2) # b.cpp not added self.assertFalse( self._dependency_graph.has_attribute( 'a.cpp', Analyser.Attributes.TRANSLATION_UNITS)) self.assertAlmostEqual( self._dependency_graph.get_attribute( 'a.hpp', Analyser.Attributes.TRANSLATION_UNITS), 1) self.assertFalse( self._dependency_graph.has_attribute( 'b.cpp', Analyser.Attributes.TRANSLATION_UNITS)) self.assertEqual( self._dependency_graph.get_attribute( 'other.hpp', Analyser.Attributes.TRANSLATION_UNITS), 2)
def setUp(self): ''' The test dependency graph is setup as follows: pch.cpp [file size: 10B, build time: 10.0, create pch: pch.h] - pch.h [file size: 50B] -- lib.hpp [file size: 20B] a.cpp [file size: 100B, build time: 3.0] - other.hpp [file size: 50B] - a.hpp [file size: 10B] -- lib.hpp [file size: 20B] b.cpp [file size: 30B, build time: 5.0, uses pch: pch.h] - other.hpp [file size: 50B] - pch.h [file size: 50B] -- lib.hpp [file size: 20B] ''' self._files = {} self._dependency_graph = DependencyGraph() self._dependency_graph.add_top_level_node( 'pch.cpp', **{ Analyser.Attributes.BUILD_TIME: 10.0, Analyser.Attributes.ABSOLUTE_PATH: self._create_file('pch.cpp', 10), Analyser.Attributes.CREATED_PCH: 'pch.h' }) self._dependency_graph.add_dependency_node( 'pch.cpp', 'pch.h', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('pch.h', 50) }) self._dependency_graph.add_top_level_node( 'a.cpp', **{ Analyser.Attributes.BUILD_TIME: 3.0, Analyser.Attributes.ABSOLUTE_PATH: self._create_file('a.cpp', 100) }) self._dependency_graph.add_dependency_node( 'a.cpp', 'other.hpp', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('other.hpp', 50) }) self._dependency_graph.add_dependency_node( 'a.cpp', 'a.hpp', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('a.hpp', 10) }) libhpp_path = self._create_file('lib.hpp', 20) self._dependency_graph.add_dependency_node( 'a.hpp', 'lib.hpp', **{Analyser.Attributes.ABSOLUTE_PATH: libhpp_path}) self._dependency_graph.add_top_level_node( 'b.cpp', **{ Analyser.Attributes.BUILD_TIME: 5.0, Analyser.Attributes.ABSOLUTE_PATH: self._create_file('b.cpp', 30), Analyser.Attributes.USED_PCH: 'pch.h' }) self._dependency_graph.add_dependency_node( 'b.cpp', 'pch.h', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('pch.h', 50) }) self._dependency_graph.add_dependency_node( 'pch.h', 'lib.hpp', **{Analyser.Attributes.ABSOLUTE_PATH: libhpp_path}) self._dependency_graph.add_dependency_node( 'b.cpp', 'other.hpp', **{ Analyser.Attributes.ABSOLUTE_PATH: self._create_file('other.hpp', 50) })
def test_total_file_sizes_no_redundant(self): depgraph = DependencyGraph() depgraph.add_top_level_node('a.cpp', **{Analyser.Attributes.FILE_SIZE: 1}) depgraph.add_dependency_node('a.cpp', 'a.h', **{Analyser.Attributes.FILE_SIZE: 1}) depgraph.add_dependency_node('a.h', 'aa.h', **{Analyser.Attributes.FILE_SIZE: 1}) depgraph.add_dependency_node('a.cpp', 'aa.h', **{Analyser.Attributes.FILE_SIZE: 1}) analyser = Analyser(depgraph) analyser.calculate_total_sizes() self.assertEqual( depgraph.get_attribute(DependencyGraph.ROOT_NODE_LABEL, Analyser.Attributes.TOTAL_SIZE), 3) self.assertEqual( depgraph.get_attribute('a.cpp', Analyser.Attributes.TOTAL_SIZE), 3) self.assertEqual( depgraph.get_attribute('a.h', Analyser.Attributes.TOTAL_SIZE), 2) self.assertEqual( depgraph.get_attribute('aa.h', Analyser.Attributes.TOTAL_SIZE), 1)