def can_be_pickled(self): c = Context() c.foo = {"bar": {"biz": ["baz", "buzz"]}} c2 = pickle.loads(pickle.dumps(c)) assert c == c2 assert c is not c2 assert c.foo.bar.biz is not c2.foo.bar.biz
def echo_hides_extra_sudo_flags(self): skip() # see TODO in sudo() re: clean output display config = Config(overrides={"runner": _Dummy}) Context(config=config).sudo("nope", echo=True) output = sys.stdout.getvalue() sys.__stderr__.write(repr(output) + "\n") assert "-S" not in output assert Context().sudo.prompt not in output assert "sudo nope" in output
def prefixes_should_apply_to_sudo(self, Local): runner = Local.return_value c = Context() with c.prefix("cd foo"): c.sudo("whoami") cmd = "sudo -S -p '[sudo] password: ' cd foo && whoami" assert runner.run.called, "sudo() never called runner.run()!" assert runner.run.call_args[0][0] == cmd
def prefixes_should_apply_to_run(self, Local): runner = Local.return_value c = Context() with c.prefix("cd foo"): c.run("whoami") cmd = "cd foo && whoami" assert runner.run.called, "run() never called runner.run()!" assert runner.run.call_args[0][0] == cmd
def kwarg_only_adds_to_kwarg(self, Local): runner = Local.return_value context = Context() watcher = self.watcher_klass() context.sudo("whoami", watchers=[watcher]) # When sudo() called w/ user-specified watchers, we add ours to # that list watchers = runner.run.call_args[1]["watchers"] # Will raise ValueError if not in the list watchers.remove(watcher) # Only remaining item in list should be our sudo responder assert len(watchers) == 1 assert isinstance(watchers[0], FailingResponder) assert watchers[0].pattern == self.escaped_prompt
def sites(c): """ Build both doc sites w/ maxed nitpicking. """ # TODO: This is super lolzy but we haven't actually tackled nontrivial # in-Python task calling yet, so we do this to get a copy of 'our' context, # which has been updated with the per-collection config data of the # docs/www subcollections. docs_c = Context(config=c.config.clone()) www_c = Context(config=c.config.clone()) docs_c.update(**docs.configuration()) www_c.update(**www.configuration()) # Must build both normally first to ensure good intersphinx inventory files # exist =/ circular dependencies ahoy! Do it quietly to avoid pulluting # output; only super-serious errors will bubble up. # TODO: wants a 'temporarily tweak context settings' contextmanager # TODO: also a f*****g spinner cuz this confuses me every time I run it # when the docs aren't already prebuilt docs_c["run"].hide = True www_c["run"].hide = True docs["build"](docs_c) www["build"](www_c) docs_c["run"].hide = False www_c["run"].hide = False # Run the actual builds, with nitpick=True (nitpicks + tracebacks) docs["build"](docs_c, nitpick=True) www["build"](www_c, nitpick=True)
def prefixes_command_with_sudo(self, Local): runner = Local.return_value Context().sudo("whoami") # NOTE: implicitly tests default sudo.prompt conf value cmd = "sudo -S -p '[sudo] password: ' whoami" assert runner.run.called, "sudo() never called runner.run()!" assert runner.run.call_args[0][0] == cmd
def tracks_times_called(self): context = Context() assert self.task.called is False self.task(context) assert self.task.called is True assert self.task.times_called == 1 self.task(context) assert self.task.times_called == 2
def base_case(self): # NOTE: Assumes a user whose password is 'mypass' has been created # & added to passworded (not passwordless) sudo configuration; and # that this user is the one running the test suite. Only for # running on Travis, basically. if not os.environ.get("TRAVIS", False): skip() config = Config({"sudo": {"password": "******"}}) result = Context(config=config).sudo("whoami", hide=True) assert result.stdout.strip() == "root"
def passes_through_other_run_kwargs(self, Local): runner = Local.return_value Context().sudo( "whoami", echo=True, warn=False, hide=True, encoding="ascii" ) assert runner.run.called, "sudo() never called runner.run()!" kwargs = runner.run.call_args[1] assert kwargs["echo"] is True assert kwargs["warn"] is False assert kwargs["hide"] is True assert kwargs["encoding"] == "ascii"
def _expect_responses(self, expected, config=None, kwargs=None): """ Execute mocked sudo(), expecting watchers= kwarg in its run(). * expected: list of 2-tuples of FailingResponder prompt/response * config: Config object, if an overridden one is needed * kwargs: sudo() kwargs, if needed """ if kwargs is None: kwargs = {} Local = Mock() runner = Local.return_value context = Context(config=config) if config else Context() context.config.runners.local = Local context.sudo("whoami", **kwargs) # Tease out the interesting bits - pattern/response - ignoring the # sentinel, etc for now. prompt_responses = [ (watcher.pattern, watcher.response) for watcher in runner.run.call_args[1]["watchers"] ] assert prompt_responses == expected
def nesting_should_retain_order(self, Local): runner = Local.return_value c = Context() with c.prefix("cd foo"): with c.prefix("cd bar"): c.run("whoami") cmd = "cd foo && cd bar && whoami" assert ( runner.run.called ), "run() never called runner.run()!" # noqa assert runner.run.call_args[0][0] == cmd c.run("whoami") cmd = "cd foo && whoami" assert runner.run.called, "run() never called runner.run()!" assert runner.run.call_args[0][0] == cmd # also test that prefixes do not persist c.run("whoami") cmd = "whoami" assert runner.run.called, "run() never called runner.run()!" assert runner.run.call_args[0][0] == cmd
def should_occur_before_prefixes(self, Local): runner = Local.return_value c = Context() with c.prefix("source venv"): with c.cd("foo"): c.run("whoami") cmd = "cd foo && source venv && whoami" assert runner.run.called, "run() never called runner.run()!" assert runner.run.call_args[0][0] == cmd
def config_only(self, Local): runner = Local.return_value # Set a config-driven list of watchers watcher = self.watcher_klass() overrides = {"run": {"watchers": [watcher]}} config = Config(overrides=overrides) Context(config=config).sudo("whoami") # Expect that sudo() extracted that config value & put it into # the kwarg level. (See comment in sudo() about why...) watchers = runner.run.call_args[1]["watchers"] # Will raise ValueError if not in the list watchers.remove(watcher) # Only remaining item in list should be our sudo responder assert len(watchers) == 1 assert isinstance(watchers[0], FailingResponder) assert watchers[0].pattern == self.escaped_prompt
def watch_docs(c): """ Watch both doc trees & rebuild them if files change. This includes e.g. rebuilding the API docs if the source code changes; rebuilding the WWW docs if the README changes; etc. Reuses the configuration values ``packaging.package`` or ``tests.package`` (the former winning over the latter if both defined) when determining which source directory to scan for API doc updates. """ # TODO: break back down into generic single-site version, then create split # tasks as with docs/www above. Probably wants raft#63. # NOTE: 'www'/'docs' refer to the module level sub-collections. meh. # Readme & WWW triggers WWW www_c = Context(config=c.config.clone()) www_c.update(**www.configuration()) www_handler = make_handler( ctx=www_c, task_=www["build"], regexes=[r"\./README.rst", r"\./sites/www"], ignore_regexes=[r".*/\..*\.swp", r"\./sites/www/_build"], ) # Code and docs trigger API docs_c = Context(config=c.config.clone()) docs_c.update(**docs.configuration()) regexes = [r"\./sites/docs"] package = c.get("packaging", {}).get("package", None) if package is None: package = c.get("tests", {}).get("package", None) if package: regexes.append(r"\./{}/".format(package)) api_handler = make_handler( ctx=docs_c, task_=docs["build"], regexes=regexes, ignore_regexes=[r".*/\..*\.swp", r"\./sites/docs/_build"], ) observe(www_handler, api_handler)
def should_use_finally_to_revert_changes_on_exceptions(self, Local): class Oops(Exception): pass runner = Local.return_value c = Context() try: with c.prefix("cd foo"): c.run("whoami") assert runner.run.call_args[0][0] == "cd foo && whoami" raise Oops except Oops: pass c.run("ls") # When bug present, this would be "cd foo && ls" assert runner.run.call_args[0][0] == "ls"
def _hang_on_full_pipe(self, pty): class Whoops(Exception): pass runner = Local(Context()) # Force runner IO thread-body method to raise an exception to mimic # real world encoding explosions/etc. When bug is present, this # will make the test hang until forcibly terminated. runner.handle_stdout = Mock(side_effect=Whoops, __name__="sigh") # NOTE: both Darwin (10.10) and Linux (Travis' docker image) have # this file. It's plenty large enough to fill most pipe buffers, # which is the triggering behavior. try: runner.run("cat /usr/share/dict/words", pty=pty) except ThreadException as e: assert len(e.exceptions) == 1 assert e.exceptions[0].type is Whoops else: assert False, "Did not receive expected ThreadException!"
def raises_auth_failure_when_failure_detected(self): with patch("raft.context.FailingResponder") as klass: unacceptable = Mock(side_effect=ResponseNotAccepted) klass.return_value.submit = unacceptable excepted = False try: config = Config(overrides={"sudo": {"password": "******"}}) Context(config=config).sudo("meh", hide=True) except AuthFailure as e: # Basic sanity checks; most of this is really tested in # Runner tests. assert e.result.exited is None expected = "The password submitted to prompt '[sudo] password: '******'t use except/else as that masks other real exceptions, # such as incorrectly unhandled ThreadErrors if not excepted: assert False, "Did not raise AuthFailure!"
def config_use_does_not_modify_config(self, Local): runner = Local.return_value watcher = self.watcher_klass() overrides = {"run": {"watchers": [watcher]}} config = Config(overrides=overrides) Context(config=config).sudo("whoami") # Here, 'watchers' is _the same object_ as was passed into # run(watchers=...). watchers = runner.run.call_args[1]["watchers"] # We want to make sure that what's in the config we just # generated, is untouched by the manipulation done inside # sudo(). # First, that they aren't the same obj err = "Found sudo() reusing config watchers list directly!" assert watchers is not config.run.watchers, err # And that the list is as it was before (i.e. it is not both # our watcher and the sudo()-added one) err = "Our config watchers list was modified!" assert config.run.watchers == [watcher], err
def both_kwarg_and_config(self, Local): runner = Local.return_value # Set a config-driven list of watchers conf_watcher = self.watcher_klass() overrides = {"run": {"watchers": [conf_watcher]}} config = Config(overrides=overrides) # AND supply a DIFFERENT kwarg-driven list of watchers kwarg_watcher = self.watcher_klass() Context(config=config).sudo("whoami", watchers=[kwarg_watcher]) # Expect that the kwarg watcher and our internal one were the # final result. watchers = runner.run.call_args[1]["watchers"] # Will raise ValueError if not in the list. .remove() uses # identity testing, so two instances of self.watcher_klass will # be different values here. watchers.remove(kwarg_watcher) # Only remaining item in list should be our sudo responder assert len(watchers) == 1 assert conf_watcher not in watchers # Extra sanity assert isinstance(watchers[0], FailingResponder) assert watchers[0].pattern == self.escaped_prompt
def takes_optional_config_arg(self): # Meh-tastic doesn't-barf tests. MEH. Context() Context(config={"foo": "bar"})
def dunder_call_wraps_body_call(self): context = Context() assert self.task(context) == 5
def setup(self): config = Config(defaults={"foo": "bar", "biz": {"baz": "boz"}}) self.c = Context(config=config)
class configuration_proxy: "Dict-like proxy for self.config" def setup(self): config = Config(defaults={"foo": "bar", "biz": {"baz": "boz"}}) self.c = Context(config=config) def direct_access_allowed(self): assert self.c.config.__class__ == Config assert self.c.config["foo"] == "bar" assert self.c.config.foo == "bar" def config_attr_may_be_overwritten_at_runtime(self): new_config = Config(defaults={"foo": "notbar"}) self.c.config = new_config assert self.c.foo == "notbar" def getitem(self): "___getitem__" assert self.c["foo"] == "bar" assert self.c["biz"]["baz"] == "boz" def getattr(self): "__getattr__" assert self.c.foo == "bar" assert self.c.biz.baz == "boz" def get(self): assert self.c.get("foo") == "bar" assert self.c.get("nope", "wut") == "wut" assert self.c.biz.get("nope", "hrm") == "hrm" def pop(self): assert self.c.pop("foo") == "bar" assert self.c.pop("foo", "notbar") == "notbar" assert self.c.biz.pop("baz") == "boz" def popitem(self): assert self.c.biz.popitem() == ("baz", "boz") del self.c["biz"] assert self.c.popitem() == ("foo", "bar") assert self.c.config == {} def del_(self): "del" del self.c["foo"] del self.c["biz"]["baz"] assert self.c.biz == {} del self.c["biz"] assert self.c.config == {} def clear(self): self.c.biz.clear() assert self.c.biz == {} self.c.clear() assert self.c.config == {} def setdefault(self): assert self.c.setdefault("foo") == "bar" assert self.c.biz.setdefault("baz") == "boz" assert self.c.setdefault("notfoo", "notbar") == "notbar" assert self.c.notfoo == "notbar" assert self.c.biz.setdefault("otherbaz", "otherboz") == "otherboz" assert self.c.biz.otherbaz == "otherboz" def update(self): self.c.update({"newkey": "newval"}) assert self.c["newkey"] == "newval" assert self.c.foo == "bar" self.c.biz.update(otherbaz="otherboz") assert self.c.biz.otherbaz == "otherboz"
def honors_runner_config_setting(self): runner_class = Mock() config = Config({"runners": {"local": runner_class}}) c = Context(config) c.run("foo") assert runner_class.mock_calls == [call(c), call().run("foo")]
def honors_config_for_prompt_value(self, Local): runner = Local.return_value config = Config(overrides={"sudo": {"prompt": "FEED ME: "}}) Context(config=config).sudo("whoami") cmd = "sudo -S -p 'FEED ME: ' whoami" assert runner.run.call_args[0][0] == cmd
def returns_run_result(self, Local): runner = Local.return_value expected = runner.run.return_value result = Context().sudo("whoami") err = "sudo() did not return run()'s return value!" assert result is expected, err
def user_kwarg_wins_over_config(self, Local): runner = Local.return_value config = Config(overrides={"sudo": {"user": "******"}}) Context(config=config).sudo("whoami", user="******") cmd = "sudo -S -p '[sudo] password: ' -H -u calrissian whoami" assert runner.run.call_args[0][0] == cmd
def defaults_to_Local(self, Local): c = Context() c.run("foo") assert Local.mock_calls == [call(c), call().run("foo")]
def _expect_attr(self, attr): c = Context() assert hasattr(c, attr) and callable(getattr(c, attr))