Beispiel #1
0
 def test_log(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "oi"])
     first_commit = dvol.voluminous.getOutput()[-1]
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "you"])
     second_commit = dvol.voluminous.getOutput()[-1]
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "log"])
     actual = dvol.voluminous.getOutput()[-1]
     expected = ("commit {second_commit}\n"
                 "Author: Who knows <mystery@person>\n"
                 "Date: Whenever\n"
                 "\n"
                 "    you\n"
                 "\n"
                 "commit {first_commit}\n"
                 "Author: Who knows <mystery@person>\n"
                 "Date: Whenever\n"
                 "\n"
                 "    oi\n").format(first_commit=first_commit,
                                    second_commit=second_commit)
     expectedLines = expected.split("\n")
     actualLines = actual.split("\n")
     self.assertEqual(len(expectedLines), len(actualLines))
     for expected, actual in zip(expectedLines, actualLines):
         self.assertTrue(actual.startswith(expected))
Beispiel #2
0
 def test_create_volume_already_exists(self):
     dvol = VoluminousOptions()
     # Create the repository twice, second time should have the error
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     self.assertEqual(dvol.voluminous.getOutput()[-1],
                      "Error: volume foo already exists")
Beispiel #3
0
 def test_create_volume_already_exists(self):
     dvol = VoluminousOptions()
     # Create the repository twice, second time should have the error
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     self.assertEqual(dvol.voluminous.getOutput()[-1],
             "Error: volume foo already exists")
Beispiel #4
0
 def test_list_empty_volumes(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     self.assertIn(
         dvol.voluminous.getOutput()[-1].strip(),
         "  VOLUME   BRANCH   CONTAINERS"
     )
Beispiel #5
0
 def test_create_volume_already_exists(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(["-p", self.tmpdir.path, "init", "foo"])
     self.assertRaises(VolumeAlreadyExists,
             dvol.parseOptions, ["-p", self.tmpdir.path, "init", "foo"])
     self.assertEqual(dvol.voluminous.getOutput(),
             ["Error: volume foo already exists"])
Beispiel #6
0
 def test_remove_volume_path_separator(self):
     dvol = VoluminousOptions()
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "rm", "-f", "foo/bar"])
         output = dvol.voluminous.getOutput()[-1]
     except CalledProcessErrorWithOutput, error:
         output = error.original.output
Beispiel #7
0
    def test_branch_multi_volumes(self, volumes):
        """
        Always show the last checked-out branch for all volumes in ``list``.
        """
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        for volume, branch in volumes:
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "init", volume])
            dvol.parseOptions(ARGS +
                              ["-p", tmpdir.path, "commit", "-m", "hello"])
            dvol.parseOptions(ARGS +
                              ["-p", tmpdir.path, "checkout", "-b", branch])

        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])
        lines = dvol.voluminous.getOutput()[-1].split("\n")
        header, rest = lines[0], lines[1:]

        expected_volumes = [[volume, branch] for volume, branch in volumes]
        # `init` activates the volume, so the last initialized volume is the
        # active one.
        expected_volumes[-1] = [
            '*', expected_volumes[-1][0], expected_volumes[-1][1]
        ]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header.split())
        self.assertEqual(
            sorted(expected_volumes),
            sorted([line.split() for line in rest]),
        )
Beispiel #8
0
 def test_log(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "oi"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "you"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "log"])
     actual = dvol.voluminous.getOutput()[-1]
     expected = ("commit\n"
                 "Author:\n"
                 "Date:\n"
                 "\n"
                 "    you\n"
                 "\n"
                 "commit\n"
                 "Author:\n"
                 "Date:\n"
                 "\n"
                 "    oi\n")
     expectedLines = expected.split("\n")
     actualLines = actual.split("\n")
     self.assertEqual(len(expectedLines), len(actualLines))
     for expected, actual in zip(expectedLines, actualLines):
         self.assertTrue(actual.startswith(expected))
Beispiel #9
0
 def test_remove_volume_path_separator(self):
     dvol = VoluminousOptions()
     try:
         dvol.parseOptions(ARGS +
                           ["-p", self.tmpdir.path, "rm", "-f", "foo/bar"])
         output = dvol.voluminous.getOutput()[-1]
     except CalledProcessErrorWithOutput, error:
         output = error.original.output
Beispiel #10
0
 def test_create_volume(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     self.assertTrue(self.tmpdir.child("foo").exists())
     self.assertTrue(self.tmpdir.child("foo").child("branches")
             .child("master").exists())
     self.assertEqual(dvol.voluminous.getOutput()[-1],
             "Created volume foo\nCreated branch foo/master")
Beispiel #11
0
 def test_get_set_config(self):
     """
     A configuration key can be set and then retrieved.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "config", "user.name", "alice"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "config", "user.name"])
     self.assertEqual(dvol.voluminous.getOutput()[-1],
         "alice")
Beispiel #12
0
 def test_create_volume(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     self.assertTrue(self.tmpdir.child("foo").exists())
     self.assertTrue(
         self.tmpdir.child("foo").child("branches").child(
             "master").exists())
     self.assertEqual(dvol.voluminous.getOutput()[-1],
                      "Created volume foo\nCreated branch foo/master")
Beispiel #13
0
 def test_create_volume(self):
     # TODO test volume names with '/' in them - they should not end up
     # making nested heirarchy
     dvol = VoluminousOptions()
     dvol.parseOptions(["-p", self.tmpdir.path, "init", "foo"])
     self.assertTrue(self.tmpdir.child("foo").exists())
     self.assertTrue(self.tmpdir.child("foo").child("branches")
             .child("master").exists())
     self.assertEqual(dvol.voluminous.getOutput(),
             ["Created volume foo", "Created branch foo/master"])
Beispiel #14
0
 def test_log(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "oi"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "you"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "log"])
     actual = dvol.voluminous.getOutput()[-1]
     expected = (
         "commit\n"
         "Author:\n"
         "Date:\n"
         "\n"
         "    you\n"
         "\n"
         "commit\n"
         "Author:\n"
         "Date:\n"
         "\n"
         "    oi\n")
     expectedLines = expected.split("\n")
     actualLines = actual.split("\n")
     self.assertEqual(len(expectedLines), len(actualLines))
     for expected, actual in zip(
             expectedLines, actualLines):
         self.assertTrue(actual.startswith(expected))
Beispiel #15
0
    def test_branch_multi_volumes(self, volumes):
        """
        Always show the last checked-out branch for all volumes in ``list``.
        """
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        for volume, branch in volumes:
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "init", volume])
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "commit", "-m", "hello"])
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "checkout", "-b", branch])

        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])
        lines = dvol.voluminous.getOutput()[-1].split("\n")
        header, rest = lines[0], lines[1:]

        expected_volumes = [[volume, branch] for volume, branch in volumes]
        # `init` activates the volume, so the last initialized volume is the
        # active one.
        expected_volumes[-1] = [
            '*', expected_volumes[-1][0], expected_volumes[-1][1]]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header.split())
        self.assertEqual(
            sorted(expected_volumes),
            sorted([line.split() for line in rest]),
        )
Beispiel #16
0
 def test_log(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "oi"])
     first_commit = dvol.voluminous.getOutput()[-1]
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "you"])
     second_commit = dvol.voluminous.getOutput()[-1]
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "log"])
     actual = dvol.voluminous.getOutput()[-1]
     expected = (
         "commit {second_commit}\n"
         "Author: Who knows <mystery@person>\n"
         "Date: Whenever\n"
         "\n"
         "    you\n"
         "\n"
         "commit {first_commit}\n"
         "Author: Who knows <mystery@person>\n"
         "Date: Whenever\n"
         "\n"
         "    oi\n").format(
             first_commit=first_commit,
             second_commit=second_commit
         )
     expectedLines = expected.split("\n")
     actualLines = actual.split("\n")
     self.assertEqual(len(expectedLines), len(actualLines))
     for expected, actual in zip(
             expectedLines, actualLines):
         self.assertTrue(actual.startswith(expected))
Beispiel #17
0
 def test_commit_no_message_raises_error(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "commit"])
         # TODO assert exit code != 0
         self.assertTrue(dvol.voluminous.getOutput()[-1].strip().endswith(
                 "You must provide a commit message"))
     except UsageError:
         # in non-out-of-process case, we'll get this exception. This is OK.
         pass
Beispiel #18
0
 def test_switch_volume_does_not_exist(self):
     """
     ``dvol switch`` should give a meaningful error message if the
     volume we try to switch to doesn't exist.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "switch", "bar"])
     except CalledProcessErrorWithOutput, error:
         self.assertEqual(error.original.output.rstrip(), "Error: bar does not exist")
Beispiel #19
0
 def test_create_volume_already_exists(self):
     dvol = VoluminousOptions()
     # Create the repository twice, second time should have the error
     expected_output = "Error: volume foo already exists"
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
         self.assertEqual(dvol.voluminous.getOutput()[-1], expected_output)
     except CalledProcessErrorWithOutput, error:
         self.assertIn(expected_output, error.original.output)
         self.assertTrue(error.original.returncode != 0)
Beispiel #20
0
    def test_switch_active_volume(self):
        """
        ``dvol switch`` should switch the currently active volume
        stored in the current_volume.json file.

        Assert whiteboxy things about the implementation, because
        we care about upgradeability (wrt on-disk format) between
        different implementations.
        """
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "bar"])
        self.assertEqual(
            json.loads(self.tmpdir.child("current_volume.json").getContent()),
            dict(current_volume="bar"))
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "switch", "foo"])
        self.assertEqual(
            json.loads(self.tmpdir.child("current_volume.json").getContent()),
            dict(current_volume="foo"))
        # Verify operation with `list`
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
        header, rest = self._parse_list_output(dvol)
        expected_volumes = [["*", "foo", "master"], ["bar", "master"]]
        self.assertEqual(
            sorted(expected_volumes),
            sorted(rest),
        )
Beispiel #21
0
 def test_commit_no_message_raises_error(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     # TODO after throwing away python version, make this test stricter
     # about exit code != 0
     try:
         try:
             dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "commit"])
         except CalledProcessErrorWithOutput, error: # go version
             expected_output = "You must provide a commit message"
             self.assertIn(expected_output, error.original.output)
             self.assertTrue(error.original.returncode != 0)
     except UsageError: # python version
         # in non-out-of-process case, we'll get this exception. This is OK.
         pass
Beispiel #22
0
 def test_create_volume(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     self.assertTrue(self.tmpdir.child("foo").exists())
     self.assertTrue(self.tmpdir.child("foo").child("branches")
             .child("master").exists())
     self.assertEqual(dvol.voluminous.getOutput()[-1],
             "Created volume foo\nCreated branch foo/master")
     # Verify operation with `list`
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     expected_volumes = [["*", "foo", "master"]]
     self.assertEqual(
         sorted(expected_volumes),
         sorted(rest),
     )
Beispiel #23
0
 def test_commit_volume(self):
     # TODO need to assert that containers using this volume get stopped
     # and started around commits
     # TODO test snapshotting nonexistent volume
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     volume = self.tmpdir.child("foo")
     volume.child("branches").child("master").child(
         "file.txt").setContent("hello!")
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "hello from 30,000 ft"])
     commitId = dvol.voluminous.getOutput()[-1]
     commit = volume.child("commits").child(commitId)
     self.assertTrue(commit.exists())
     self.assertTrue(commit.child("file.txt").exists())
     self.assertEqual(commit.child("file.txt").getContent(), "hello!")
Beispiel #24
0
    def test_switch_active_volume(self):
        """
        ``dvol switch`` should switch the currently active volume
        stored in the current_volume.json file.

        Assert whiteboxy things about the implementation, because
        we care about upgradeability (wrt on-disk format) between
        different implementations.
        """
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "bar"])
        self.assertEqual(
            json.loads(self.tmpdir.child("current_volume.json").getContent()),
            dict(current_volume="bar")
        )
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "switch", "foo"])
        self.assertEqual(
            json.loads(self.tmpdir.child("current_volume.json").getContent()),
            dict(current_volume="foo")
        )
        # Verify operation with `list`
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
        header, rest = self._parse_list_output(dvol)
        expected_volumes = [["*", "foo", "master"], ["bar", "master"]]
        self.assertEqual(
            sorted(expected_volumes),
            sorted(rest),
        )
Beispiel #25
0
 def test_remove_volume(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "rm", "-f", "foo"])
     self.assertEqual(dvol.voluminous.getOutput()[-1],
                      "Deleting volume 'foo'")
     self.assertFalse(self.tmpdir.child("foo").exists())
     # Verify operation with `list`
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     self.assertEqual(len(rest), 0)
Beispiel #26
0
    def test_list_multi_volumes(self, volumes):
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        for name in volumes:
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "init", name])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])

        header, rest = self._parse_list_output(dvol)
        expected_volumes = [[name, 'master'] for name in volumes]
        # `init` activates the volume, so the last initialized volume is the
        # active one.
        expected_volumes[-1] = ['*', expected_volumes[-1][0], expected_volumes[-1][1]]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header)
        self.assertEqual(
            sorted(expected_volumes),
            sorted(rest),
        )
Beispiel #27
0
 def test_list_multi_volumes(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(["-p", self.tmpdir.path, "init", "foo2"])
     dvol.parseOptions(["-p", self.tmpdir.path, "list"])
     self.assertEqual(sorted(dvol.voluminous.getOutput()[0].split("\n")),
             sorted(["  VOLUME   BRANCH   CONTAINERS ",
                     "  foo      master              ",
                     "* foo2     master              "]))
Beispiel #28
0
    def test_list_multi_volumes(self, volumes):
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        for name in volumes:
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "init", name])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])

        lines = dvol.voluminous.getOutput()[-1].split("\n")
        header, rest = lines[0], lines[1:]
        expected_volumes = [[name, 'master'] for name in volumes]
        # `init` activates the volume, so the last initialized volume is the
        # active one.
        expected_volumes[-1] = ['*', expected_volumes[-1][0], expected_volumes[-1][1]]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header.split())
        self.assertEqual(
            sorted(expected_volumes),
            sorted([line.split() for line in rest]),
        )
Beispiel #29
0
    def test_non_standard_branch(self, volume_name, branch_name,
                                 commit_message, filename, content):
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ['-p', tmpdir.path, 'init', volume_name])
        volume = tmpdir.child(volume_name)
        volume.child("branches").child("master").child(filename).setContent(
            content)
        dvol.parseOptions(ARGS +
                          ["-p", tmpdir.path, "commit", "-m", commit_message])
        dvol.parseOptions(ARGS +
                          ["-p", tmpdir.path, "checkout", "-b", branch_name])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])
        lines = dvol.voluminous.getOutput()[-1].split("\n")
        header, rest = lines[0], lines[1:]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header.split())
        self.assertEqual([['*', volume_name, branch_name]],
                         [line.split() for line in rest])
Beispiel #30
0
    def test_non_standard_branch(self, volume_name, branch_name, commit_message, filename,
                                 content):
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ['-p', tmpdir.path, 'init', volume_name])
        volume = tmpdir.child(volume_name)
        volume.child("branches").child("master").child(filename).setContent(content)
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "commit", "-m", commit_message])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "checkout", "-b", branch_name])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])
        lines = dvol.voluminous.getOutput()[-1].split("\n")
        header, rest = lines[0], lines[1:]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header.split())
        self.assertEqual(
            [['*', volume_name, branch_name]], [line.split() for line in rest])
Beispiel #31
0
 def test_remove_volume(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "rm", "-f", "foo"])
     self.assertEqual(dvol.voluminous.getOutput()[-1],
         "Deleting volume 'foo'")
     self.assertFalse(self.tmpdir.child("foo").exists())
     # Verify operation with `list`
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     self.assertEqual(len(rest), 0)
Beispiel #32
0
 def test_created_volume_active_after_switch(self):
     """
     After we have used ``dvol switch`` to switch volume, ``dvol init``
     should be able to set the active volume to the one just created.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "bar"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "switch", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "baz"])
     self.assertEqual(
         json.loads(self.tmpdir.child("current_volume.json").getContent()),
         dict(current_volume="baz"))
     # Verify operation with `list`
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     expected_volumes = [["foo", "master"], ["bar", "master"],
                         ["*", "baz", "master"]]
     self.assertEqual(
         sorted(expected_volumes),
         sorted(rest),
     )
Beispiel #33
0
 def test_reset_HEAD(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     volume = self.tmpdir.child("foo")
     volume.child("branches").child("master").child("file.txt").setContent(
         "alpha")
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])
     volume.child("branches").child("master").child("file.txt").setContent(
         "beta")
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "reset", "--hard", "HEAD"])
     # working copy is changed
     self.assertEqual(
         volume.child("branches").child("master").child(
             "file.txt").getContent(), "alpha")
Beispiel #34
0
 def test_list_current_volume_deleted(self):
     """
     If the currently selected volume has been deleted, `list`
     displays all remaining volumes and no volume is marked with '*'
     indicating that it is the currently selected volume.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "bar"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "rm", "-f", "bar"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     expected_volumes = [["foo", "master"]]
     self.assertEqual(
         sorted(expected_volumes),
         sorted(rest),
     )
Beispiel #35
0
    def test_reset_HEAD_multiple_commits(self):
        # assert that the correct (latest) commit is rolled back to
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        volume = self.tmpdir.child("foo")

        volume.child("branches").child("master").child("file.txt").setContent(
            "BAD")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])

        volume.child("branches").child("master").child("file.txt").setContent(
            "alpha")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 2"])

        volume.child("branches").child("master").child("file.txt").setContent(
            "beta")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "reset", "--hard", "HEAD"])
        # working copy is changed from beta to alpha, but not BAD
        self.assertEqual(
            volume.child("branches").child("master").child(
                "file.txt").getContent(), "alpha")
Beispiel #36
0
 def test_get_set_config(self):
     """
     A configuration key can be set and then retrieved.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(
         ARGS + ["-p", self.tmpdir.path, "config", "user.name", "alice"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "config", "user.name"])
     self.assertEqual(dvol.voluminous.getOutput()[-1], "alice")
Beispiel #37
0
 def test_create_volume_already_exists(self):
     dvol = VoluminousOptions()
     # Create the repository twice, second time should have the error
     expected_output = "Error: volume foo already exists"
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
         self.assertEqual(dvol.voluminous.getOutput()[-1], expected_output)
     except CalledProcessErrorWithOutput, error:
         self.assertIn(expected_output, error.original.output)
         self.assertTrue(error.original.returncode != 0)
Beispiel #38
0
 def test_commit_no_message_raises_error(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "commit"])
         # TODO assert exit code != 0
         self.assertTrue(dvol.voluminous.getOutput()[-1].strip().endswith(
             "You must provide a commit message"))
     except UsageError:
         # in non-out-of-process case, we'll get this exception. This is OK.
         pass
Beispiel #39
0
 def test_reset_HEAD(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     volume = self.tmpdir.child("foo")
     volume.child("branches").child("master").child(
         "file.txt").setContent("alpha")
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "commit 1"])
     volume.child("branches").child("master").child(
         "file.txt").setContent("beta")
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "reset", "--hard", "HEAD"])
     # working copy is changed
     self.assertEqual(volume.child("branches").child("master")
             .child("file.txt").getContent(), "alpha")
Beispiel #40
0
 def test_switch_volume_does_not_exist(self):
     """
     ``dvol switch`` should give a meaningful error message if the
     volume we try to switch to doesn't exist.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     try:
         dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "switch", "bar"])
     except CalledProcessErrorWithOutput, error:
         self.assertEqual(error.original.output.rstrip(),
                          "Error: bar does not exist")
Beispiel #41
0
    def test_reset_HEAD_multiple_commits(self):
        # assert that the correct (latest) commit is rolled back to
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        volume = self.tmpdir.child("foo")

        volume.child("branches").child("master").child(
            "file.txt").setContent("BAD")
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "commit", "-m", "commit 1"])

        volume.child("branches").child("master").child(
            "file.txt").setContent("alpha")
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "commit", "-m", "commit 2"])

        volume.child("branches").child("master").child(
            "file.txt").setContent("beta")
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "reset", "--hard", "HEAD"])
        # working copy is changed from beta to alpha, but not BAD
        self.assertEqual(volume.child("branches").child("master")
                .child("file.txt").getContent(), "alpha")
Beispiel #42
0
 def test_reset(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     volume = self.tmpdir.child("foo")
     volume.child("branches").child("master").child("file.txt").setContent(
         "alpha")
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])
     commitId = dvol.voluminous.getOutput()[-1]
     commit = volume.child("commits").child(commitId)
     self.assertTrue(commit.exists())
     self.assertTrue(commit.child("file.txt").exists())
     self.assertEqual(commit.child("file.txt").getContent(), "alpha")
     volume.child("branches").child("master").child("file.txt").setContent(
         "beta")
     dvol.parseOptions(
         ARGS + ["-p", self.tmpdir.path, "reset", "--hard", commitId])
     self.assertEqual(
         volume.child("branches").child("master").child(
             "file.txt").getContent(), "alpha")
Beispiel #43
0
 def test_list_current_volume_deleted(self):
     """
     If the currently selected volume has been deleted, `list`
     displays all remaining volumes and no volume is marked with '*'
     indicating that it is the currently selected volume.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "bar"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "rm", "-f", "bar"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     expected_volumes = [["foo", "master"]]
     self.assertEqual(
         sorted(expected_volumes),
         sorted(rest),
     )
Beispiel #44
0
 def test_created_volume_active_after_switch(self):
     """
     After we have used ``dvol switch`` to switch volume, ``dvol init``
     should be able to set the active volume to the one just created.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "bar"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "switch", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "baz"])
     self.assertEqual(
         json.loads(self.tmpdir.child("current_volume.json").getContent()),
         dict(current_volume="baz")
     )
     # Verify operation with `list`
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     expected_volumes = [
         ["foo", "master"], ["bar", "master"], ["*", "baz", "master"]
     ]
     self.assertEqual(
         sorted(expected_volumes),
         sorted(rest),
     )
Beispiel #45
0
 def test_commit_no_message_raises_error(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     # TODO after throwing away python version, make this test stricter
     # about exit code != 0
     try:
         try:
             dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "commit"])
         except CalledProcessErrorWithOutput, error:  # go version
             expected_output = "You must provide a commit message"
             self.assertIn(expected_output, error.original.output)
             self.assertTrue(error.original.returncode != 0)
     except UsageError:  # python version
         # in non-out-of-process case, we'll get this exception. This is OK.
         pass
Beispiel #46
0
 def test_reset(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     volume = self.tmpdir.child("foo")
     volume.child("branches").child("master").child(
         "file.txt").setContent("alpha")
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "commit", "-m", "commit 1"])
     commitId = dvol.voluminous.getOutput()[-1]
     commit = volume.child("commits").child(commitId)
     self.assertTrue(commit.exists())
     self.assertTrue(commit.child("file.txt").exists())
     self.assertEqual(commit.child("file.txt").getContent(), "alpha")
     volume.child("branches").child("master").child(
         "file.txt").setContent("beta")
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
         "reset", "--hard", commitId])
     self.assertEqual(volume.child("branches").child("master")
             .child("file.txt").getContent(), "alpha")
Beispiel #47
0
 def test_commit_volume(self):
     # TODO need to assert that containers using this volume get stopped
     # and started around commits
     # TODO test snapshotting nonexistent volume
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     volume = self.tmpdir.child("foo")
     volume.child("branches").child("master").child("file.txt").setContent(
         "hello!")
     dvol.parseOptions(
         ARGS +
         ["-p", self.tmpdir.path, "commit", "-m", "hello from 30,000 ft"])
     commitId = dvol.voluminous.getOutput()[-1]
     commit = volume.child("commits").child(commitId)
     self.assertTrue(commit.exists())
     self.assertTrue(commit.child("file.txt").exists())
     self.assertEqual(commit.child("file.txt").getContent(), "hello!")
Beispiel #48
0
 def test_create_volume(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     self.assertTrue(self.tmpdir.child("foo").exists())
     self.assertTrue(
         self.tmpdir.child("foo").child("branches").child(
             "master").exists())
     self.assertEqual(dvol.voluminous.getOutput()[-1],
                      "Created volume foo\nCreated branch foo/master")
     # Verify operation with `list`
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     header, rest = self._parse_list_output(dvol)
     expected_volumes = [["*", "foo", "master"]]
     self.assertEqual(
         sorted(expected_volumes),
         sorted(rest),
     )
Beispiel #49
0
    def test_list_multi_volumes(self, volumes):
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        for name in volumes:
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "init", name])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])

        header, rest = self._parse_list_output(dvol)
        expected_volumes = [[name, 'master'] for name in volumes]
        # `init` activates the volume, so the last initialized volume is the
        # active one.
        expected_volumes[-1] = [
            '*', expected_volumes[-1][0], expected_volumes[-1][1]
        ]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header)
        self.assertEqual(
            sorted(expected_volumes),
            sorted(rest),
        )
Beispiel #50
0
    def test_list_multi_volumes(self, volumes):
        tmpdir = FilePath(self.mktemp())
        tmpdir.makedirs()

        dvol = VoluminousOptions()
        for name in volumes:
            dvol.parseOptions(ARGS + ["-p", tmpdir.path, "init", name])
        dvol.parseOptions(ARGS + ["-p", tmpdir.path, "list"])

        lines = dvol.voluminous.getOutput()[-1].split("\n")
        header, rest = lines[0], lines[1:]
        expected_volumes = [[name, 'master'] for name in volumes]
        # `init` activates the volume, so the last initialized volume is the
        # active one.
        expected_volumes[-1] = [
            '*', expected_volumes[-1][0], expected_volumes[-1][1]
        ]
        self.assertEqual(['VOLUME', 'BRANCH', 'CONTAINERS'], header.split())
        self.assertEqual(
            sorted(expected_volumes),
            sorted([line.split() for line in rest]),
        )
Beispiel #51
0
 def test_branch_default_master(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "branch"])
     actual = dvol.voluminous.getOutput()[-1]
     self.assertEqual(actual.strip(), "* master")
Beispiel #52
0
    def test_create_branch_from_current_HEAD(self):
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])

        volume = self.tmpdir.child("foo")
        volume.child("branches").child("master").child("file.txt").setContent(
            "hello")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])

        dvol.parseOptions(
            ARGS + ["-p", self.tmpdir.path, "checkout", "-b", "newbranch"])
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "branch"])
        actual = dvol.voluminous.getOutput()[-1]
        self.assertEqual(actual, "  master\n* newbranch")

        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "log"])
        actual = dvol.voluminous.getOutput()[-1]
        # the commit should have been "copied" to the new branch
        self.assertEqual(len(actual.split("\n")), 6)  # 6 lines = 1 commit
Beispiel #53
0
    def test_reset_HEAD_hat_multiple_commits(self):
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        volume = self.tmpdir.child("foo")

        volume.child("branches").child("master").child("file.txt").setContent(
            "OLD")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])
        oldCommit = dvol.voluminous.getOutput()[-1]

        volume.child("branches").child("master").child("file.txt").setContent(
            "NEW")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 2"])
        newCommit = dvol.voluminous.getOutput()[-1]

        volume.child("branches").child("master").child("file.txt").setContent(
            "NEWER")

        # both exist
        self.assertTrue(volume.child("commits").child(oldCommit).exists())
        self.assertTrue(volume.child("commits").child(newCommit).exists())

        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "reset", "--hard", "HEAD^"])
        self.assertEqual(
            volume.child("branches").child("master").child(
                "file.txt").getContent(), "OLD")

        # newest commit has been wiped out
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "log"])
        actual = dvol.voluminous.getOutput()[-1]
        self.assertEqual(len(actual.split("\n")), 6)  # 6 lines = 1 commit

        # only old exists
        self.assertTrue(volume.child("commits").child(oldCommit).exists())
        self.assertFalse(volume.child("commits").child(newCommit).exists())
Beispiel #54
0
    def test_create_branch_from_current_HEAD(self):
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])

        volume = self.tmpdir.child("foo")
        volume.child("branches").child("master").child(
            "file.txt").setContent("hello")
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "commit", "-m", "commit 1"])

        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "checkout", "-b", "newbranch"])
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "branch"])
        actual = dvol.voluminous.getOutput()[-1]
        self.assertEqual(actual, "  master\n* newbranch")

        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "log"])
        actual = dvol.voluminous.getOutput()[-1]
        # the commit should have been "copied" to the new branch
        self.assertEqual(len(actual.split("\n")), 6) # 6 lines = 1 commit
Beispiel #55
0
    def test_rollback_branch_doesnt_delete_referenced_data_in_other_branches(self):
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        volume = self.tmpdir.child("foo")

        volume.child("branches").child("master").child(
            "file.txt").setContent("OLD")
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "commit", "-m", "commit 1"])
        oldCommit = dvol.voluminous.getOutput()[-1]

        volume.child("branches").child("master").child(
            "file.txt").setContent("NEW")
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "commit", "-m", "commit 2"])
        newCommit = dvol.voluminous.getOutput()[-1]

        volume.child("branches").child("master").child(
            "file.txt").setContent("NEWER")

        # both exist
        self.assertTrue(volume.child("commits").child(oldCommit).exists())
        self.assertTrue(volume.child("commits").child(newCommit).exists())

        # create new branch from current HEAD, and then switch back to master.
        # should protect commit *data* from being destroyed when we later
        # rollback.
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "checkout", "-b", "newbranch"])
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "checkout", "master"])

        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "reset", "--hard", "HEAD^"])
        self.assertEqual(volume.child("branches").child("master")
                .child("file.txt").getContent(), "OLD")

        # newest commit has been wiped out in master branch metadata
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path,
            "log"])
        actual = dvol.voluminous.getOutput()[-1]
        self.assertEqual(len(actual.split("\n")), 6) # 6 lines = 1 commit

        # new still exists because it's referenced in another branch
        self.assertTrue(volume.child("commits").child(oldCommit).exists())
        self.assertTrue(volume.child("commits").child(newCommit).exists())
Beispiel #56
0
 def test_branch_default_master(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "branch"])
     actual = dvol.voluminous.getOutput()[-1]
     self.assertEqual(actual.strip(), "* master")
Beispiel #57
0
    def test_rollback_branch_doesnt_delete_referenced_data_in_other_branches(
            self):
        dvol = VoluminousOptions()
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
        volume = self.tmpdir.child("foo")

        volume.child("branches").child("master").child("file.txt").setContent(
            "OLD")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])
        oldCommit = dvol.voluminous.getOutput()[-1]

        volume.child("branches").child("master").child("file.txt").setContent(
            "NEW")
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "commit", "-m", "commit 2"])
        newCommit = dvol.voluminous.getOutput()[-1]

        volume.child("branches").child("master").child("file.txt").setContent(
            "NEWER")

        # both exist
        self.assertTrue(volume.child("commits").child(oldCommit).exists())
        self.assertTrue(volume.child("commits").child(newCommit).exists())

        # create new branch from current HEAD, and then switch back to master.
        # should protect commit *data* from being destroyed when we later
        # rollback.
        dvol.parseOptions(
            ARGS + ["-p", self.tmpdir.path, "checkout", "-b", "newbranch"])
        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "checkout", "master"])

        dvol.parseOptions(ARGS +
                          ["-p", self.tmpdir.path, "reset", "--hard", "HEAD^"])
        self.assertEqual(
            volume.child("branches").child("master").child(
                "file.txt").getContent(), "OLD")

        # newest commit has been wiped out in master branch metadata
        dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "log"])
        actual = dvol.voluminous.getOutput()[-1]
        self.assertEqual(len(actual.split("\n")), 6)  # 6 lines = 1 commit

        # new still exists because it's referenced in another branch
        self.assertTrue(volume.child("commits").child(oldCommit).exists())
        self.assertTrue(volume.child("commits").child(newCommit).exists())
Beispiel #58
0
 def test_create_volume_with_path_separator(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo/bar"])
     output = dvol.voluminous.getOutput()[-1]
     self.assertIn("Error", output)
     self.assertIn("foo/bar", output)
Beispiel #59
0
 def test_branch_already_exists(self):
     """
     Creating a branch with the same name as an existing branch
     gives an appropriate meaningful error message.
     """
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "init", "foo"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "commit 1"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "checkout", "-b", "alpha"])
     dvol.parseOptions(ARGS +
                       ["-p", self.tmpdir.path, "commit", "-m", "commit 2"])
     expected_output = "Cannot create existing branch alpha"
     try:
         dvol.parseOptions(
             ARGS + ["-p", self.tmpdir.path, "checkout", "-b", "alpha"])
         self.assertEqual(dvol.voluminous.getOutput()[-1], expected_output)
     except CalledProcessErrorWithOutput, error:
         self.assertIn(expected_output, error.original.output)
         self.assertTrue(error.original.returncode != 0)
Beispiel #60
0
 def test_list_empty_volumes(self):
     dvol = VoluminousOptions()
     dvol.parseOptions(ARGS + ["-p", self.tmpdir.path, "list"])
     self.assertEqual(dvol.voluminous.getOutput(),
                      ["  VOLUME   BRANCH   CONTAINERS "])