def test_should_recursively_call_itself_on_dependencies_before_adding_dependencies_to_graph( self, mock_add_location_dependencies, mock_generate_cell_subgraph ): mock_generate_cell_subgraph.copied_call_args_list = [] def mock_recalc_recursive_call(worksheet, context, loc, visited, path): self.assertFalse(mock_add_location_dependencies.called) mock_generate_cell_subgraph.copied_call_args_list.append((worksheet, context, loc, set(visited), list(path))) mock_generate_cell_subgraph.side_effect = mock_recalc_recursive_call mock_generate_cell_subgraph_was_called_before_add_location_dependencies = [] def add_location_dependencies_side_effect(*_): mock_generate_cell_subgraph_was_called_before_add_location_dependencies.append(mock_generate_cell_subgraph.called) mock_add_location_dependencies.side_effect = add_location_dependencies_side_effect worksheet = Worksheet() worksheet[1, 11].formula = '=formula' worksheet[1, 11].dependencies = [(2, 22), (3, 33)] context = sentinel.context _generate_cell_subgraph(worksheet, context, (1, 11), set(), []) self.assertTrue(mock_add_location_dependencies.called) self.assertTrue(mock_generate_cell_subgraph_was_called_before_add_location_dependencies[0]) self.assertItemsEqual( mock_generate_cell_subgraph.copied_call_args_list, [ (worksheet, context, (2, 22), set(), [(1, 11)]), (worksheet, context, (3, 33), set(), [(1, 11)]), ] )
def test_should_recursively_call_itself_on_dependencies_before_adding_dependencies_to_graph( self, mock_add_location_dependencies, mock_generate_cell_subgraph): mock_generate_cell_subgraph.copied_call_args_list = [] def mock_recalc_recursive_call(worksheet, context, loc, visited, path): self.assertFalse(mock_add_location_dependencies.called) mock_generate_cell_subgraph.copied_call_args_list.append( (worksheet, context, loc, set(visited), list(path))) mock_generate_cell_subgraph.side_effect = mock_recalc_recursive_call mock_generate_cell_subgraph_was_called_before_add_location_dependencies = [] def add_location_dependencies_side_effect(*_): mock_generate_cell_subgraph_was_called_before_add_location_dependencies.append( mock_generate_cell_subgraph.called) mock_add_location_dependencies.side_effect = add_location_dependencies_side_effect worksheet = Worksheet() worksheet[1, 11].formula = '=formula' worksheet[1, 11].dependencies = [(2, 22), (3, 33)] context = sentinel.context _generate_cell_subgraph(worksheet, context, (1, 11), set(), []) self.assertTrue(mock_add_location_dependencies.called) self.assertTrue( mock_generate_cell_subgraph_was_called_before_add_location_dependencies[ 0]) self.assertItemsEqual( mock_generate_cell_subgraph.copied_call_args_list, [ (worksheet, context, (2, 22), set(), [(1, 11)]), (worksheet, context, (3, 33), set(), [(1, 11)]), ])
def test_should_not_reraise_cycle_error_if_its_outside_the_cycle_path( self, mock_report_cell_error, mock_recursive_call ): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 3].formula = "=foo" mock_recursive_call.side_effect = die(cycle_error) _generate_cell_subgraph(worksheet, sentinel.graph, (1, 3), set(), []) # should not raise
def test_should_raise_any_existing_cycle_error_for_visited_locations(self): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].error = cycle_error try: _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set([(1, 2)]), sentinel.path) except Exception, e: self.assertEquals(e, cycle_error)
def test_should_add_cell_to_graph_if_formula_not_set_but_python_formula_is( self, mock_add_location_dependencies ): worksheet = Worksheet() worksheet[1, 2].python_formula = 'blerk' _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set(), []) self.assertCalledOnce(mock_add_location_dependencies, sentinel.graph, (1, 2), set())
def test_should_add_cell_to_graph_if_formula_not_set_but_python_formula_is( self, mock_add_location_dependencies): worksheet = Worksheet() worksheet[1, 2].python_formula = 'blerk' _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set(), []) self.assertCalledOnce(mock_add_location_dependencies, sentinel.graph, (1, 2), set())
def test_should_not_reraise_cycle_error_if_its_outside_the_cycle_path( self, mock_report_cell_error, mock_recursive_call): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 3].formula = "=foo" mock_recursive_call.side_effect = die(cycle_error) _generate_cell_subgraph(worksheet, sentinel.graph, (1, 3), set(), []) # should not raise
def test_should_not_reprocess_locations_already_in_visited_even_if_it_is_in_worksheet( self, mock_add_location_dependencies): cell = Cell() cell.formula = 'constant' worksheet = Worksheet() worksheet[1, 2] = cell _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set([(1, 2)]), []) self.assertFalse(mock_add_location_dependencies.called)
def test_should_not_reprocess_locations_already_in_visited_even_if_it_is_in_worksheet( self, mock_add_location_dependencies ): cell = Cell() cell.formula = 'constant' worksheet = Worksheet() worksheet[1, 2] = cell _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set([(1, 2)]), []) self.assertFalse(mock_add_location_dependencies.called)
def test_should_report_cell_error_and_not_add_location_on_recursive_call_raising_cycle_error_if_location_is_not_in_cycle_path( self, mock_report_cell_error, mock_add_location_dependencies ): worksheet = Worksheet() worksheet[1, 11].formula = '=A12' worksheet[1, 11].dependencies = [(1, 12)] _generate_cell_subgraph(worksheet, sentinel.graph, (1, 11), set(), []) self.assertCalledOnce(mock_add_location_dependencies, sentinel.graph, (1, 11), set()) self.assertCalledOnce(mock_report_cell_error, worksheet, (1, 11), CycleError([]))
def test_reports_error_once_per_cell(self, mock_report_cell_error): mock_report_cell_error.side_effect = report_cell_error worksheet = Worksheet() worksheet[1, 1].formula = '=A2' worksheet[1, 2].formula = '=A1' try: _generate_cell_subgraph(worksheet, {}, (1, 1), set(), []) except CycleError: pass self.assertEquals(len(mock_report_cell_error.call_args_list), 2)
def test_does_not_include_discovered_cycle_in_deps_of_current_cell(self):# worksheet = Worksheet() worksheet[1, 1].formula = '=A2' worksheet[1, 2].formula = '=A1' worksheet[1, 3].formula = '=A1' visited = set() graph = {} _generate_cell_subgraph(worksheet, graph, (1, 3), visited, []) self.assertEquals(graph, {(1, 3): Node((1, 3), set())}) self.assertEquals(visited, set([(1, 2), (1, 3), (1, 1)]))
def test_should_report_then_raise_cycle_error_when_there_is_a_cycle( self, mock_report_cell_error): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].formula = "=foo" visited = set() try: _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, [(8, 9), (1, 2), (3, 4)]) except Exception, e: self.assertEquals(e, cycle_error)
def test_should_report_cell_error_and_not_add_location_on_recursive_call_raising_cycle_error_if_location_is_not_in_cycle_path( self, mock_report_cell_error, mock_add_location_dependencies): worksheet = Worksheet() worksheet[1, 11].formula = '=A12' worksheet[1, 11].dependencies = [(1, 12)] _generate_cell_subgraph(worksheet, sentinel.graph, (1, 11), set(), []) self.assertCalledOnce(mock_add_location_dependencies, sentinel.graph, (1, 11), set()) self.assertCalledOnce(mock_report_cell_error, worksheet, (1, 11), CycleError([]))
def test_should_report_then_raise_cycle_error_when_there_is_a_cycle( self, mock_report_cell_error ): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].formula = "=foo" visited = set() try: _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, [(8, 9), (1, 2), (3, 4)]) except Exception, e: self.assertEquals(e, cycle_error)
def test_does_not_include_discovered_cycle_in_deps_of_current_cell( self): # worksheet = Worksheet() worksheet[1, 1].formula = '=A2' worksheet[1, 2].formula = '=A1' worksheet[1, 3].formula = '=A1' visited = set() graph = {} _generate_cell_subgraph(worksheet, graph, (1, 3), visited, []) self.assertEquals(graph, {(1, 3): Node((1, 3), set())}) self.assertEquals(visited, set([(1, 2), (1, 3), (1, 1)]))
def test_should_reraise_cycle_error_after_reporting_if_its_in_the_cycle_path( self, mock_report_cell_error, mock_recursive_call ): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].formula = "=C4" mock_recursive_call.side_effect = die(cycle_error) visited = set() try: _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, []) except Exception, e: self.assertEquals(e, cycle_error)
def test_should_reraise_cycle_error_after_reporting_if_its_in_the_cycle_path( self, mock_report_cell_error, mock_recursive_call): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].formula = "=C4" mock_recursive_call.side_effect = die(cycle_error) visited = set() try: _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, []) except Exception, e: self.assertEquals(e, cycle_error)
def test_should_add_dependencies_to_graph(self, mock_add_location_dependencies): worksheet = Worksheet() worksheet[99, 98].formula = '=foobar' worksheet[1, 11].formula = '=foo' worksheet[1, 11].dependencies = [(99, 98)] graph = sentinel.graph _generate_cell_subgraph(worksheet, graph, (1, 11), set(), []) self.assertEqual( mock_add_location_dependencies.call_args, ((graph, (1, 11), set([(99, 98)])), {}), )
def test_should_remove_dependencies_with_errors_and_empty_cells( self, mock_add_location_dependencies): worksheet = Worksheet() worksheet[1, 1].formula = '1' worksheet[1, 2].error = CycleError([]) worksheet[1, 3].error = SyntaxError('') worksheet[1, 11].formula = '=foo' worksheet[1, 11].dependencies = [(1, 1), (1, 2), (1, 3), (1, 4)] graph = sentinel.graph _generate_cell_subgraph(worksheet, graph, (1, 11), set(), []) self.assertCalledOnce(mock_add_location_dependencies, graph, (1, 11), set())
def test_should_add_dependencies_to_graph( self, mock_add_location_dependencies ): worksheet = Worksheet() worksheet[99, 98].formula = '=foobar' worksheet[1, 11].formula = '=foo' worksheet[1, 11].dependencies = [(99, 98)] graph = sentinel.graph _generate_cell_subgraph(worksheet, graph, (1, 11), set(), []) self.assertEqual( mock_add_location_dependencies.call_args, ((graph, (1, 11), set([(99, 98)])), {}), )
def test_should_remove_dependencies_with_errors_and_empty_cells( self, mock_add_location_dependencies ): worksheet = Worksheet() worksheet[1, 1].formula = '1' worksheet[1, 2].error = CycleError([]) worksheet[1, 3].error = SyntaxError('') worksheet[1, 11].formula = '=foo' worksheet[1, 11].dependencies = [(1, 1), (1, 2), (1, 3), (1, 4)] graph = sentinel.graph _generate_cell_subgraph(worksheet, graph, (1, 11), set(), []) self.assertCalledOnce(mock_add_location_dependencies, graph, (1, 11), set())
def test_should_add_location_to_visited_set_after_recursing_deps( self, mock_generate_cell_subgraph): visited = set() visited_set_at_time_of_recursive_call = [] # NB: Clone visited or changes will be reflected in the one we store mock_generate_cell_subgraph.side_effect = lambda *_: visited_set_at_time_of_recursive_call.append( set(visited)) worksheet = Worksheet() worksheet[1, 2].formula = '=23' worksheet[1, 2].dependencies = [(3, 4)] _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, []) self.assertEquals(visited_set_at_time_of_recursive_call[0], set()) self.assertEquals(visited, set([(1, 2)])) self.assertTrue(mock_generate_cell_subgraph.called)
def test_should_not_recurse_into_existing_cycle_errors_or_include_them_in_its_deps( self, mock_recursive_call): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].error = cycle_error worksheet[3, 4].error = cycle_error worksheet[1, 3].formula = "=foo" worksheet[1, 3].dependencies = [(3, 4)] visited = set([(1, 2), (3, 4)]) graph = {} _generate_cell_subgraph(worksheet, graph, (1, 3), visited, []) dep_cell_calls = [c[0][2] for c in mock_recursive_call.call_args_list] self.assertNotIn(dep_cell_calls, (3, 4)) self.assertEquals(visited, set([(1, 2), (1, 3), (3, 4)])) self.assertEquals(graph, {(1, 3): Node((1, 3), set())})
def test_should_add_location_to_visited_set_after_recursing_deps( self, mock_generate_cell_subgraph ): visited = set() visited_set_at_time_of_recursive_call = [] # NB: Clone visited or changes will be reflected in the one we store mock_generate_cell_subgraph.side_effect = lambda *_: visited_set_at_time_of_recursive_call.append(set(visited)) worksheet = Worksheet() worksheet[1, 2].formula = '=23' worksheet[1, 2].dependencies = [(3, 4)] _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, []) self.assertEquals(visited_set_at_time_of_recursive_call[0], set()) self.assertEquals(visited, set([(1, 2)])) self.assertTrue(mock_generate_cell_subgraph.called)
def test_should_not_recurse_into_existing_cycle_errors_or_include_them_in_its_deps( self, mock_recursive_call ): cycle_error = CycleError([(1, 2), (3, 4), (1, 2)]) worksheet = Worksheet() worksheet[1, 2].error = cycle_error worksheet[3, 4].error = cycle_error worksheet[1, 3].formula = "=foo" worksheet[1, 3].dependencies = [(3, 4)] visited = set([(1, 2), (3, 4)]) graph = {} _generate_cell_subgraph(worksheet, graph, (1, 3), visited, []) dep_cell_calls = [c[0][2] for c in mock_recursive_call.call_args_list] self.assertNotIn(dep_cell_calls, (3, 4)) self.assertEquals(visited, set([(1, 2), (1, 3), (3, 4)])) self.assertEquals(graph, {(1, 3): Node((1, 3), set())})
def test_should_safely_handle_nonexistent_location(self): empty_worksheet = {} _generate_cell_subgraph(empty_worksheet, sentinel.graph, (1, 2), set(), [])