class TestFSLegacyDatedRadixTreeStorage(TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"
    CRASH_ID_3 = "0bba929f-8721-460c-dddd-a43c20071025"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option('logger', default=mock_logging)
        config_manager = ConfigurationManager(
            [required_config],
            app_name='testapp',
            app_version='1.0',
            app_description='app description',
            values_source_list=[{
                'logger': mock_logging,
                'minute_slice_interval': 1
            }],
            argv_source=[])
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash({"test": "TEST"},
                                  MemoryDumpsMapping({
                                      'foo':
                                      'bar',
                                      self.fsrts.config.dump_field:
                                      'baz'
                                  }), self.CRASH_ID_1)

    def _make_test_crash_3(self):
        self.fsrts.save_raw_crash({"test": "TEST"},
                                  MemoryDumpsMapping({
                                      'foo':
                                      'bar',
                                      self.fsrts.config.dump_field:
                                      'baz'
                                  }), self.CRASH_ID_3)

    def test_save_raw_crash(self):
        self._make_test_crash()
        ok_(
            os.path.islink(
                os.path.join(
                    self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                    self.fsrts._get_date_root_name(self.CRASH_ID_1))))
        ok_(
            os.path.exists(
                os.path.join(
                    self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                    self.fsrts._get_date_root_name(self.CRASH_ID_1),
                    self.CRASH_ID_1)))

    def test_get_raw_crash(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_crash(self.CRASH_ID_1)['test'], "TEST")
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_crash,
                      self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_dump(self.CRASH_ID_1, 'foo'), "bar")
        eq_(
            self.fsrts.get_raw_dump(self.CRASH_ID_1,
                                    self.fsrts.config.dump_field), "baz")
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_dump,
                      self.CRASH_ID_2, "foo")
        assert_raises(IOError, self.fsrts.get_raw_dump, self.CRASH_ID_1,
                      "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        eq_(
            self.fsrts.get_raw_dumps(self.CRASH_ID_1),
            MemoryDumpsMapping({
                'foo': 'bar',
                self.fsrts.config.dump_field: 'baz'
            }))
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_dumps,
                      self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
                self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                self.fsrts._get_date_root_name(self.CRASH_ID_1)))

        p = os.path.join(parent, self.CRASH_ID_1)
        ok_(not os.path.exists(p))

        assert_raises(CrashIDNotFound, self.fsrts.remove, self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        eq_(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
        eq_(list(self.fsrts.new_crashes()), [])
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot

        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()

        date_path = self.fsrts._get_dated_parent_directory(
            self.CRASH_ID_1, ['00', '00_00'])

        new_date_path = self.fsrts._get_dated_parent_directory(
            self.CRASH_ID_1, ['00', '00_01'])

        webhead_path = os.sep.join([new_date_path, 'webhead_0'])

        os.mkdir(new_date_path)
        os.rename(date_path, webhead_path)

        os.unlink(os.sep.join([webhead_path, self.CRASH_ID_1]))
        os.symlink(
            '../../../../name/' +
            os.sep.join(self.fsrts._get_radix(self.CRASH_ID_1)),
            os.sep.join([webhead_path, self.CRASH_ID_1]))

        self.fsrts._current_slot = lambda: ['00', '00_02']
        eq_(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])

    def test_orphaned_symlink_clean_up(self):
        # Bug 971496 identified a problem where a second crash coming in with
        # the same crash id would derail saving the second crash and leave
        # an extra undeleted symbolic link in the file system.  This link
        # would be sited as undeleted on every run of 'new_crashes'.
        # this test shows that we can clean these extra symlinks if we
        # encounter them.
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        # make sure we can't create the duplicate in a different slot
        assert_raises(OSError, self._make_test_crash)
        # make sure the second slot exists so we can make the bogus symlink
        self._make_test_crash_3()
        # create bogus orphan link
        self.fsrts._create_name_to_date_symlink(self.CRASH_ID_1,
                                                self.fsrts._current_slot())
        ok_(
            os.path.islink(
                './crashes/20071025/date/00/00_01/0bba929f-8721-460c-dead-'
                'a43c20071025'))
        # run through the new_crashes iterator which will yield each of the
        # crashes that has been submitted since the last run of new_crashes.
        # this should cause all the symlinks to be removed.
        # we don't bother saving the crashes, as we don't need them.
        for x in self.fsrts.new_crashes():
            pass
        ok_(not os.path.exists(
            './crashes/20071025/date/00/00_01/0bba929f-8721-460c-dead-'
            'a43c20071025'))
class TestFSLegacyDatedRadixTreeStorage(TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"
    CRASH_ID_3 = "0bba929f-8721-460c-dddd-a43c20071025"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option('logger', default=mock_logging)
        config_manager = ConfigurationManager(
            [required_config],
            app_name='testapp',
            app_version='1.0',
            app_description='app description',
            values_source_list=[{
                'logger': mock_logging,
                'minute_slice_interval': 1,
                'fs_root': FS_ROOT,
            }],
            argv_source=[])
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash({"test": "TEST"},
                                  MemoryDumpsMapping({
                                      'foo':
                                      'bar',
                                      self.fsrts.config.dump_field:
                                      'baz'
                                  }), self.CRASH_ID_1)

    def _make_test_crash_3(self):
        self.fsrts.save_raw_crash({"test": "TEST"},
                                  MemoryDumpsMapping({
                                      'foo':
                                      'bar',
                                      self.fsrts.config.dump_field:
                                      'baz'
                                  }), self.CRASH_ID_3)

    def test_save_raw_crash(self):
        self._make_test_crash()
        assert os.path.islink(
            os.path.join(
                self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                self.fsrts._get_date_root_name(self.CRASH_ID_1)))
        assert os.path.exists(
            os.path.join(
                self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                self.fsrts._get_date_root_name(self.CRASH_ID_1),
                self.CRASH_ID_1))

    def test_get_raw_crash(self):
        self._make_test_crash()
        assert self.fsrts.get_raw_crash(self.CRASH_ID_1)['test'] == 'TEST'
        with pytest.raises(CrashIDNotFound):
            self.fsrts.get_raw_crash(self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        assert self.fsrts.get_raw_dump(self.CRASH_ID_1, 'foo') == "bar"
        assert self.fsrts.get_raw_dump(self.CRASH_ID_1,
                                       self.fsrts.config.dump_field) == 'baz'
        with pytest.raises(CrashIDNotFound):
            self.fsrts.get_raw_dump(self.CRASH_ID_2, "foo")

        with pytest.raises(IOError):
            self.fsrts.get_raw_dump(self.CRASH_ID_1, "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        expected = MemoryDumpsMapping({
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        })
        assert self.fsrts.get_raw_dumps(self.CRASH_ID_1) == expected
        with pytest.raises(CrashIDNotFound):
            self.fsrts.get_raw_dumps(self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
                self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                self.fsrts._get_date_root_name(self.CRASH_ID_1)))

        p = os.path.join(parent, self.CRASH_ID_1)
        assert not os.path.exists(p)

        with pytest.raises(CrashIDNotFound):
            self.fsrts.remove(self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        assert list(self.fsrts.new_crashes()) == [self.CRASH_ID_1]
        assert list(self.fsrts.new_crashes()) == []
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot

        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()

        date_path = self.fsrts._get_dated_parent_directory(
            self.CRASH_ID_1, ['00', '00_00'])

        new_date_path = self.fsrts._get_dated_parent_directory(
            self.CRASH_ID_1, ['00', '00_01'])

        webhead_path = os.sep.join([new_date_path, 'webhead_0'])

        os.mkdir(new_date_path)
        os.rename(date_path, webhead_path)

        os.unlink(os.sep.join([webhead_path, self.CRASH_ID_1]))
        os.symlink(
            '../../../../name/' +
            os.sep.join(self.fsrts._get_radix(self.CRASH_ID_1)),
            os.sep.join([webhead_path, self.CRASH_ID_1]))

        self.fsrts._current_slot = lambda: ['00', '00_02']
        assert list(self.fsrts.new_crashes()) == [self.CRASH_ID_1]

    def test_doesnt_raise_oserror(self):
        # Bug 1297760 is caused by trying to create a symlink which kicks up
        # an OSError which never gets handled which causes the collector to
        # raise an HTTP 500 error. We want to make sure save_raw_crash()
        # doesn't raise OSErrors.
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
class TestFSLegacyDatedRadixTreeStorage(TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"
    CRASH_ID_3 = "0bba929f-8721-460c-dddd-a43c20071025"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option('logger', default=mock_logging)
        config_manager = ConfigurationManager(
          [required_config],
          app_name='testapp',
          app_version='1.0',
          app_description='app description',
          values_source_list=[{
            'logger': mock_logging,
            'minute_slice_interval': 1
          }],
          argv_source=[]
        )
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash({
            "test": "TEST"
        }, MemoryDumpsMapping({
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }), self.CRASH_ID_1)

    def _make_test_crash_3(self):
        self.fsrts.save_raw_crash({
            "test": "TEST"
        }, MemoryDumpsMapping({
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }), self.CRASH_ID_3)

    def test_save_raw_crash(self):
        self._make_test_crash()
        ok_(os.path.islink(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1))))
        ok_(os.path.exists(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1),
              self.CRASH_ID_1)))

    def test_get_raw_crash(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_crash(self.CRASH_ID_1)['test'],
                         "TEST")
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_crash,
                          self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_dump(self.CRASH_ID_1, 'foo'),
                         "bar")
        eq_(self.fsrts.get_raw_dump(self.CRASH_ID_1,
                                                 self.fsrts.config.dump_field),
                         "baz")
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_dump,
                          self.CRASH_ID_2, "foo")
        assert_raises(IOError, self.fsrts.get_raw_dump, self.CRASH_ID_1,
                          "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_dumps(self.CRASH_ID_1), MemoryDumpsMapping({
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }))
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_dumps,
                          self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1)))

        p = os.path.join(parent, self.CRASH_ID_1)
        ok_(not os.path.exists(p))

        assert_raises(CrashIDNotFound, self.fsrts.remove,
                          self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        eq_(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
        eq_(list(self.fsrts.new_crashes()), [])
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot

        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()

        date_path = self.fsrts._get_dated_parent_directory(self.CRASH_ID_1,
                                                           ['00', '00_00'])

        new_date_path = self.fsrts._get_dated_parent_directory(self.CRASH_ID_1,
                                                               ['00', '00_01'])

        webhead_path = os.sep.join([new_date_path, 'webhead_0'])

        os.mkdir(new_date_path)
        os.rename(date_path, webhead_path)

        os.unlink(os.sep.join([webhead_path, self.CRASH_ID_1]))
        os.symlink('../../../../name/' + os.sep.join(self.fsrts._get_radix(
                       self.CRASH_ID_1)),
                   os.sep.join([webhead_path, self.CRASH_ID_1]))

        self.fsrts._current_slot = lambda: ['00', '00_02']
        eq_(list(self.fsrts.new_crashes()),
                         [self.CRASH_ID_1])

    def test_doesnt_raise_oserror(self):
        # Bug 1297760 is caused by trying to create a symlink which kicks up
        # an OSError which never gets handled which causes the collector to
        # raise an HTTP 500 error. We want to make sure save_raw_crash()
        # doesn't raise OSErrors.
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
class TestFSLegacyDatedRadixTreeStorage(unittest.TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option("logger", default=mock_logging)
        config_manager = ConfigurationManager(
            [required_config],
            app_name="testapp",
            app_version="1.0",
            app_description="app description",
            values_source_list=[{"logger": mock_logging, "minute_slice_interval": 1}],
        )
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash(
            {"test": "TEST"}, {"foo": "bar", self.fsrts.config.dump_field: "baz"}, self.CRASH_ID_1
        )

    def test_save_raw_crash(self):
        self._make_test_crash()
        self.assertTrue(
            os.path.islink(
                os.path.join(
                    self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                    self.fsrts._get_date_root_name(self.CRASH_ID_1),
                )
            )
        )
        self.assertTrue(
            os.path.exists(
                os.path.join(
                    self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                    self.fsrts._get_date_root_name(self.CRASH_ID_1),
                    self.CRASH_ID_1,
                )
            )
        )

    def test_get_raw_crash(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_crash(self.CRASH_ID_1)["test"], "TEST")
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_crash, self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_dump(self.CRASH_ID_1, "foo"), "bar")
        self.assertEqual(self.fsrts.get_raw_dump(self.CRASH_ID_1, self.fsrts.config.dump_field), "baz")
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_dump, self.CRASH_ID_2, "foo")
        self.assertRaises(IOError, self.fsrts.get_raw_dump, self.CRASH_ID_1, "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_dumps(self.CRASH_ID_1), {"foo": "bar", self.fsrts.config.dump_field: "baz"})
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_dumps, self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
                self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                self.fsrts._get_date_root_name(self.CRASH_ID_1),
            )
        )

        p = os.path.join(parent, self.CRASH_ID_1)
        self.assertTrue(not os.path.exists(p))

        p = os.path.dirname(p)
        self.assertTrue(not os.path.exists(p))

        p = os.path.dirname(p)
        self.assertTrue(not os.path.exists(p))

        self.assertRaises(CrashIDNotFound, self.fsrts.remove, self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ["00", "00_00"]
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ["00", "00_01"]
        self.assertEqual(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
        self.assertEqual(list(self.fsrts.new_crashes()), [])
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot
Example #5
0
class TestFSLegacyDatedRadixTreeStorage(unittest.TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option('logger', default=mock_logging)
        config_manager = ConfigurationManager(
            [required_config],
            app_name='testapp',
            app_version='1.0',
            app_description='app description',
            values_source_list=[{
                'logger': mock_logging,
                'minute_slice_interval': 1
            }],
            argv_source=[])
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash({"test": "TEST"}, {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }, self.CRASH_ID_1)

    def test_save_raw_crash(self):
        self._make_test_crash()
        self.assertTrue(
            os.path.islink(
                os.path.join(
                    self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                    self.fsrts._get_date_root_name(self.CRASH_ID_1))))
        self.assertTrue(
            os.path.exists(
                os.path.join(
                    self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                    self.fsrts._get_date_root_name(self.CRASH_ID_1),
                    self.CRASH_ID_1)))

    def test_get_raw_crash(self):
        self._make_test_crash()
        self.assertEqual(
            self.fsrts.get_raw_crash(self.CRASH_ID_1)['test'], "TEST")
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_crash,
                          self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_dump(self.CRASH_ID_1, 'foo'),
                         "bar")
        self.assertEqual(
            self.fsrts.get_raw_dump(self.CRASH_ID_1,
                                    self.fsrts.config.dump_field), "baz")
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_dump,
                          self.CRASH_ID_2, "foo")
        self.assertRaises(IOError, self.fsrts.get_raw_dump, self.CRASH_ID_1,
                          "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_dumps(self.CRASH_ID_1), {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        })
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_dumps,
                          self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
                self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
                self.fsrts._get_date_root_name(self.CRASH_ID_1)))

        p = os.path.join(parent, self.CRASH_ID_1)
        self.assertTrue(not os.path.exists(p))

        self.assertRaises(CrashIDNotFound, self.fsrts.remove, self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        self.assertEqual(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
        self.assertEqual(list(self.fsrts.new_crashes()), [])
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot

        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()

        date_path = self.fsrts._get_dated_parent_directory(
            self.CRASH_ID_1, ['00', '00_00'])

        new_date_path = self.fsrts._get_dated_parent_directory(
            self.CRASH_ID_1, ['00', '00_01'])

        webhead_path = os.sep.join([new_date_path, 'webhead_0'])

        os.mkdir(new_date_path)
        os.rename(date_path, webhead_path)

        os.unlink(os.sep.join([webhead_path, self.CRASH_ID_1]))
        os.symlink(
            '../../../../name/' +
            os.sep.join(self.fsrts._get_radix(self.CRASH_ID_1)),
            os.sep.join([webhead_path, self.CRASH_ID_1]))

        self.fsrts._current_slot = lambda: ['00', '00_02']
        self.assertEqual(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
class TestFSLegacyDatedRadixTreeStorage(unittest.TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"
    CRASH_ID_3 = "0bba929f-8721-460c-dddd-a43c20071025"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option('logger', default=mock_logging)
        config_manager = ConfigurationManager(
          [required_config],
          app_name='testapp',
          app_version='1.0',
          app_description='app description',
          values_source_list=[{
            'logger': mock_logging,
            'minute_slice_interval': 1
          }],
          argv_source=[]
        )
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash({
            "test": "TEST"
        }, {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }, self.CRASH_ID_1)

    def _make_test_crash_3(self):
        self.fsrts.save_raw_crash({
            "test": "TEST"
        }, {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }, self.CRASH_ID_3)


    def test_save_raw_crash(self):
        self._make_test_crash()
        ok_(os.path.islink(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1))))
        ok_(os.path.exists(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1),
              self.CRASH_ID_1)))

    def test_get_raw_crash(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_crash(self.CRASH_ID_1)['test'],
                         "TEST")
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_crash,
                          self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_dump(self.CRASH_ID_1, 'foo'),
                         "bar")
        eq_(self.fsrts.get_raw_dump(self.CRASH_ID_1,
                                                 self.fsrts.config.dump_field),
                         "baz")
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_dump,
                          self.CRASH_ID_2, "foo")
        assert_raises(IOError, self.fsrts.get_raw_dump, self.CRASH_ID_1,
                          "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        eq_(self.fsrts.get_raw_dumps(self.CRASH_ID_1), {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        })
        assert_raises(CrashIDNotFound, self.fsrts.get_raw_dumps,
                          self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1)))

        p = os.path.join(parent, self.CRASH_ID_1)
        ok_(not os.path.exists(p))

        assert_raises(CrashIDNotFound, self.fsrts.remove,
                          self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        eq_(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
        eq_(list(self.fsrts.new_crashes()), [])
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot

        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()

        date_path = self.fsrts._get_dated_parent_directory(self.CRASH_ID_1,
                                                           ['00', '00_00'])

        new_date_path = self.fsrts._get_dated_parent_directory(self.CRASH_ID_1,
                                                               ['00', '00_01'])

        webhead_path = os.sep.join([new_date_path, 'webhead_0'])

        os.mkdir(new_date_path)
        os.rename(date_path, webhead_path)

        os.unlink(os.sep.join([webhead_path, self.CRASH_ID_1]))
        os.symlink('../../../../name/' + os.sep.join(self.fsrts._get_radix(
                       self.CRASH_ID_1)),
                   os.sep.join([webhead_path, self.CRASH_ID_1]))

        self.fsrts._current_slot = lambda: ['00', '00_02']
        eq_(list(self.fsrts.new_crashes()),
                         [self.CRASH_ID_1])

    def test_orphaned_symlink_clean_up(self):
        # Bug 971496 identified a problem where a second crash coming in with
        # the same crash id would derail saving the second crash and leave
        # an extra undeleted symbolic link in the file system.  This link
        # would be sited as undeleted on every run of 'new_crashes'.
        # this test shows that we can clean these extra symlinks if we
        # encounter them.
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        # make sure we can't create the duplicate in a different slot
        assert_raises(OSError, self._make_test_crash)
        # make sure the second slot exists so we can make the bogus symlink
        self._make_test_crash_3()
        # create bogus orphan link
        self.fsrts._create_name_to_date_symlink(
            self.CRASH_ID_1,
            self.fsrts._current_slot()
        )
        ok_(os.path.islink(
            './crashes/20071025/date/00/00_01/0bba929f-8721-460c-dead-'
            'a43c20071025'
        ))
        # run through the new_crashes iterator which will yield each of the
        # crashes that has been submitted since the last run of new_crashes.
        # this should cause all the symlinks to be removed.
        # we don't bother saving the crashes, as we don't need them.
        for x in self.fsrts.new_crashes():
            pass
        ok_(not os.path.exists(
            './crashes/20071025/date/00/00_01/0bba929f-8721-460c-dead-'
            'a43c20071025'
        ))
class TestFSLegacyDatedRadixTreeStorage(unittest.TestCase):
    CRASH_ID_1 = "0bba929f-8721-460c-dead-a43c20071025"
    CRASH_ID_2 = "0bba929f-8721-460c-dead-a43c20071026"

    def setUp(self):
        with self._common_config_setup().context() as config:
            self.fsrts = FSLegacyDatedRadixTreeStorage(config)

    def tearDown(self):
        shutil.rmtree(self.fsrts.config.fs_root)

    def _common_config_setup(self):
        mock_logging = Mock()
        required_config = FSLegacyDatedRadixTreeStorage.get_required_config()
        required_config.add_option('logger', default=mock_logging)
        config_manager = ConfigurationManager(
          [required_config],
          app_name='testapp',
          app_version='1.0',
          app_description='app description',
          values_source_list=[{
            'logger': mock_logging,
            'minute_slice_interval': 1
          }]
        )
        return config_manager

    def _make_test_crash(self):
        self.fsrts.save_raw_crash({
            "test": "TEST"
        }, {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        }, self.CRASH_ID_1)

    def test_save_raw_crash(self):
        self._make_test_crash()
        self.assertTrue(os.path.islink(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1))))
        self.assertTrue(os.path.exists(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1),
              self.CRASH_ID_1)))

    def test_get_raw_crash(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_crash(self.CRASH_ID_1)['test'],
                         "TEST")
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_crash,
                          self.CRASH_ID_2)

    def test_get_raw_dump(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_dump(self.CRASH_ID_1, 'foo'),
                         "bar")
        self.assertEqual(self.fsrts.get_raw_dump(self.CRASH_ID_1,
                                                 self.fsrts.config.dump_field),
                         "baz")
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_dump,
                          self.CRASH_ID_2, "foo")
        self.assertRaises(IOError, self.fsrts.get_raw_dump, self.CRASH_ID_1,
                          "foor")

    def test_get_raw_dumps(self):
        self._make_test_crash()
        self.assertEqual(self.fsrts.get_raw_dumps(self.CRASH_ID_1), {
            'foo': 'bar',
            self.fsrts.config.dump_field: 'baz'
        })
        self.assertRaises(CrashIDNotFound, self.fsrts.get_raw_dumps,
                          self.CRASH_ID_2)

    def test_remove(self):
        self._make_test_crash()
        self.fsrts.remove(self.CRASH_ID_1)

        parent = os.path.realpath(
            os.path.join(
              self.fsrts._get_radixed_parent_directory(self.CRASH_ID_1),
              self.fsrts._get_date_root_name(self.CRASH_ID_1)))

        p = os.path.join(parent, self.CRASH_ID_1)
        self.assertTrue(not os.path.exists(p))

        self.assertRaises(CrashIDNotFound, self.fsrts.remove,
                          self.CRASH_ID_2)

    def test_new_crashes(self):
        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()
        self.fsrts._current_slot = lambda: ['00', '00_01']
        self.assertEqual(list(self.fsrts.new_crashes()), [self.CRASH_ID_1])
        self.assertEqual(list(self.fsrts.new_crashes()), [])
        self.fsrts.remove(self.CRASH_ID_1)
        del self.fsrts._current_slot

        self.fsrts._current_slot = lambda: ['00', '00_00']
        self._make_test_crash()

        date_path = self.fsrts._get_dated_parent_directory(self.CRASH_ID_1,
                                                           ['00', '00_00'])

        new_date_path = self.fsrts._get_dated_parent_directory(self.CRASH_ID_1,
                                                               ['00', '00_01'])

        webhead_path = os.sep.join([new_date_path, 'webhead_0'])

        os.mkdir(new_date_path)
        os.rename(date_path, webhead_path)

        os.unlink(os.sep.join([webhead_path, self.CRASH_ID_1]))
        os.symlink('../../../../name/' + os.sep.join(self.fsrts._get_radix(
                       self.CRASH_ID_1)),
                   os.sep.join([webhead_path, self.CRASH_ID_1]))

        self.fsrts._current_slot = lambda: ['00', '00_02']
        self.assertEqual(list(self.fsrts.new_crashes()),
                         [self.CRASH_ID_1])