class OSWrapperTests(test_os_helper.OSWrapperTests):
    """Tests for os wrapper functions."""

    @defer.inlineCallbacks
    def setUp(self, test_dir_name=None, test_file_name=None,
              valid_file_path_builder=None):
        """Set up."""
        yield super(OSWrapperTests, self).setUp(
            test_dir_name=test_dir_name, test_file_name=test_file_name,
            valid_file_path_builder=None)
        self.handler = MementoHandler()
        self.handler.setLevel(logging.DEBUG)
        self._logger = logging.getLogger('ubuntuone.SyncDaemon')
        self._logger.addHandler(self.handler)
        self.addCleanup(self._logger.removeHandler, self.handler)
        self.patch(darwin.shutil, "move", self._fake_move)

    def _fake_move(*args):
        """Fake shutil move."""
        raise Exception("Fail fake move")

    def test_stat_symlink(self):
        """Test that it doesn't follow symlinks.

        We compare the inode only (enough to see if it's returning info
        from the link or the linked), as we can not compare the full stat
        because the st_mode will be different.
        """
        link = os.path.join(self.basedir, 'foo')
        os.symlink(self.testfile, link)
        self.assertNotEqual(os.stat(link).st_ino, stat_path(link).st_ino)
        self.assertEqual(os.lstat(link).st_ino, stat_path(link).st_ino)

    def test_movetotrash_file_bad(self):
        """Something bad happen when moving to trash, removed anyway."""
        path = os.path.join(self.basedir, 'foo')
        open_file(path, 'w').close()
        move_to_trash(path)
        self.assertFalse(os.path.exists(path))
        self.assertTrue(self.handler.check_warning("Problems moving to trash!",
                                                   "Removing anyway", "foo"))

    def test_movetotrash_file_not_exists(self):
        """Something bad happen when moving to trash, removed anyway."""
        path = os.path.join(self.basedir, 'foo2')
        self.assertFalse(os.path.exists(path))
        self.assertRaises(OSError, move_to_trash, path)

    def test_movetotrash_dir_bad(self):
        """Something bad happen when moving to trash, removed anyway."""
        path = os.path.join(self.basedir, 'foo')
        os.mkdir(path)
        open_file(os.path.join(path, 'file inside directory'), 'w').close()
        move_to_trash(path)
        self.assertFalse(os.path.exists(path))
        self.assertTrue(self.handler.check_warning("Problems moving to trash!",
                                                   "Removing anyway", "foo"))
    def test_gsettings_cannot_parse(self):
        """Some weird setting that cannot be parsed is logged with warning."""
        memento = MementoHandler()
        memento.setLevel(logging.DEBUG)
        gsettings.logger.addHandler(memento)
        self.addCleanup(gsettings.logger.removeHandler, memento)

        troublesome_value = "#bang"
        template_values = dict(BASE_GSETTINGS_VALUES)
        template_values["ignore_hosts"] = troublesome_value
        fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values)
        self.patch(gsettings.subprocess, "check_output",
                   lambda _: fake_output)
        ps = gsettings.get_proxy_settings()
        self.assertTrue(memento.check_warning(gsettings.CANNOT_PARSE_WARNING %
                                              troublesome_value))
        self.assertEqual(ps, {})
class ClientDummyAuthTests(AuthenticationBaseTestCase):
    """Client authentication tests using the dummy auth provider."""

    auth_provider_class = DummyAuthProvider

    @defer.inlineCallbacks
    def setUp(self):
        yield super(ClientDummyAuthTests, self).setUp()
        self.creds = "open sesame"
        self.bad_creds = "not my secret"
        self.handler = MementoHandler()
        logger = logging.getLogger("storage.server")
        logger.addHandler(self.handler)
        self.addCleanup(logger.removeHandler, self.handler)
        self.handler.setLevel(logging.DEBUG)

    def assert_auth_ok_logging(self):
        self.assertTrue(self.handler.check_debug("authenticated user", "OK", self.usr0.username))
        self.assertFalse(self.handler.check_warning("missing user"))

    def assert_auth_ok_missing_user(self):
        self.assertTrue(self.handler.check_debug("missing user", "(id=%s)" % self.usr0.id))
        self.assertFalse(self.handler.check_info("authenticated user"))

    @defer.inlineCallbacks
    def test_auth_ok_user_ok(self):
        """Correct authentication must succeed."""
        yield self.callback_test(self.do_auth, credentials=self.creds, add_default_callbacks=True)
        self.assert_auth_ok_logging()

    @defer.inlineCallbacks
    def test_auth_ok_bad_user(self):
        """Non existing user must fail authentication."""
        # make the user getter fail
        self.patch(self.service.factory.content, "get_user_by_id", lambda *a, **k: defer.fail(DoesNotExist()))

        d = self.callback_test(self.do_auth, credentials=self.creds, add_default_callbacks=True)
        yield self.assertFailure(d, protocol_errors.AuthenticationFailedError)

        self.assert_auth_ok_missing_user()

    @defer.inlineCallbacks
    def test_auth_ok_with_session_id(self):
        """Correct authentication must succeed and include the session_id."""
        auth_request = yield self.callback_test(self.do_auth, credentials=self.creds, add_default_callbacks=True)

        protocol = self.service.factory.protocols[0]
        self.assertEqual(auth_request.session_id, str(protocol.session_id))

    @defer.inlineCallbacks
    def test_auth_ok_with_metadata(self):
        """Correct authentication must succeed and include metadata."""
        m_called = []
        self.service.factory.metrics.meter = lambda *a: m_called.append(a)

        metadata = {u"platform": u"linux2", u"version": u"1.0", u"foo": u"bar"}
        yield self.callback_test(self.do_auth, credentials=self.creds, metadata=metadata, add_default_callbacks=True)

        self.assertTrue(self.handler.check_info("Client metadata: %s" % metadata))
        self.assertIn(("client.platform.linux2", 1), m_called)
        self.assertIn(("client.version.1_0", 1), m_called)
        self.assertNotIn(("client.foo.bar", 1), m_called)

    def test_auth_fail(self):
        """Wrong secret must fail."""

        def test(client, **kwargs):
            d = self.do_auth(client, credentials=self.bad_creds)
            d.addCallbacks(
                lambda _: client.test_fail(Exception("Should not succeed.")), lambda _: client.test_done("ok")
            )

        return self.callback_test(test)

    def test_get_root(self):
        """Must receive the root after authentication."""

        @defer.inlineCallbacks
        def test(client, **kwargs):
            yield self.do_auth(client, credentials=self.creds)
            root_id = yield client.get_root()
            self.assertIsNotNone(root_id)

        return self.callback_test(test, add_default_callbacks=True)

    def test_get_root_twice(self):
        """Get root must keep the root id."""

        @defer.inlineCallbacks
        def test(client, **kwargs):
            yield self.do_auth(client, credentials=self.creds)
            root_id1 = yield client.get_root()
            root_id2 = yield client.get_root()
            self.assertEqual(root_id1, root_id2)

        return self.callback_test(test, add_default_callbacks=True)

    def test_user_becomes_inactive(self):
        """After StorageUser authentication ok it becomes inactive."""

        @defer.inlineCallbacks
        def test(client):
            """Test."""
            yield self.do_auth(client, credentials=self.creds)
            root_id = yield client.get_root()

            # create one file, should be ok
            yield client.make_file(request.ROOT, root_id, "f1")

            # cancel user subscription, so it needs
            # to get it again from the DB
            self.usr0.update(subscription=False)

            # create second file, should NOT be ok
            try:
                yield client.make_file(request.ROOT, root_id, "f2")
            except protocol_errors.DoesNotExistError:
                pass  # failed as we expected
            else:
                client.test_fail("It should have failed!")

        return self.callback_test(test, add_default_callbacks=True)
class HeartbeatWriterTest(TwistedTestCase):
    """Tests for HeartbeatWriter."""

    interval = 5

    @defer.inlineCallbacks
    def setUp(self):
        yield super(HeartbeatWriterTest, self).setUp()
        self.logger = logging.Logger("HeartbeatWriter.test")
        self.handler = MementoHandler()
        self.logger.addHandler(self.handler)
        self.addCleanup(self.logger.removeHandler, self.handler)
        self.clock = task.Clock()
        self.hw = HeartbeatWriter(self.interval, self.logger,
                                  reactor=self.clock)

    def test_send_no_transport(self):
        """Log a warning when there is no transport."""
        self.hw.send()
        self.assertTrue(self.handler.check_warning(
            "Can't send heartbeat without a transport"))

    def test_send_loop(self):
        """Send heartbeats in the LoopingCall."""
        # first connect to something
        transport = StringIO()
        self.clock.advance(2)
        self.hw.makeConnection(transport)
        self.clock.advance(5)
        self.clock.advance(5)
        # we should have 3 heartbeats in the transport, get them
        raw_events = transport.getvalue().split(BEGIN_TOKEN, 3)
        events = []
        for raw_event in raw_events:
            if raw_event:
                events.append(json.loads(raw_event.strip(END_TOKEN)))
        # strip the tokens
        for i, timestamp in [(0, 2), (1, 7), (2, 12)]:
            self.assertEqual(events[i]['type'], "heartbeat")
            self.assertEqual(events[i]['time'], timestamp)

    def test_send_on_connectionMade(self):
        """On connectionMade start the loop and send."""
        # first connect to something
        transport = StringIO()
        self.clock.advance(0.1)
        self.hw.makeConnection(transport)
        self.assertTrue(self.hw.loop.running)
        raw_event = transport.getvalue()
        self.assertTrue(raw_event.startswith(BEGIN_TOKEN))
        self.assertTrue(raw_event.endswith(END_TOKEN))
        # strip the tokens
        payload = json.loads(raw_event.strip(BEGIN_TOKEN).strip(END_TOKEN))
        self.assertEqual(payload['type'], "heartbeat")
        self.assertEqual(payload['time'], self.clock.seconds())

    def test_connectionLost(self):
        """On connectionLost cleanup everything."""
        self.hw.makeConnection(None)
        called = []
        self.patch(self.hw.loop, 'stop', lambda: called.append(True))
        self.hw.connectionLost(protocol.connectionDone)
        self.assertTrue(self.handler.check_info(
            "HeartbeatWriter connectionLost: %s" % (protocol.connectionDone,)))
        self.assertTrue(called)
        self.assertEqual(self.hw.loop, None)
        self.assertEqual(self.hw.reactor, None)
        self.assertEqual(self.hw.logger, None)
class OSWrapperTests(test_os_helper.OSWrapperTests):
    """Tests for os wrapper functions."""

    @defer.inlineCallbacks
    def setUp(self):
        """Set up."""
        yield super(OSWrapperTests, self).setUp()
        self.handler = MementoHandler()
        self.handler.setLevel(logging.DEBUG)
        self._logger = logging.getLogger('ubuntuone.SyncDaemon')
        self._logger.addHandler(self.handler)
        self.addCleanup(self._logger.removeHandler, self.handler)

    def test_stat_symlink(self):
        """Test that it doesn't follow symlinks.

        We compare the inode only (enough to see if it's returning info
        from the link or the linked), as we can not compare the full stat
        because the st_mode will be different.
        """
        link = os.path.join(self.basedir, 'foo')
        os.symlink(self.testfile, link)
        self.assertNotEqual(os.stat(link).st_ino, stat_path(link).st_ino)
        self.assertEqual(os.lstat(link).st_ino, stat_path(link).st_ino)

    def test_movetotrash_file_bad(self):
        """Something bad happen when moving to trash, removed anyway."""
        FakeGIOFile._bad_trash_call = False   # error
        self.patch(gio, "File", FakeGIOFile)
        path = os.path.join(self.basedir, 'foo')
        open_file(path, 'w').close()
        move_to_trash(path)
        self.assertFalse(os.path.exists(path))
        self.assertTrue(self.handler.check_warning("Problems moving to trash!",
                                                   "Removing anyway", "foo"))

    def test_movetotrash_dir_bad(self):
        """Something bad happen when moving to trash, removed anyway."""
        FakeGIOFile._bad_trash_call = False   # error
        self.patch(gio, "File", FakeGIOFile)
        path = os.path.join(self.basedir, 'foo')
        os.mkdir(path)
        open_file(os.path.join(path, 'file inside directory'), 'w').close()
        move_to_trash(path)
        self.assertFalse(os.path.exists(path))
        self.assertTrue(self.handler.check_warning("Problems moving to trash!",
                                                   "Removing anyway", "foo"))

    def test_movetotrash_file_systemnotcapable(self):
        """The system is not capable of moving into trash."""
        FakeGIOFile._bad_trash_call = GIO_NOT_SUPPORTED
        self.patch(gio, "File", FakeGIOFile)
        path = os.path.join(self.basedir, 'foo')
        open_file(path, 'w').close()
        move_to_trash(path)
        self.assertFalse(os.path.exists(path))
        self.assertTrue(self.handler.check_warning("Problems moving to trash!",
                                                   "Removing anyway", "foo",
                                                   "ERROR_NOT_SUPPORTED"))

    def test_movetotrash_dir_systemnotcapable(self):
        """The system is not capable of moving into trash."""
        FakeGIOFile._bad_trash_call = GIO_NOT_SUPPORTED
        self.patch(gio, "File", FakeGIOFile)
        path = os.path.join(self.basedir, 'foo')
        os.mkdir(path)
        open_file(os.path.join(path, 'file inside directory'), 'w').close()
        move_to_trash(path)
        self.assertFalse(os.path.exists(path))
        self.assertTrue(self.handler.check_warning("Problems moving to trash!",
                                                   "Removing anyway", "foo",
                                                   "ERROR_NOT_SUPPORTED"))
Exemple #6
0
class HeartbeatWriterTest(TwistedTestCase):
    """Tests for HeartbeatWriter."""

    interval = 5

    @defer.inlineCallbacks
    def setUp(self):
        yield super(HeartbeatWriterTest, self).setUp()
        self.logger = logging.Logger("HeartbeatWriter.test")
        self.handler = MementoHandler()
        self.logger.addHandler(self.handler)
        self.addCleanup(self.logger.removeHandler, self.handler)
        self.clock = task.Clock()
        self.hw = HeartbeatWriter(self.interval,
                                  self.logger,
                                  reactor=self.clock)

    def test_send_no_transport(self):
        """Log a warning when there is no transport."""
        self.hw.send()
        self.assertTrue(
            self.handler.check_warning(
                "Can't send heartbeat without a transport"))

    def test_send_loop(self):
        """Send heartbeats in the LoopingCall."""
        # first connect to something
        transport = StringIO()
        self.clock.advance(2)
        self.hw.makeConnection(transport)
        self.clock.advance(5)
        self.clock.advance(5)
        # we should have 3 heartbeats in the transport, get them
        raw_events = transport.getvalue().split(BEGIN_TOKEN, 3)
        events = []
        for raw_event in raw_events:
            if raw_event:
                events.append(json.loads(raw_event.strip(END_TOKEN)))
        # strip the tokens
        for i, timestamp in [(0, 2), (1, 7), (2, 12)]:
            self.assertEqual(events[i]['type'], "heartbeat")
            self.assertEqual(events[i]['time'], timestamp)

    def test_send_on_connectionMade(self):
        """On connectionMade start the loop and send."""
        # first connect to something
        transport = StringIO()
        self.clock.advance(0.1)
        self.hw.makeConnection(transport)
        self.assertTrue(self.hw.loop.running)
        raw_event = transport.getvalue()
        self.assertTrue(raw_event.startswith(BEGIN_TOKEN))
        self.assertTrue(raw_event.endswith(END_TOKEN))
        # strip the tokens
        payload = json.loads(raw_event.strip(BEGIN_TOKEN).strip(END_TOKEN))
        self.assertEqual(payload['type'], "heartbeat")
        self.assertEqual(payload['time'], self.clock.seconds())

    def test_connectionLost(self):
        """On connectionLost cleanup everything."""
        self.hw.makeConnection(None)
        called = []
        self.patch(self.hw.loop, 'stop', lambda: called.append(True))
        self.hw.connectionLost(protocol.connectionDone)
        self.assertTrue(
            self.handler.check_info("HeartbeatWriter connectionLost: %s" %
                                    (protocol.connectionDone, )))
        self.assertTrue(called)
        self.assertEqual(self.hw.loop, None)
        self.assertEqual(self.hw.reactor, None)
        self.assertEqual(self.hw.logger, None)
class HeartbeatListenerTestCase(TestCase):
    """Tests for HeartbeatListener class."""

    def setUp(self):
        super(HeartbeatListenerTestCase, self).setUp()
        self.stdin = StringIO()
        self.stdout = StringIO()
        self.stderr = StringIO()
        self.mocker = Mocker()
        self.rpc = self.mocker.mock()
        self.listener = HeartbeatListener(1, 10, ['foo'], [], self.rpc,
                                          stdin=self.stdin, stdout=self.stdout,
                                          stderr=self.stderr)
        self.next_fail = {}
        self.handler = MementoHandler()
        self.listener.logger.addHandler(self.handler)
        self.listener.logger.setLevel(logging.DEBUG)
        self.handler.setLevel(logging.DEBUG)
        self.listener.logger.propagate = False
        self.processes = [dict(name="heartbeat", group="heartbeat", pid="101",
                               state=RUNNING)]
        self.handler.debug = True

    def tearDown(self):
        self.listener.logger.removeHandler(self.handler)
        self.handler.close()
        self.next_fail = None
        self.handler = None
        self.listener = None
        super(HeartbeatListenerTestCase, self).tearDown()

    def fail_next_stop(self, pname):
        """Make next stopProcess to fail."""
        expect(self.rpc.supervisor.stopProcess(pname)).throw(
            xmlrpclib.Fault(42, "Failed to stop the process."))

    def fail_next_start(self, pname):
        """Make next startProcess to fail."""
        expect(self.rpc.supervisor.startProcess(pname)).throw(
            xmlrpclib.Fault(42, "Failed to start the process."))

    def test_restart(self):
        """Test the restart method."""
        expect(self.rpc.supervisor.stopProcess("foo"))
        expect(self.rpc.supervisor.startProcess("foo"))
        with self.mocker:
            self.listener.restart("foo", "testing")
        self.assertTrue(self.handler.check_info("Restarting foo (last "
                                                "hearbeat: testing)"))

    def test_restart_fail_stop(self):
        """Test the restart method failing to stop the process."""
        self.fail_next_stop("foo")
        last = time.time()
        with self.mocker:
            try:
                self.listener.restart("foo", last)
            except xmlrpclib.Fault:
                msg = ("Failed to stop process %s (last heartbeat: %s), "
                       "exiting: %s") % \
                    ("foo", last, "<Fault 42: 'Failed to stop the process.'>")
                self.assertTrue(self.handler.check_error(msg))
            else:
                self.fail("Should get an xmlrpclib.Fault")

    def test_restart_fail_start(self):
        """Test the restart method failing to start the process."""
        expect(self.rpc.supervisor.stopProcess("foo"))
        self.fail_next_start("foo")
        last = time.time()
        with self.mocker:
            try:
                self.listener.restart("foo", last)
            except xmlrpclib.Fault:
                msg = (
                    'Failed to start process %s after stopping it, exiting: %s'
                ) % ("foo", "<Fault 42: 'Failed to start the process.'>")
                self.assertTrue(self.handler.check_error(msg))
            else:
                self.fail("Should get an xmlrpclib.Fault")

    def test_check_processes(self):
        """Test the check_processes method."""
        # add the fake process to the process list
        self.processes.append(dict(name="foo", group="foo", pid="42",
                                   state=RUNNING))
        self.processes.append(dict(name="bar", group="bar", pid="43",
                                   state=RUNNING))
        self.listener.processes = ['bar']
        # 2 process to restart
        self.listener.data['foo'] = {
            'time': time.time() - (self.listener.timeout + 2)}
        self.listener.data['bar'] = {
            'time': time.time() - (self.listener.timeout + 3)}
        self.listener.data['p-1'] = {
            'time': time.time() - (self.listener.timeout - 1)}
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        expect(self.rpc.supervisor.stopProcess("foo:"))
        expect(self.rpc.supervisor.startProcess("foo:"))
        expect(self.rpc.supervisor.stopProcess("bar:bar"))
        expect(self.rpc.supervisor.startProcess("bar:bar"))
        with self.mocker:
            self.listener.check_processes()

    def test_check_processes_no_data(self):
        """Test the check_processes method with no data of a process."""
        # add the fake process to the process list
        self.processes.append(dict(name="foo", group="foo", pid="42",
                                   state=RUNNING))
        self.processes.append(dict(name="bar", group="bar", pid="43",
                                   state=RUNNING))
        self.listener.processes = ['bar']
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        expect(self.rpc.supervisor.stopProcess("foo:"))
        expect(self.rpc.supervisor.startProcess("foo:"))
        expect(self.rpc.supervisor.stopProcess("bar:bar"))
        expect(self.rpc.supervisor.startProcess("bar:bar"))
        with self.mocker:
            # one process to restart
            self.listener.check_processes()
        self.assertTrue(self.handler.check_warning(
            "Restarting process foo:foo (42), as we never received a hearbeat"
            " event from it"))
        self.assertTrue(self.handler.check_warning(
            "Restarting process bar:bar (43), as we never received a hearbeat"
            " event from it"))

    def test_check_processes_untracked(self):
        """Test the check_processes method with a untracked proccess."""
        # add the fake process to the process list
        self.processes.append(dict(name="foo-untracked", group="untracked",
                                   pid="43", state=RUNNING))
        # add a new tracked process from an untracked group
        self.processes.append(dict(name="bar-untracked", group="bar", pid="44",
                                   state=RUNNING))
        self.listener.processes = ['bar']
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        with self.mocker:
            self.listener.check_processes()
        self.assertTrue(self.handler.check_info(
            "Ignoring untracked:foo-untracked (43) as isn't tracked."))
        self.assertTrue(self.handler.check_info(
            "Ignoring bar:bar-untracked (44) as isn't tracked."))

    def test_check_processes_not_running(self):
        """Test the check_processes method if the proccess isn't running."""
        # add the fake process to the process list
        self.processes.append(dict(name="foo", group="foo", pid="42",
                                   state=states.ProcessStates.STARTING))
        # add a new tracked process from an untracked group
        self.processes.append(dict(name="bar", group="bar", pid="43",
                                   state=states.ProcessStates.STARTING))
        self.listener.processes = ['bar']
        # 2 processes to restart
        self.listener.data['foo'] = {
            'time': time.time() - (self.listener.timeout + 2)}
        self.listener.data['bar'] = {
            'time': time.time() - (self.listener.timeout + 2)}
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        with self.mocker:
            self.listener.check_processes()
        self.assertTrue(self.handler.check_info(
            "Ignoring foo:foo (42) as isn't running."))
        self.assertTrue(self.handler.check_info(
            "Ignoring bar:bar (43) as isn't running."))

    def test_handle_heartbeat(self):
        """Test handle_heartbeat method."""
        payload = {"time": time.time()}
        self.listener.handle_heartbeat('process_name', 'group_name',
                                       '42', payload)
        info = {"pid": "42", "time": payload["time"],
                "received": self.listener.data["process_name"]["received"]}
        self.assertEqual({"process_name": info}, self.listener.data)

    def test_handle_event(self):
        """Test handle_event method."""
        # patch handle_heartbeat
        called = []

        def handle_heartbeat(process_name, group_name, pid, payload):
            """Fake handle_heartbeat."""
            called.append((process_name, group_name, pid, payload))

        self.listener.handle_heartbeat = handle_heartbeat
        payload_dict = {u"time": time.time(), "type": "heartbeat"}
        raw_data = ("processname:ticker groupname:ticker pid:42\n" +
                    json.dumps(payload_dict))
        raw_header = ("ver:3.0 server:supervisor serial:1 pool:listener "
                      "poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT"
                      " len:%s\n" % len(raw_data))
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        headers = childutils.get_headers(raw_header)
        self.listener._handle_event()
        # check
        self.assertEqual(1, len(called))
        del payload_dict['type']
        self.assertEqual(('ticker', 'ticker', '42', payload_dict), called[0])
        self.assertTrue(self.handler.check_debug(
            "Event '%s' received: %r" % (headers['eventname'], raw_data)))
        # check the stdout info
        self.assertEqual(["READY", "RESULT 2", "OK"],
                         self.stdout.getvalue().split("\n"))

    def test_invalid_event_type(self):
        """Test with an invalid type."""
        payload_dict = {u"time": time.time(), "type": "ping"}
        raw_data = 'processname:ticker groupname:ticker pid:42\n' + \
            json.dumps(payload_dict)
        raw_header = ("ver:3.0 server:supervisor serial:1 pool:listener "
                      "poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT"
                      " len:%s\n" % len(raw_data))
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        self.listener._handle_event()
        # check
        self.assertTrue(self.handler.check_error(
            "Unable to handle event type '%s' - %r" % ('ping', raw_data)))

    def test_invalid_payload(self):
        """Test with an invalid payload."""
        payload_dict = {u"time": time.time(), "type": "ping"}
        raw_data = 'processname:ticker groupname:ticker pid:42\n' + \
            json.dumps(payload_dict) + "<!foo>"
        raw_header = ("ver:3.0 server:supervisor serial:1 pool:listener "
                      "poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT"
                      " len:%s\n" % len(raw_data))
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        self.listener._handle_event()
        # check
        self.assertTrue(self.handler.check_error(
            "Unable to handle event type '%s' - %r" % ('None', raw_data)))

    def test_unhandled_event(self):
        """A unhandled event type."""
        payload_dict = {u"time": time.time(), "type": "ping"}
        raw_data = 'processname:ticker groupname:ticker pid:42\n' + \
            json.dumps(payload_dict)
        raw_header = "ver:3.0 server:supervisor serial:1 pool:heartbeat " + \
            "poolserial:1 eventname:UNKNOWN len:%s\n" % len(raw_data)
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        self.listener._handle_event()
        # check
        self.assertTrue(self.handler.check_warning(
            "Received unsupported event: %s - %r" % ('UNKNOWN', raw_data)))

    def test_check_interval(self):
        """Check that we properly check on the specified interval."""
        header = "ver:3.0 server:supervisor serial:1 pool:heartbeat " + \
                 "poolserial:1 eventname:TICK_5 len:0\n"
        expect(self.rpc.supervisor.getAllProcessInfo()).result([])
        self.stdin.write(header)
        self.stdin.seek(0)
        self.listener._handle_event()
        self.assertEqual(self.listener.tick_count, 1)
        self.stdin.seek(0)
        with self.mocker:
            self.listener._handle_event()
class SignalBroadcasterTestCase(TestCase):
    """Test the signal broadcaster code."""

    @defer.inlineCallbacks
    def setUp(self):
        yield super(SignalBroadcasterTestCase, self).setUp()
        self.client = FakeRemoteClient()
        self.sb = ipc.SignalBroadcaster()

        self.memento = MementoHandler()
        ipc.logger.addHandler(self.memento)
        ipc.logger.setLevel(logging.DEBUG)
        self.addCleanup(ipc.logger.removeHandler, self.memento)

    def test_remote_register_to_signals(self):
        """Assert that the client was added."""
        signals = ["demo_signal1", "demo_signal2"]
        self.sb.remote_register_to_signals(self.client, signals)
        for signal in signals:
            clients = self.sb.clients_per_signal[signal]
            self.assertTrue(self.client in clients)

    def test_emit_signal(self):
        """Assert that the client method was called."""
        first = 1
        second = 2
        word = 'word'
        signal_name = 'on_test'

        self.client.callRemote(signal_name, first, second, word=word)

        signals = [signal_name]
        self.sb.remote_register_to_signals(self.client, signals)
        self.sb.emit_signal(signal_name, first, second, foo=word)

        self.assertEqual(self.client.called, ((first, second), dict(foo=word)))

    def test_emit_signal_dead_reference(self):
        """Test dead reference while emitting a signal."""
        sample_signal = "sample_signal"
        fake_remote_client = FakeRemoteClient(dead_remote=True)

        self.sb.remote_register_to_signals(fake_remote_client, [sample_signal])
        self.assertIn(fake_remote_client,
                      self.sb.clients_per_signal[sample_signal])

        self.sb.emit_signal(sample_signal)
        self.assertNotIn(fake_remote_client,
                         self.sb.clients_per_signal[sample_signal])

    def test_emit_signal_some_dead_some_not(self):
        """Test a clean reference after a dead one."""
        sample_signal = "sample_signal"
        fake_dead_remote = FakeRemoteClient(dead_remote=True)
        fake_alive_remote = FakeRemoteClient()

        self.sb.remote_register_to_signals(fake_dead_remote, [sample_signal])
        self.sb.remote_register_to_signals(fake_alive_remote, [sample_signal])
        self.sb.emit_signal(sample_signal)

        self.assertTrue(fake_alive_remote.called, "The alive must be called.")

    def test_emit_signal_ignore_missing_handlers(self):
        """A missing signal handler should just log a debug line."""
        fake_remote_client = FakeRemoteClient()

        signal = fake_remote_client.missing_signal
        signals = [signal]
        self.sb.remote_register_to_signals(fake_remote_client, signals)
        sb_clients = self.sb.clients_per_signal[signal]
        self.assertIn(fake_remote_client, sb_clients)
        self.sb.emit_signal(signal)

        expected = ipc.SignalBroadcaster.MSG_NO_SIGNAL_HANDLER % (
            signal,
            fake_remote_client,
        )
        self.assertTrue(self.memento.check_debug(*expected))

    def test_emit_signal_log_other_errors(self):
        """Other errors should be logged as warnings."""
        fake_remote_client = FakeRemoteClient()

        signal = fake_remote_client.failing_signal
        signals = [signal]
        self.sb.remote_register_to_signals(fake_remote_client, signals)
        sb_clients = self.sb.clients_per_signal[signal]
        self.assertIn(fake_remote_client, sb_clients)
        self.sb.emit_signal(signal)

        expected = ipc.SignalBroadcaster.MSG_COULD_NOT_EMIT_SIGNAL % (
            signal,
            fake_remote_client,
            fake_remote_client.random_exception,
        )
        self.assertTrue(self.memento.check_warning(*expected))
Exemple #9
0
class ClientDummyAuthTests(AuthenticationBaseTestCase):
    """Client authentication tests using the dummy auth provider."""

    auth_provider_class = DummyAuthProvider

    @defer.inlineCallbacks
    def setUp(self):
        yield super(ClientDummyAuthTests, self).setUp()
        self.creds = 'open sesame'
        self.bad_creds = 'not my secret'
        self.handler = MementoHandler()
        logger = logging.getLogger('storage.server')
        logger.addHandler(self.handler)
        self.addCleanup(logger.removeHandler, self.handler)
        self.handler.setLevel(logging.DEBUG)

    def assert_auth_ok_logging(self):
        self.assertTrue(
            self.handler.check_debug("authenticated user", "OK",
                                     self.usr0.username))
        self.assertFalse(self.handler.check_warning("missing user"))

    def assert_auth_ok_missing_user(self):
        self.assertTrue(
            self.handler.check_debug("missing user", "(id=%s)" % self.usr0.id))
        self.assertFalse(self.handler.check_info("authenticated user"))

    @defer.inlineCallbacks
    def test_auth_ok_user_ok(self):
        """Correct authentication must succeed."""
        yield self.callback_test(self.do_auth,
                                 credentials=self.creds,
                                 add_default_callbacks=True)
        self.assert_auth_ok_logging()

    @defer.inlineCallbacks
    def test_auth_ok_bad_user(self):
        """Non existing user must fail authentication."""
        # make the user getter fail
        self.patch(self.service.factory.content, 'get_user_by_id',
                   lambda *a, **k: defer.fail(DoesNotExist()))

        d = self.callback_test(self.do_auth,
                               credentials=self.creds,
                               add_default_callbacks=True)
        yield self.assertFailure(d, protocol_errors.AuthenticationFailedError)

        self.assert_auth_ok_missing_user()

    @defer.inlineCallbacks
    def test_auth_ok_with_session_id(self):
        """Correct authentication must succeed and include the session_id."""
        auth_request = yield self.callback_test(self.do_auth,
                                                credentials=self.creds,
                                                add_default_callbacks=True)

        protocol = self.service.factory.protocols[0]
        self.assertEqual(auth_request.session_id, str(protocol.session_id))

    @defer.inlineCallbacks
    def test_auth_ok_with_metadata(self):
        """Correct authentication must succeed and include metadata."""
        m_called = []
        self.service.factory.metrics.meter = lambda *a: m_called.append(a)

        metadata = {u"platform": u"linux2", u"version": u"1.0", u"foo": u"bar"}
        yield self.callback_test(self.do_auth,
                                 credentials=self.creds,
                                 metadata=metadata,
                                 add_default_callbacks=True)

        self.assertTrue(
            self.handler.check_info("Client metadata: %s" % metadata))
        self.assertIn(("client.platform.linux2", 1), m_called)
        self.assertIn(("client.version.1_0", 1), m_called)
        self.assertNotIn(("client.foo.bar", 1), m_called)

    def test_auth_fail(self):
        """Wrong secret must fail."""
        def test(client, **kwargs):
            d = self.do_auth(client, credentials=self.bad_creds)
            d.addCallbacks(
                lambda _: client.test_fail(Exception("Should not succeed.")),
                lambda _: client.test_done("ok"))

        return self.callback_test(test)

    def test_get_root(self):
        """Must receive the root after authentication."""
        @defer.inlineCallbacks
        def test(client, **kwargs):
            yield self.do_auth(client, credentials=self.creds)
            root_id = yield client.get_root()
            self.assertIsNotNone(root_id)

        return self.callback_test(test, add_default_callbacks=True)

    def test_get_root_twice(self):
        """Get root must keep the root id."""
        @defer.inlineCallbacks
        def test(client, **kwargs):
            yield self.do_auth(client, credentials=self.creds)
            root_id1 = yield client.get_root()
            root_id2 = yield client.get_root()
            self.assertEqual(root_id1, root_id2)

        return self.callback_test(test, add_default_callbacks=True)

    def test_user_becomes_inactive(self):
        """After StorageUser authentication ok it becomes inactive."""
        @defer.inlineCallbacks
        def test(client):
            """Test."""
            yield self.do_auth(client, credentials=self.creds)
            root_id = yield client.get_root()

            # create one file, should be ok
            yield client.make_file(request.ROOT, root_id, "f1")

            # cancel user subscription, so it needs
            # to get it again from the DB
            self.usr0.update(subscription=False)

            # create second file, should NOT be ok
            try:
                yield client.make_file(request.ROOT, root_id, "f2")
            except protocol_errors.DoesNotExistError:
                pass  # failed as we expected
            else:
                client.test_fail("It should have failed!")

        return self.callback_test(test, add_default_callbacks=True)
class NodeStructureTestCase(unittest.TestCase):
    """Tests that we store the node commands ok."""

    def setUp(self):
        """Set up the test."""
        self.qc = QueueContent(home='/')
        self.handler = MementoHandler()
        self.handler.setLevel(logging.DEBUG)
        logger = logging.getLogger('magicicada.queue_content')
        logger.addHandler(self.handler)
        logger.setLevel(logging.DEBUG)
        self.addCleanup(logger.removeHandler, self.handler)

    def test_one_node_file(self):
        """Add one node with a file op."""
        self.qc.set_content([('MakeFile', '123', {'path': '/a/b/foo'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertEqual(node.last_modified, None)
        self.assertEqual(node.kind, KIND_DIR)
        self.assertEqual(node.operations, [])
        self.assertEqual(node.done, None)
        self.assertEqual(len(node.children), 1)

        node = node.children['b']
        self.assertEqual(node.last_modified, None)
        self.assertEqual(node.kind, KIND_DIR)
        self.assertEqual(node.operations, [])
        self.assertEqual(node.done, None)
        self.assertEqual(len(node.children), 1)

        node = node.children['foo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        expected = [('123', 'MakeFile',
                     {'path': '/a/b/foo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

    def test_one_node_dir(self):
        """Add one node with a dir op."""
        self.qc.set_content([('MakeDir', '123', {'path': '/a/boo'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertEqual(node.last_modified, None)
        self.assertEqual(node.kind, KIND_DIR)
        self.assertEqual(node.operations, [])
        self.assertEqual(node.done, None)
        self.assertEqual(len(node.children), 1)

        node = node.children['boo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('123', 'MakeDir', {'path': '/a/boo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

    def test_one_node_unknown(self):
        """Add one node with a unknown op."""
        self.qc.set_content([('Unlink', '123', {'path': '/a/boo'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertEqual(node.last_modified, None)
        self.assertEqual(node.kind, KIND_DIR)
        self.assertEqual(node.operations, [])
        self.assertEqual(node.done, None)
        self.assertEqual(len(node.children), 1)

        node = node.children['boo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_UNKNOWN)
        expected = [('123', 'Unlink', {'path': '/a/boo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

    def test_one_node_known_unknown(self):
        """Add one node known op with a later unknown op."""
        self.qc.set_content([('MakeFile', '123', {'path': '/a'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        expected = [('123', 'MakeFile', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        self.qc.set_content([('Unlink', '456', {'path': '/a'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        expected = [('123', 'MakeFile', {'path': '/a', '__done__': False}),
                    ('456', 'Unlink', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

    def test_one_node_unknown_known(self):
        """Add one node unknown op with a later known op."""
        self.qc.set_content([('Unlink', '123', {'path': '/a'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_UNKNOWN)
        expected = [('123', 'Unlink', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        self.qc.set_content([('MakeDir', '456', {'path': '/a'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('123', 'Unlink', {'path': '/a', '__done__': False}),
                    ('456', 'MakeDir', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

    def test_several_nodes_mixed(self):
        """Add some nodes with different combinations."""
        # add /a/b
        self.qc.set_content([('MakeDir', '12', {'path': '/a/b'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        node = self.qc._node_ops[''].children['a']
        self.assertEqual(node.last_modified, None)
        self.assertEqual(node.kind, KIND_DIR)
        self.assertEqual(node.operations, [])
        self.assertEqual(node.done, None)
        self.assertEqual(len(node.children), 1)

        node = node.children['b']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('12', 'MakeDir', {'path': '/a/b', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # add /a/b/foo
        self.qc.set_content([('MakeDir', '34', {'path': '/a/b/foo'})])
        node = self.qc._node_ops[''].children['a']

        node = node.children['b']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('12', 'MakeDir', {'path': '/a/b', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 1)

        node = node.children['foo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('34', 'MakeDir', {'path': '/a/b/foo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # add /a/b/bar
        self.qc.set_content([('MakeDir', '45', {'path': '/a/b/bar'})])
        node = self.qc._node_ops[''].children['a']

        node = node.children['b']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('12', 'MakeDir', {'path': '/a/b', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 2)

        node = node.children['bar']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('45', 'MakeDir', {'path': '/a/b/bar', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # add /a/b/foo/fighters
        self.qc.set_content([('MakeFile', '67',
                              {'path': '/a/b/foo/fighters'})])
        node = self.qc._node_ops[''].children['a']

        node = node.children['b']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('12', 'MakeDir', {'path': '/a/b', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 2)

        node = node.children['foo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('34', 'MakeDir', {'path': '/a/b/foo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 1)

        node = node.children['fighters']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        expected = [('67', 'MakeFile',
                     {'path': '/a/b/foo/fighters', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # other /a/b/foo/fighters
        self.qc.set_content([('Unlink', '89', {'path': '/a/b/foo/fighters'})])
        node = self.qc._node_ops[''].children['a']

        node = node.children['b']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('12', 'MakeDir', {'path': '/a/b', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 2)

        node = node.children['foo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('34', 'MakeDir', {'path': '/a/b/foo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 1)

        node = node.children['fighters']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        p = '/a/b/foo/fighters'
        expected = [('67', 'MakeFile', {'path': p, '__done__': False}),
                    ('89', 'Unlink', {'path': p, '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

    def test_finishing_nothing(self):
        """Finish something that is not there."""
        r = self.qc.remove('MakeDir', '34', {'path': '/a/bar'})
        self.assertEqual(r, None)
        self.assertTrue(self.handler.check_warning(
                        "Element ''", "['', 'a', 'bar']", 'not in children'))

    def test_operation_error_nothing(self):
        """Finish an operation that is not there."""
        # create a node and break it on purpose
        self.qc.add('MakeDir', '12', {'path': '/a'})
        self.assertEqual(len(self.qc._node_ops), 1)
        node = self.qc._node_ops[''].children['a']
        node.operations = []

        # remove the operation and check
        r = self.qc.remove('MakeDir', '12', {'path': '/a'})
        self.assertEqual(r, None)
        self.assertTrue(self.handler.check_error(
                        "found 0 times", "MakeDir", "12"))

    def test_operation_error_several(self):
        """Finish an operation that is more than once."""
        # create a node and break it on purpose
        self.qc.add('MakeDir', '12', {'path': '/a'})
        self.assertEqual(len(self.qc._node_ops), 1)
        node = self.qc._node_ops[''].children['a']
        node.operations = node.operations * 2

        # remove the operation and check
        r = self.qc.remove('MakeDir', '12', {'path': '/a'})
        self.assertEqual(r, None)
        self.assertTrue(self.handler.check_error(
                        "found 2 times", "MakeDir", "12"))

    def test_two_ops_finishing_one(self):
        """Add some nodes with different combinations."""
        # create two dirs
        self.qc.set_content([('MakeDir', '12', {'path': '/a/foo'}),
                             ('MakeDir', '34', {'path': '/a/bar'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        # all inited properly
        root = self.qc._node_ops[''].children['a']
        self.assertEqual(root.last_modified, None)
        self.assertEqual(root.kind, KIND_DIR)
        self.assertEqual(root.operations, [])
        self.assertEqual(root.done, None)
        self.assertEqual(len(root.children), 2)

        node = root.children['foo']
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('12', 'MakeDir', {'path': '/a/foo', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        node = root.children['bar']
        bar_created_timestamp = node.last_modified
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('34', 'MakeDir', {'path': '/a/bar', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # finish the second make dir and check again
        self.qc.remove('MakeDir', '34', {'path': '/a/bar'})
        self.assertTrue(node.last_modified > bar_created_timestamp)
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_DIR)
        expected = [('34', 'MakeDir', {'path': '/a/bar', '__done__': True})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, True)
        self.assertEqual(len(node.children), 0)

    def test_two_ops_finishing_both(self):
        """Add two ops to the same node and finish both."""
        # create two dirs
        self.qc.set_content([('MakeFile', '12', {'path': '/a'}),
                             ('Upload', '34', {'path': '/a'})])
        self.assertEqual(len(self.qc._node_ops), 1)

        # all inited properly
        node = self.qc._node_ops[''].children['a']
        node_created_tstamp = node.last_modified
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        expected = [('12', 'MakeFile', {'path': '/a', '__done__': False}),
                    ('34', 'Upload', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # finish one
        self.qc.remove('MakeFile', '12', {'path': '/a'})
        node_changed_tstamp = node.last_modified
        self.assertTrue(node.last_modified > node_created_tstamp)
        expected = [('12', 'MakeFile', {'path': '/a', '__done__': True}),
                    ('34', 'Upload', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)

        # finish the second
        self.qc.remove('Upload', '34', {'path': '/a'})
        self.assertTrue(node.last_modified > node_changed_tstamp)
        expected = [('12', 'MakeFile', {'path': '/a', '__done__': True}),
                    ('34', 'Upload', {'path': '/a', '__done__': True})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, True)

    def test_one_op_finishes_startsagain(self):
        """Add an op, finish it, add another one."""
        self.qc.add('MakeFile', '12', {'path': '/a'})
        self.assertEqual(len(self.qc._node_ops), 1)

        # all inited properly
        node = self.qc._node_ops[''].children['a']
        node_created_tstamp = node.last_modified
        self.assertTrue(isinstance(node.last_modified, float))
        self.assertEqual(node.kind, KIND_FILE)
        expected = [('12', 'MakeFile', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
        self.assertEqual(len(node.children), 0)

        # finish the op
        self.qc.remove('MakeFile', '12', {'path': '/a'})
        node_changed_tstamp = node.last_modified
        self.assertTrue(node.last_modified > node_created_tstamp)
        expected = [('12', 'MakeFile', {'path': '/a', '__done__': True})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, True)

        # send other one to the same node
        self.qc.add('Upload', '34', {'path': '/a'})
        self.assertTrue(node.last_modified > node_changed_tstamp)
        expected = [('34', 'Upload', {'path': '/a', '__done__': False})]
        self.assertEqual(node.operations, expected)
        self.assertEqual(node.done, False)
class ReactorInspectorTestCase(TwistedTestCase):
    """Test the ReactorInspector class."""

    def setUp(self):
        """Set up."""
        class Helper(object):
            """Fake object with a controllable call."""
            def __init__(self):
                self.call_count = 1
                self.calls = []
                self.ri = None

            def call(self, func):
                """Call function when counter is 0, then stop running."""
                self.call_count -= 1
                self.calls.append(func)
                if self.call_count == 0:
                    for f in self.calls:
                        f()
                if self.call_count <= 0:
                    self.ri.stop()

        class FakeMetrics(object):
            """Fake Metrics object that records calls."""
            def __init__(self):
                """Initialize calls."""
                self.calls = []

            def meter(self, name, count):
                """Record call to meter()."""
                self.calls.append(("meter", name, count))

            def gauge(self, name, val):
                """Record call to gauge()."""
                self.calls.append(("gauge", name, val))

        logger = logging.getLogger("storage.server")
        logger.propagate = False
        logger.setLevel(TRACE)
        self.handler = MementoHandler()
        self.handler.setLevel(TRACE)
        logger.addHandler(self.handler)
        self.addCleanup(logger.removeHandler, self.handler)
        self.helper = Helper()
        self.fake_metrics = FakeMetrics()
        MetricsConnector.register_metrics("reactor_inspector",
                                          instance=self.fake_metrics)
        self.addCleanup(MetricsConnector.unregister_metrics)
        self.ri = ReactorInspector(logger, self.helper.call, loop_time=.1)
        self.helper.ri = self.ri

    def run_ri(self, call_count=None, join=True):
        """Set the call count and then run the ReactorInspector."""
        if call_count is not None:
            self.helper.call_count = call_count
        self.start_ts = time.time()
        self.ri.start()
        # Reactor will stop after call_count calls, thanks to helper
        if join:
            self.ri.join()

    def test_stop(self):
        """It stops."""
        self.run_ri(1000, join=False)
        assert self.ri.is_alive()
        self.ri.stop()
        self.ri.join()
        self.assertFalse(self.ri.is_alive())

    @defer.inlineCallbacks
    def test_dump_frames(self):
        """Test how frames are dumped.

        Rules:
        - own frame must not be logged
        - must log all other threads
        - main reactor thread must have special title
        """
        # other thread, whose frame must be logged
        waitingd = defer.Deferred()

        def waiting_function():
            """Function with funny name to be checked later."""
            reactor.callFromThread(waitingd.callback, True)
            # wait have a default value
            event.wait()

        event = threading.Event()
        threading.Thread(target=waiting_function).start()
        # Make sure the thread has entered the waiting_function
        yield waitingd

        # Set reactor_thread since we're not starting the ReactorInspector
        # thread here.
        self.ri.reactor_thread = threading.currentThread().ident

        # dump frames in other thread, also
        def dumping_function():
            """Function with funny name to be checked later."""
            time.sleep(.1)
            self.ri.dump_frames()
            reactor.callFromThread(d.callback, True)

        d = defer.Deferred()
        threading.Thread(target=dumping_function).start()
        yield d
        event.set()

        # check
        self.assertFalse(self.handler.check_debug("dumping_function"))
        self.assertTrue(self.handler.check_debug("Dumping Python frame",
                                                 "waiting_function"))
        self.assertTrue(self.handler.check_debug("Dumping Python frame",
                                                 "reactor main thread"))

    def test_reactor_ok(self):
        """Reactor working fast."""
        self.run_ri()
        ok_line = self.handler.check(TRACE, "ReactorInspector: ok")
        self.assertTrue(ok_line)
        self.assertTrue(ok_line.args[-1] >= 0)  # Should be near zero delay
        # Check the metrics
        expected_metric = ("gauge", "delay", ok_line.args[-1])
        self.assertEqual([expected_metric], self.fake_metrics.calls)
        self.assertTrue(self.ri.last_responsive_ts >= self.start_ts)

    @defer.inlineCallbacks
    def test_reactor_blocked(self):
        """Reactor not working fast."""
        dump_frames_called = defer.Deferred()
        self.ri.dump_frames = lambda: dump_frames_called.callback(True)
        self.run_ri(0)
        yield dump_frames_called
        log_line = self.handler.check(logging.CRITICAL, "ReactorInspector",
                                      "detected unresponsive")
        self.assertTrue(log_line)
        self.assertTrue(log_line.args[-1] >= .1)  # waited for entire loop time
        # Check the metrics
        expected_metric = ("gauge", "delay", log_line.args[-1])
        self.assertEqual([expected_metric], self.fake_metrics.calls)

        self.assertTrue(self.ri.last_responsive_ts < self.start_ts)

    def test_reactor_back_alive(self):
        """Reactor resurrects after some loops."""
        self.run_ri(3)
        late_line = self.handler.check_warning("ReactorInspector: late",
                                               "got: 0")
        self.assertTrue(late_line)
        self.assertTrue(late_line.args[-1] >= .2)  # At least 2 cycles of delay
        # Check the metrics
        expected_metric = ("gauge", "delay", late_line.args[-1])
        self.assertEqual(expected_metric, self.fake_metrics.calls[-1])

        self.assertTrue(self.ri.queue.empty())
        # A late reactor is not considered responsive (until a successful loop)
        self.assertTrue(self.ri.last_responsive_ts < self.start_ts)
class OffloadQueueTestCase(TwistedTestCase):
    """Tests the OffloadQueue class."""

    def setUp(self):
        """Set up."""
        self.handler = MementoHandler()
        self.handler.setLevel(logging.DEBUG)
        logger = logging.getLogger("ubuntuone.SyncDaemon.OffloadQueue")
        logger.setLevel(logging.DEBUG)
        logger.addHandler(self.handler)
        self.addCleanup(logger.removeHandler, self.handler)
        self.oq = OffloadQueue()
        self.addCleanup(self.oq._tempfile.close)
        return super(OffloadQueueTestCase, self).setUp()

    def test_serialization_tuple(self):
        """Check that it can store tuples of strings."""
        data = ("foo", "bar")
        self.oq.push(data)
        retrieved = self.oq.pop()
        self.assertEqual(data, retrieved)

    def test_serialization_markers(self):
        """Check that it can store markers."""
        marker = MDMarker("foo")
        self.oq.push(marker)
        retrieved = self.oq.pop()
        self.assertTrue(IMarker.providedBy(retrieved))

    def test_fifo_simple(self):
        """Check FIFO queue with one silly value."""
        data = "data"
        self.oq.push(data)
        self.assertEqual(len(self.oq), 1)
        retrieved = self.oq.pop()
        self.assertEqual(data, retrieved)
        self.assertEqual(len(self.oq), 0)

    def test_fifo_double(self):
        """Check FIFO queue with two values."""
        data1, data2 = "data1", "data2"
        self.oq.push(data1)
        self.oq.push(data2)
        self.assertEqual(len(self.oq), 2)
        retrieved = self.oq.pop()
        self.assertEqual(data1, retrieved)
        self.assertEqual(len(self.oq), 1)
        retrieved = self.oq.pop()
        self.assertEqual(data2, retrieved)
        self.assertEqual(len(self.oq), 0)

    def test_fifo_mixed(self):
        """Check FIFO queue with more values."""
        data1, data2, data3 = "data1", "data2", "data3"
        self.oq.push(data1)
        self.oq.push(data2)
        self.assertEqual(data1, self.oq.pop())
        self.oq.push(data3)
        self.assertEqual(data2, self.oq.pop())
        self.assertEqual(data3, self.oq.pop())

    def test_rotate_limit_not_reached(self):
        """File does not rotate if limits are not reached."""
        orig_temp = self.oq._tempfile
        self.oq.push("data")
        self.assertEqual(self.oq._tempfile, orig_temp)
        self.oq.pop()
        self.assertEqual(self.oq._tempfile, orig_temp)

    def _get_data(self, data="data"):
        """Return data to store and it's item size in disk."""
        pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
        item_size = len(pickled) + STRUCT_SIZE
        return data, item_size

    def test_rotate_soft_limit_on_push(self):
        """Rotation happens with soft limit on push."""
        # set and check rotation limits
        data, item_size = self._get_data()
        self.oq._rotation_soft_limit = item_size * 2.5
        assert self.oq._rotation_too_big_size > item_size * 10
        assert self.oq._rotation_hard_limit > item_size * 10

        # put two items, removing one so we can save space on rotation
        orig_temp = self.oq._tempfile
        self.oq.push(data)
        self.oq.pop()
        self.oq.push(data)
        self.assertEqual(self.oq._tempfile, orig_temp)

        # push another, now we're over again, but now we can save space
        self.oq.push(data)
        self.assertNotEqual(self.oq._tempfile, orig_temp)

    def test_rotate_soft_limit_on_pop(self):
        """Rotation happens with soft limit on pop."""
        # set and check rotation limits
        data, item_size = self._get_data()
        self.oq._rotation_soft_limit = item_size * 2.5
        assert self.oq._rotation_too_big_size > item_size * 10
        assert self.oq._rotation_hard_limit > item_size * 10

        # put four items
        orig_temp = self.oq._tempfile
        self.oq.push(data)
        self.oq.push(data)
        self.oq.push(data)
        self.oq.push(data)
        self.assertEqual(self.oq._tempfile, orig_temp)

        # pop the first one, we make room and still have more than soft limit
        # and min size, so we rotate
        self.oq.pop()
        self.assertNotEqual(self.oq._tempfile, orig_temp)

    def test_rotate_too_much_data(self):
        """Soft rotation doesn't happen if we have more than the max size."""
        # set and check rotation limits
        data, item_size = self._get_data()
        self.oq._rotation_soft_limit = item_size * 2.5
        self.oq._rotation_too_big_size = item_size
        assert self.oq._rotation_hard_limit > item_size * 10

        # put two items, removing one so we can save space on rotation
        orig_temp = self.oq._tempfile
        self.oq.push(data)
        self.oq.pop()
        self.oq.push(data)
        self.assertEqual(self.oq._tempfile, orig_temp)

        # push another, now we're over but we have too much data to move,
        # so rotation should not happen
        self.oq.push(data)
        self.assertEqual(self.oq._tempfile, orig_temp)

    def test_rotate_hard_limit(self):
        """We rotate on hard limit, no matter what."""
        # set and check rotation limits
        data, item_size = self._get_data()
        self.oq._rotation_soft_limit = item_size * 2.5
        self.oq._rotation_too_big_size = item_size
        self.oq._rotation_hard_limit = item_size * 3.5

        # put two items, removing one so we can save space on rotation
        orig_temp = self.oq._tempfile
        self.oq.push(data)
        self.oq.pop()
        self.oq.push(data)
        self.assertEqual(self.oq._tempfile, orig_temp)

        # push another, now we're over but we have too much data to move,
        # so rotation should not happen
        self.oq.push(data)
        self.assertEqual(self.oq._tempfile, orig_temp)

        # push another one, and check that after going over the hard limit
        # it will rotate no matter what
        self.oq.push(data)
        self.assertNotEqual(self.oq._tempfile, orig_temp)

    def test_rotate_keep_working(self):
        """Just check that all is normal after rotation."""
        data = []
        size = 0
        for i in xrange(10):
            d, s = self._get_data("data" + str(i))
            data.append(d)
            size += s
        self.oq._rotation_soft_limit = size * 0.7
        orig_temp = self.oq._tempfile

        # put one item and remove just to make it rotable
        results = []
        self.oq.push(data[0])
        results.append(self.oq.pop())

        # push the rest of the data, it should rotate at some point
        for d in data[1:]:
            self.oq.push(d)
        assert self.oq._tempfile != orig_temp

        # pop everything and compare
        while len(self.oq):
            results.append(self.oq.pop())
        self.assertEqual(data, results)

    def test_rotate_removes_old_file(self):
        """Rotation should start a new file and remove the previous one."""
        data, item_size = self._get_data()
        self.oq._rotation_soft_limit = item_size * 2.5
        orig_fname = self.oq._tempfile_name

        self.oq.push(data)
        self.oq.pop()
        self.oq.push(data)
        self.oq.push(data)

        self.assertFalse(os.path.exists(orig_fname))

    def test_log_init_tempfile(self):
        """Log the initial temp file used."""
        self.assertTrue(self.handler.check_debug("Using temporary file", repr(self.oq._tempfile_name)))

    def test_log_rotate(self):
        """Log new file in rotation."""
        data, item_size = self._get_data()
        self.oq._rotation_soft_limit = item_size * 2.5

        self.oq.push(data)
        self.oq.pop()
        self.oq.push(data)
        self.oq.push(data)

        self.assertTrue(self.handler.check_debug("Rotation into", "moving", repr(self.oq._tempfile_name)))

    def test_safe_rotate_crash(self):
        """All is ok even after rotation crashes when getting temp file."""

        def crash(*a):
            """Will crash."""
            raise NameError("ugly")

        self.patch(tempfile, "mkstemp", crash)

        # do a lot of things, rotating in the middle, checking all is ok
        self.test_rotate_keep_working()
        self.assertTrue(self.handler.check_exception(NameError))
        self.assertTrue(self.oq._in_memory)

    def test_safe_rotate_unlink(self):
        """All is ok after failing to unlink old file."""

        def crash(*a):
            """Will crash."""
            raise NameError("ugly")

        self.patch(os, "unlink", crash)

        # do a lot of things, rotating in the middle, checking all is ok
        self.test_rotate_keep_working()
        self.assertTrue(self.handler.check_warning("Error when removing old tempfile", "NameError"))

    def _test_safe_push_write(self, count):
        """Fail when pushing an item will leave it all ok."""

        class CrashingFile(StringIO.StringIO):
            """File-like object that crashes in second write."""

            def __init__(self):
                self._fail_counter = 0
                StringIO.StringIO.__init__(self)

            def write(self, *a):
                """Crashing write."""
                self._fail_counter += 1
                if self._fail_counter == count:
                    raise ValueError("broken")
                else:
                    StringIO.StringIO.write(self, *a)

        self.oq._tempfile = CrashingFile()

        # will try three items, checking all is ok
        self.test_fifo_mixed()
        self.assertTrue(self.handler.check_exception(ValueError))
        self.assertTrue(self.oq._in_memory)

    def test_safe_push_write_first(self):
        """Fail when pushing an item, on first write."""
        self._test_safe_push_write(1)

    def test_safe_push_write_second(self):
        """Fail when pushing an item, on second write."""
        self._test_safe_push_write(2)
class HeartbeatListenerTestCase(TestCase):
    """Tests for HeartbeatListener class."""
    def setUp(self):
        super(HeartbeatListenerTestCase, self).setUp()
        self.stdin = StringIO()
        self.stdout = StringIO()
        self.stderr = StringIO()
        self.mocker = Mocker()
        self.rpc = self.mocker.mock()
        self.listener = HeartbeatListener(1,
                                          10, ['foo'], [],
                                          self.rpc,
                                          stdin=self.stdin,
                                          stdout=self.stdout,
                                          stderr=self.stderr)
        self.next_fail = {}
        self.handler = MementoHandler()
        self.listener.logger.addHandler(self.handler)
        self.listener.logger.setLevel(logging.DEBUG)
        self.handler.setLevel(logging.DEBUG)
        self.listener.logger.propagate = False
        self.processes = [
            dict(name="heartbeat", group="heartbeat", pid="101", state=RUNNING)
        ]
        self.handler.debug = True

    def tearDown(self):
        self.listener.logger.removeHandler(self.handler)
        self.handler.close()
        self.next_fail = None
        self.handler = None
        self.listener = None
        super(HeartbeatListenerTestCase, self).tearDown()

    def fail_next_stop(self, pname):
        """Make next stopProcess to fail."""
        expect(self.rpc.supervisor.stopProcess(pname)).throw(
            xmlrpclib.Fault(42, "Failed to stop the process."))

    def fail_next_start(self, pname):
        """Make next startProcess to fail."""
        expect(self.rpc.supervisor.startProcess(pname)).throw(
            xmlrpclib.Fault(42, "Failed to start the process."))

    def test_restart(self):
        """Test the restart method."""
        expect(self.rpc.supervisor.stopProcess("foo"))
        expect(self.rpc.supervisor.startProcess("foo"))
        with self.mocker:
            self.listener.restart("foo", "testing")
        self.assertTrue(
            self.handler.check_info("Restarting foo (last "
                                    "hearbeat: testing)"))

    def test_restart_fail_stop(self):
        """Test the restart method failing to stop the process."""
        self.fail_next_stop("foo")
        last = time.time()
        with self.mocker:
            try:
                self.listener.restart("foo", last)
            except xmlrpclib.Fault:
                msg = ("Failed to stop process %s (last heartbeat: %s), "
                       "exiting: %s") % \
                    ("foo", last, "<Fault 42: 'Failed to stop the process.'>")
                self.assertTrue(self.handler.check_error(msg))
            else:
                self.fail("Should get an xmlrpclib.Fault")

    def test_restart_fail_start(self):
        """Test the restart method failing to start the process."""
        expect(self.rpc.supervisor.stopProcess("foo"))
        self.fail_next_start("foo")
        last = time.time()
        with self.mocker:
            try:
                self.listener.restart("foo", last)
            except xmlrpclib.Fault:
                msg = (
                    'Failed to start process %s after stopping it, exiting: %s'
                ) % ("foo", "<Fault 42: 'Failed to start the process.'>")
                self.assertTrue(self.handler.check_error(msg))
            else:
                self.fail("Should get an xmlrpclib.Fault")

    def test_check_processes(self):
        """Test the check_processes method."""
        # add the fake process to the process list
        self.processes.append(
            dict(name="foo", group="foo", pid="42", state=RUNNING))
        self.processes.append(
            dict(name="bar", group="bar", pid="43", state=RUNNING))
        self.listener.processes = ['bar']
        # 2 process to restart
        self.listener.data['foo'] = {
            'time': time.time() - (self.listener.timeout + 2)
        }
        self.listener.data['bar'] = {
            'time': time.time() - (self.listener.timeout + 3)
        }
        self.listener.data['p-1'] = {
            'time': time.time() - (self.listener.timeout - 1)
        }
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        expect(self.rpc.supervisor.stopProcess("foo:"))
        expect(self.rpc.supervisor.startProcess("foo:"))
        expect(self.rpc.supervisor.stopProcess("bar:bar"))
        expect(self.rpc.supervisor.startProcess("bar:bar"))
        with self.mocker:
            self.listener.check_processes()

    def test_check_processes_no_data(self):
        """Test the check_processes method with no data of a process."""
        # add the fake process to the process list
        self.processes.append(
            dict(name="foo", group="foo", pid="42", state=RUNNING))
        self.processes.append(
            dict(name="bar", group="bar", pid="43", state=RUNNING))
        self.listener.processes = ['bar']
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        expect(self.rpc.supervisor.stopProcess("foo:"))
        expect(self.rpc.supervisor.startProcess("foo:"))
        expect(self.rpc.supervisor.stopProcess("bar:bar"))
        expect(self.rpc.supervisor.startProcess("bar:bar"))
        with self.mocker:
            # one process to restart
            self.listener.check_processes()
        self.assertTrue(
            self.handler.check_warning(
                "Restarting process foo:foo (42), as we never received a hearbeat"
                " event from it"))
        self.assertTrue(
            self.handler.check_warning(
                "Restarting process bar:bar (43), as we never received a hearbeat"
                " event from it"))

    def test_check_processes_untracked(self):
        """Test the check_processes method with a untracked proccess."""
        # add the fake process to the process list
        self.processes.append(
            dict(name="foo-untracked",
                 group="untracked",
                 pid="43",
                 state=RUNNING))
        # add a new tracked process from an untracked group
        self.processes.append(
            dict(name="bar-untracked", group="bar", pid="44", state=RUNNING))
        self.listener.processes = ['bar']
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        with self.mocker:
            self.listener.check_processes()
        self.assertTrue(
            self.handler.check_info(
                "Ignoring untracked:foo-untracked (43) as isn't tracked."))
        self.assertTrue(
            self.handler.check_info(
                "Ignoring bar:bar-untracked (44) as isn't tracked."))

    def test_check_processes_not_running(self):
        """Test the check_processes method if the proccess isn't running."""
        # add the fake process to the process list
        self.processes.append(
            dict(name="foo",
                 group="foo",
                 pid="42",
                 state=states.ProcessStates.STARTING))
        # add a new tracked process from an untracked group
        self.processes.append(
            dict(name="bar",
                 group="bar",
                 pid="43",
                 state=states.ProcessStates.STARTING))
        self.listener.processes = ['bar']
        # 2 processes to restart
        self.listener.data['foo'] = {
            'time': time.time() - (self.listener.timeout + 2)
        }
        self.listener.data['bar'] = {
            'time': time.time() - (self.listener.timeout + 2)
        }
        expect(self.rpc.supervisor.getAllProcessInfo()).result(self.processes)
        with self.mocker:
            self.listener.check_processes()
        self.assertTrue(
            self.handler.check_info("Ignoring foo:foo (42) as isn't running."))
        self.assertTrue(
            self.handler.check_info("Ignoring bar:bar (43) as isn't running."))

    def test_handle_heartbeat(self):
        """Test handle_heartbeat method."""
        payload = {"time": time.time()}
        self.listener.handle_heartbeat('process_name', 'group_name', '42',
                                       payload)
        info = {
            "pid": "42",
            "time": payload["time"],
            "received": self.listener.data["process_name"]["received"]
        }
        self.assertEqual({"process_name": info}, self.listener.data)

    def test_handle_event(self):
        """Test handle_event method."""
        # patch handle_heartbeat
        called = []

        def handle_heartbeat(process_name, group_name, pid, payload):
            """Fake handle_heartbeat."""
            called.append((process_name, group_name, pid, payload))

        self.listener.handle_heartbeat = handle_heartbeat
        payload_dict = {u"time": time.time(), "type": "heartbeat"}
        raw_data = ("processname:ticker groupname:ticker pid:42\n" +
                    json.dumps(payload_dict))
        raw_header = ("ver:3.0 server:supervisor serial:1 pool:listener "
                      "poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT"
                      " len:%s\n" % len(raw_data))
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        headers = childutils.get_headers(raw_header)
        self.listener._handle_event()
        # check
        self.assertEqual(1, len(called))
        del payload_dict['type']
        self.assertEqual(('ticker', 'ticker', '42', payload_dict), called[0])
        self.assertTrue(
            self.handler.check_debug("Event '%s' received: %r" %
                                     (headers['eventname'], raw_data)))
        # check the stdout info
        self.assertEqual(["READY", "RESULT 2", "OK"],
                         self.stdout.getvalue().split("\n"))

    def test_invalid_event_type(self):
        """Test with an invalid type."""
        payload_dict = {u"time": time.time(), "type": "ping"}
        raw_data = 'processname:ticker groupname:ticker pid:42\n' + \
            json.dumps(payload_dict)
        raw_header = ("ver:3.0 server:supervisor serial:1 pool:listener "
                      "poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT"
                      " len:%s\n" % len(raw_data))
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        self.listener._handle_event()
        # check
        self.assertTrue(
            self.handler.check_error("Unable to handle event type '%s' - %r" %
                                     ('ping', raw_data)))

    def test_invalid_payload(self):
        """Test with an invalid payload."""
        payload_dict = {u"time": time.time(), "type": "ping"}
        raw_data = 'processname:ticker groupname:ticker pid:42\n' + \
            json.dumps(payload_dict) + "<!foo>"
        raw_header = ("ver:3.0 server:supervisor serial:1 pool:listener "
                      "poolserial:10 eventname:PROCESS_COMMUNICATION_STDOUT"
                      " len:%s\n" % len(raw_data))
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        self.listener._handle_event()
        # check
        self.assertTrue(
            self.handler.check_error("Unable to handle event type '%s' - %r" %
                                     ('None', raw_data)))

    def test_unhandled_event(self):
        """A unhandled event type."""
        payload_dict = {u"time": time.time(), "type": "ping"}
        raw_data = 'processname:ticker groupname:ticker pid:42\n' + \
            json.dumps(payload_dict)
        raw_header = "ver:3.0 server:supervisor serial:1 pool:heartbeat " + \
            "poolserial:1 eventname:UNKNOWN len:%s\n" % len(raw_data)
        self.stdin.write(raw_header + raw_data)
        self.stdin.seek(0)
        self.listener._handle_event()
        # check
        self.assertTrue(
            self.handler.check_warning("Received unsupported event: %s - %r" %
                                       ('UNKNOWN', raw_data)))

    def test_check_interval(self):
        """Check that we properly check on the specified interval."""
        header = "ver:3.0 server:supervisor serial:1 pool:heartbeat " + \
                 "poolserial:1 eventname:TICK_5 len:0\n"
        expect(self.rpc.supervisor.getAllProcessInfo()).result([])
        self.stdin.write(header)
        self.stdin.seek(0)
        self.listener._handle_event()
        self.assertEqual(self.listener.tick_count, 1)
        self.stdin.seek(0)
        with self.mocker:
            self.listener._handle_event()