class BaseDealerTest(BankMockerTest): # pylint: disable=too-few-public-methods """A container for utility classes common when testing the Dealer class.""" FEATURES = { BANK_PATH_1: (FEATURE_PATH_1, "", FEATURE_1 + "\n"), BANK_PATH_2: (FEATURE_PATH_2, "", FEATURE_2 + "\n"), } def __init__(self): super(BaseDealerTest, self).__init__() self.dealer = None self.mocked_open = MockOpen() self.mocked_popen = create_autospec(Popen) self.mocked_mkdir = Mock() def teardown(self): super(BaseDealerTest, self).teardown() patch.stopall() # Reset dealer instance. self.dealer = None def _mock_dealer_functions(self): """Mock out standard library functions used by the dealer module.""" self._reset_mocks() patcher = patch.multiple( "bddbot.dealer", open = self.mocked_open, Bank = self.mock_bank_class, RemoteBank = self.mock_bank_class, Popen = self.mocked_popen, mkdir = self.mocked_mkdir) patcher.start() def _create_dealer(self, banks, tests, name = ""): """Create a new dealer instance without loading state.""" if tests is None: tests = DEFAULT_TEST_COMMANDS self.mocked_open[STATE_PATH].side_effect = IOError() self.dealer = Dealer(banks, tests, name = name) self.mocked_open.assert_called_once_with(STATE_PATH, "rb") self._reset_mocks() def _load_dealer(self, banks = None, tests = None, name = ""): """Simulate a call to load() and verify success.""" if banks is None: banks = [BANK_PATH_1, ] if not tests: tests = DEFAULT_TEST_COMMANDS if self.dealer is None: self._create_dealer(banks, tests, name = name) # pylint: disable=bad-continuation with patch.multiple("bddbot.dealer", Bank = self.mock_bank_class, RemoteBank = self.mock_bank_class): self.dealer.load() # Verify calls to mocks. self.mocked_open.assert_not_called() self.mocked_popen.assert_not_called() for path in banks: if not path.startswith("@"): self.mock_bank_class.assert_any_call(path) else: (host, port) = path[1:].split(":") self.mock_bank_class.assert_called_with(name, host, int(port)) self._reset_mocks() def _deal(self, expected_feature, expected_scenario, bank_path = None, feature_path = None): # pylint: disable=too-many-arguments """Simulate dealing a scenario and verify success. If `expected_feature` is specified, simulate the first time a scenario is dealt from the features bank. Otherwise, simulate a consecutive deal from a previous bank. Return the commands passed to `Popen()` to verify outside of this function. """ if not bank_path: bank_path = BANK_PATH_1 if not feature_path: feature_path = bank_path.replace("bank", "feature") self.mocked_popen.return_value.returncode = 0 self.mocked_popen.return_value.communicate.return_value = ("", "") self.dealer.deal() # If feature is specified, simulate the first deal from the features bank. if expected_feature is not None: self.mocked_open.assert_called_once_with(feature_path, "w") self._assert_writes( ["", expected_feature + "\n", expected_scenario, ], path = feature_path) self.mocked_mkdir.assert_called_once_with(dirname(feature_path)) # If feature isn't specified, simulate a consecutive deal. # Note that calls to Popen should be verified outside of this function in this case. else: self.mocked_open.assert_called_once_with(feature_path, "ab") self.mocked_open[feature_path].write.assert_called_once_with(expected_scenario) self.mocked_popen.return_value.communicate.assert_called_with() self.mocked_mkdir.assert_not_called() self.mock_banks[bank_path].is_fresh.assert_called_with() self.mock_banks[bank_path].is_done.assert_called_with() self.mock_banks[bank_path].get_next_scenario.assert_called_once_with() # We return the commands because we reset the mocks at the end of the function. # The stdout/stderr values aren't important, we only care about the commands. popen_calls = [command for ((command, ), _) in self.mocked_popen.call_args_list] # Reset mocks. self._reset_mocks() return popen_calls def _assert_writes(self, chunks, path = FEATURE_PATH_1): """Verify all calls to write().""" assert_equal([call(chunk) for chunk in chunks], self.mocked_open[path].write.mock_calls) def _reset_mocks(self): """Reset all mocks.""" self.mocked_open.reset_mock() self.mocked_popen.reset_mock() self.mock_bank_class.reset_mock() self.mocked_mkdir.reset_mock() for mock_bank in self.mock_banks.itervalues(): mock_bank.reset_mock()