Example #1
0
    def _read_and_clear_state(self):
        if self.CHARM_STATE_FILE.stat().st_size:
            storage = SQLiteStorage(self.CHARM_STATE_FILE)
            with (self.JUJU_CHARM_DIR / 'metadata.yaml').open() as m:
                af = (self.JUJU_CHARM_DIR / 'actions.yaml')
                if af.exists():
                    with af.open() as a:
                        meta = CharmMeta.from_yaml(m, a)
                else:
                    meta = CharmMeta.from_yaml(m)
            framework = Framework(storage, self.JUJU_CHARM_DIR, meta, None)

            class ThisCharmEvents(CharmEvents):
                pass

            class Charm(self.charm_module.Charm):
                on = ThisCharmEvents()

            mycharm = Charm(framework)
            stored = mycharm._stored
            # Override the saved data with a cleared state
            storage.save_snapshot(stored._data.handle.path, {})
            storage.commit()
            framework.close()
        else:
            stored = StoredStateData(None, None)
        return stored
Example #2
0
    def setUp(self):
        tmpdir = Path(tempfile.mkdtemp())
        self.addCleanup(shutil.rmtree, str(tmpdir))

        self.framework = Framework(tmpdir / "framework.data", tmpdir, None,
                                   None)
        self.addCleanup(self.framework.close)
Example #3
0
    def create_framework(self):
        framework = Framework(self.tmpdir / "framework.data", self.tmpdir,
                              CharmMeta(), None)
        # Ensure that the Framework object is closed and cleaned up even
        # when the test fails or errors out.
        self.addCleanup(framework.close)

        return framework
    def create_framework(self):
        model = create_autospec(Model)
        model.unit = create_autospec(Unit)
        model.unit.is_leader = MagicMock(return_value=False)
        model.app = create_autospec(Application)
        model.pod = create_autospec(Pod)
        model.config = create_autospec(ConfigData)
        raw_meta = {'provides': {'mongo': {"interface": "mongodb"}}}
        framework = Framework(
            self.tmpdir / "framework.data.{}".format(str(uuid4)), self.tmpdir,
            CharmMeta(raw=raw_meta), model)

        framework.model.app.name = "test-app"
        self.addCleanup(framework.close)

        return framework
Example #5
0
    def create_framework(self, *, model=None, tmpdir=None):
        """Create a Framework object.

        By default operate in-memory; pass a temporary directory via the 'tmpdir'
        parameter if you whish to instantiate several frameworks sharing the
        same dir (e.g. for storing state).
        """
        if tmpdir is None:
            data_fpath = ":memory:"
            charm_dir = 'non-existant'
        else:
            data_fpath = tmpdir / "framework.data"
            charm_dir = tmpdir

        framework = Framework(data_fpath, charm_dir, meta=None, model=model)
        self.addCleanup(framework.close)
        return framework
Example #6
0
 def create_framework(self):
     model = Model(self.meta, _ModelBackend('local/0'))
     framework = Framework(self.tmpdir / "framework.data", self.tmpdir,
                           self.meta, model)
     self.addCleanup(framework.close)
     return framework
Example #7
0
 def create_framework(self):
     framework = Framework(self.tmpdir / "framework.data", self.tmpdir,
                           None, None)
     self.addCleanup(framework.close)
     return framework
Example #8
0
 def create_framework(self):
     model = Model(self.meta, _ModelBackend('local/0'))
     framework = Framework(SQLiteStorage(':memory:'), self.tmpdir,
                           self.meta, model)
     self.addCleanup(framework.close)
     return framework
Example #9
0
class BreakpointTests(unittest.TestCase):
    def setUp(self):
        tmpdir = Path(tempfile.mkdtemp())
        self.addCleanup(shutil.rmtree, str(tmpdir))

        self.framework = Framework(tmpdir / "framework.data", tmpdir, None,
                                   None)
        self.addCleanup(self.framework.close)

    def test_ignored(self, fake_stderr):
        # It doesn't do anything really unless proper environment is there.
        assert 'JUJU_DEBUG_AT' not in os.environ

        with patch('pdb.Pdb.set_trace') as mock:
            self.framework.breakpoint()
        self.assertEqual(mock.call_count, 0)
        self.assertEqual(fake_stderr.getvalue(), "")

    def test_pdb_properly_called(self, fake_stderr):
        # The debugger needs to leave the user in the frame where the breakpoint is executed,
        # which for the test is the frame we're calling it here in the test :).
        with patch.dict(os.environ, {'JUJU_DEBUG_AT': 'all'}):
            with patch('pdb.Pdb.set_trace') as mock:
                this_frame = inspect.currentframe()
                self.framework.breakpoint()
        self.assertEqual(mock.call_count, 1)
        self.assertEqual(mock.call_args, ((this_frame, ), {}))

    def test_welcome_message(self, fake_stderr):
        # Check that an initial message is shown to the user when code is interrupted.
        with patch.dict(os.environ, {'JUJU_DEBUG_AT': 'all'}):
            with patch('pdb.Pdb.set_trace'):
                self.framework.breakpoint()
        self.assertEqual(fake_stderr.getvalue(), _BREAKPOINT_WELCOME_MESSAGE)

    def test_welcome_message_not_multiple(self, fake_stderr):
        # Check that an initial message is NOT shown twice if the breakpoint is exercised
        # twice in the same run.
        with patch.dict(os.environ, {'JUJU_DEBUG_AT': 'all'}):
            with patch('pdb.Pdb.set_trace'):
                self.framework.breakpoint()
                self.assertEqual(fake_stderr.getvalue(),
                                 _BREAKPOINT_WELCOME_MESSAGE)
                self.framework.breakpoint()
                self.assertEqual(fake_stderr.getvalue(),
                                 _BREAKPOINT_WELCOME_MESSAGE)

    def test_builtin_breakpoint_hooked(self, fake_stderr):
        # Verify that the proper hook is set.
        with patch.dict(os.environ, {'JUJU_DEBUG_AT': 'all'}):
            with patch('pdb.Pdb.set_trace') as mock:
                # Calling through sys, not breakpoint() directly, so we can run the
                # tests with Py < 3.7.
                sys.breakpointhook()
        self.assertEqual(mock.call_count, 1)

    def test_breakpoint_names(self, fake_stderr):
        # Name rules:
        # - must start and end with lowercase alphanumeric characters
        # - only contain lowercase alphanumeric characters, or the hyphen "-"
        good_names = [
            'foobar',
            'foo-bar-baz',
            'foo-------bar',
            'foo123',
            '778',
            '77-xx',
            'a-b',
            'ab',
            'x',
        ]
        for name in good_names:
            with self.subTest(name=name):
                self.framework.breakpoint(name)

        bad_names = [
            '',
            '.',
            '-',
            '...foo',
            'foo.bar',
            'bar--'
            'FOO',
            'FooBar',
            'foo bar',
            'foo_bar',
            '/foobar',
            'break-here-☚',
        ]
        msg = 'breakpoint names must look like "foo" or "foo-bar"'
        for name in bad_names:
            with self.subTest(name=name):
                with self.assertRaises(ValueError) as cm:
                    self.framework.breakpoint(name)
                self.assertEqual(str(cm.exception), msg)

        reserved_names = [
            'all',
            'hook',
        ]
        msg = 'breakpoint names "all" and "hook" are reserved'
        for name in reserved_names:
            with self.subTest(name=name):
                with self.assertRaises(ValueError) as cm:
                    self.framework.breakpoint(name)
                self.assertEqual(str(cm.exception), msg)

        not_really_names = [
            123,
            1.1,
            False,
        ]
        for name in not_really_names:
            with self.subTest(name=name):
                with self.assertRaises(TypeError) as cm:
                    self.framework.breakpoint(name)
                self.assertEqual(str(cm.exception),
                                 'breakpoint names must be strings')

    def check_trace_set(self, envvar_value, breakpoint_name, call_count):
        """Helper to check the diverse combinations of situations."""
        with patch.dict(os.environ, {'JUJU_DEBUG_AT': envvar_value}):
            with patch('pdb.Pdb.set_trace') as mock:
                self.framework.breakpoint(breakpoint_name)
        self.assertEqual(mock.call_count, call_count)

    def test_unnamed_indicated_all(self, fake_stderr):
        # If 'all' is indicated, unnamed breakpoints will always activate.
        self.check_trace_set('all', None, 1)

    def test_unnamed_indicated_hook(self, fake_stderr):
        # Special value 'hook' was indicated, nothing to do with any call.
        self.check_trace_set('hook', None, 0)

    def test_named_indicated_specifically(self, fake_stderr):
        # Some breakpoint was indicated, and the framework call used exactly that name.
        self.check_trace_set('mybreak', 'mybreak', 1)

    def test_named_indicated_somethingelse(self, fake_stderr):
        # Some breakpoint was indicated, but the framework call was not with that name.
        self.check_trace_set('some-breakpoint', None, 0)

    def test_named_indicated_ingroup(self, fake_stderr):
        # A multiple breakpoint was indicated, and the framework call used a name among those.
        self.check_trace_set('some,mybreak,foobar', 'mybreak', 1)

    def test_named_indicated_all(self, fake_stderr):
        # The framework indicated 'all', which includes any named breakpoint set.
        self.check_trace_set('all', 'mybreak', 1)

    def test_named_indicated_hook(self, fake_stderr):
        # The framework indicated the special value 'hook', nothing to do with any named call.
        self.check_trace_set('hook', 'mybreak', 0)