Example #1
0
  def testCommitManyFiles(self):
    # Regression test for "operating on too many entity groups in a
    # single transaction" error. This usually happens through the HTTP API
    # when lazy file objects are created from the manifest and then end up
    # being evaluated one-by-one inside the commit code path.
    test_user = users.TitanUser('*****@*****.**')
    changeset = self.vcs.new_staging_changeset(created_by=test_user)
    for i in range(100):
      files.File('/foo%s' % i, changeset=changeset).write(str(i))
    changeset.finalize_associated_files()
    # Recreate the changeset from scratch, and reassociate lazy file objects.
    changeset = versions.Changeset(changeset.num)
    for i in range(100):
      changeset.associate_file(files.File('/foo%s' % i, changeset=changeset))
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)

    # Test again, but test the list_files code path with force=True.
    changeset = self.vcs.new_staging_changeset(created_by=test_user)
    for i in range(100):
      files.File('/foo%s' % i, changeset=changeset).write(str(i))
    changeset.finalize_associated_files()
    # Recreate the changeset from scratch, and reassociate lazy file objects.
    changeset = versions.Changeset(changeset.num)
    self.vcs.commit(changeset, force=True)
Example #2
0
  def testNamespaceState(self):
    self.make_namespaced_testdata()

    # Verify the state of the filesystem in the default namespace.
    changeset = versions.Changeset(2)
    self.assertEqual('foo', files.File('/foo', changeset=changeset).content)
    # Files from other namespaces should not exist.
    self.assertFalse(files.File('/bar', changeset=changeset).exists)
    self.assertFalse(files.File('/qux', changeset=changeset).exists)
    changeset = versions.Changeset(4)
    # Verify the current state of the filesystem in the default namespace.
    # Force use of dynamic _FilePointer by not passing the changeset arg.
    self.assertEqual('newfoo', files.File('/foo').content)
    self.assertEqual('bar', files.File('/bar').content)

    # Verify the state of the filesystem in the 'aaa' namespace at changeset 2.
    changeset = versions.Changeset(2, namespace='aaa')
    titan_file = files.File('/foo', changeset=changeset, namespace='aaa')
    self.assertEqual('foo-aaa', titan_file.content)
    titan_file = files.File('/bar', changeset=changeset, namespace='aaa')
    self.assertEqual('bar-aaa', titan_file.content)
    # Files from other namespaces should not exist.
    titan_file = files.File('/qux', changeset=changeset, namespace='aaa')
    self.assertFalse(titan_file.exists)

    # Verify the state of the filesystem in the 'aaa' namespace at changeset 4.
    changeset = versions.Changeset(4, namespace='aaa')
    titan_file = files.File('/foo', changeset=changeset, namespace='aaa')
    self.assertEqual('newfoo-aaa', titan_file.content)
    titan_file = files.File('/bar', changeset=changeset, namespace='aaa')
    self.assertFalse(titan_file.exists)

    # Verify the current state of the filesystem in the 'aaa' namespace.
    # Force use of dynamic _FilePointer by not passing the changeset arg.
    self.assertEqual('newfoo-aaa', files.File('/foo', namespace='aaa').content)
    self.assertFalse(files.File('/bar', namespace='aaa').exists)

    # Verify the state of the filesystem in the 'bbb' namespace at changeset 2.
    changeset = versions.Changeset(2, namespace='bbb')
    titan_file = files.File('/foo', changeset=changeset, namespace='bbb')
    self.assertEqual('foo-bbb', titan_file.content)
    titan_file = files.File('/qux', changeset=changeset, namespace='bbb')
    self.assertEqual('qux-bbb', titan_file.content)
    # Files from other namespaces should not exist.
    titan_file = files.File('/bar', changeset=changeset, namespace='bbb')
    self.assertFalse(titan_file.exists)
Example #3
0
  def testCommit(self):
    test_user = users.TitanUser('*****@*****.**')
    changeset = self.vcs.new_staging_changeset(created_by=test_user)

    # Shouldn't be able to submit changesets with no changed files:
    self.assertRaises(versions.CommitError, self.vcs.commit, changeset,
                      force=True)

    # Verify that the auto_current_user_add property is overwritten.
    self.assertEqual('*****@*****.**', str(changeset.created_by))

    # Before a changeset is committed, its associated files must be finalized
    # to indicate that the object's files can be trusted for strong consistency.
    files.File('/foo', changeset=changeset).write('')
    self.assertRaises(versions.ChangesetError, self.vcs.commit, changeset)
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)

    # When a changeset is committed, a new changeset is created (so that
    # changes are always sequential) with a created time. The old changeset
    # is marked as deleted by submit.
    staged_changeset = versions.Changeset(1)
    self.assertEqual(CHANGESET_DELETED_BY_SUBMIT, staged_changeset.status)
    self.assertEqual(CHANGESET_SUBMITTED, final_changeset.status)

    # Also, the changesets are linked to each other:
    self.assertEqual(1, final_changeset.linked_changeset_num)
    self.assertEqual(2, staged_changeset.linked_changeset_num)
    self.assertEqual(versions.Changeset(1),
                     final_changeset.linked_changeset)
    self.assertEqual(versions.Changeset(2),
                     staged_changeset.linked_changeset)

    # Verify base_path properties also:
    self.assertEqual('/_titan/ver/2', final_changeset.base_path)
    self.assertEqual('/_titan/ver/1',
                     final_changeset.linked_changeset_base_path)

    # Verify that the auto_current_user_add property is overwritten in the
    # final_changeset because it was overwritten in the staged_changeset.
    self.assertEqual('*****@*****.**', str(final_changeset.created_by))

    # After commit(), files in a changeset cannot be modified.
    titan_file = files.File('/foo', changeset=changeset)
    self.assertRaises(versions.ChangesetError, titan_file.write, '')
    self.assertRaises(versions.ChangesetError, titan_file.delete)
Example #4
0
  def testGetChangeset(self):
    self.assertIsNone(self.vcs.get_last_submitted_changeset())
    self.make_testdata()

    # Creating a new changeset should not affect get_last_submitted_changeset().
    self.assertEqual(17, self.vcs.get_last_submitted_changeset().num)
    self.vcs.new_staging_changeset()
    self.assertEqual(17, self.vcs.get_last_submitted_changeset().num)

    # List files changed in a deleted-by-submit changeset and a final changeset.
    # NOTE: the status checks here are merely for testing purposes.
    # VersionedFile objects should never be trusted for canonical version info.
    self.assertRaises(
        versions.ChangesetError,
        lambda: versions.Changeset(12).list_files('/'))
    self.assertRaises(
        versions.ChangesetError, versions.Changeset(12).get_files)
    titan_files = versions.Changeset(13).list_files('/')
    self.assertEqual(titan_files['/foo'].meta.status, FILE_EDITED)
    self.assertEqual(titan_files['/bar'].meta.status, FILE_DELETED)
    self.assertEqual(titan_files['/baz'].meta.status, FILE_EDITED)
    self.assertEqual(titan_files['/qux'].meta.status, FILE_EDITED)

    # Test serialize().
    changeset = versions.Changeset(12)
    expected_data = {
        'num': 12,
        'namespace': changeset.namespace,
        'base_changeset_num': changeset.base_changeset_num,
        'created': changeset.created,
        'status': CHANGESET_DELETED_BY_SUBMIT,
        'base_path': '/_titan/ver/12',
        'linked_changeset_base_path': '/_titan/ver/13',
        'linked_changeset_num': 13,
        'created_by': '*****@*****.**',
    }
    self.assertEqual(expected_data, changeset.serialize())
Example #5
0
  def make_testdata(self):
    for _ in range(1, 10):
      self.vcs.new_staging_changeset()

    # Changeset 11 (was changeset 10 before commit):
    changeset = self.vcs.new_staging_changeset()
    self.assertIsNone(changeset.base_changeset)
    files.File('/foo', changeset=changeset).write('foo')
    files.File('/bar', changeset=changeset).write('bar')
    files.File('/qux', changeset=changeset).write('qux')
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)
    # For testing, move the submitted datetime to 31 days ago.
    changeset_ent = versions.Changeset(11).changeset_ent
    created = datetime.datetime.now() - datetime.timedelta(days=31)
    changeset_ent.created = created
    changeset_ent.put()

    # Changset 13:
    changeset = self.vcs.new_staging_changeset()
    self.assertEqual(11, changeset.base_changeset.num)
    files.File('/foo', changeset=changeset).write('foo2')  # edit
    files.File('/bar', changeset=changeset).delete()  # delete
    files.File('/baz', changeset=changeset).write('baz')  # create
    files.File('/qux', changeset=changeset).write('qux2')  # edit
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)
    self.assertEqual(13, self.vcs.get_last_submitted_changeset().num)
    self.assertIsNone(self.vcs.get_last_submitted_changeset().namespace)

    # Changset 15:
    changeset = self.vcs.new_staging_changeset()
    self.assertEqual(13, changeset.base_changeset.num)
    files.File('/foo', changeset=changeset).delete()  # delete
    files.File('/bar', changeset=changeset).delete()  # delete
    files.File('/baz', changeset=changeset).write('baz2')  # edit
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)

    # Changset 17:
    changeset = self.vcs.new_staging_changeset()
    modified_by = users.TitanUser('*****@*****.**')
    files.File('/foo', changeset=changeset).write(
        'foo3', modified_by=modified_by)  # re-create
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)
Example #6
0
    def post(self):
        """POST handler."""
        try:
            namespace = self.request.get('namespace', None)
            save_manifest = self.request.get('save_manifest', 'true') == 'true'
            vcs = versions.VersionControlService()
            staging_changeset = versions.Changeset(int(
                self.request.get('changeset')),
                                                   namespace=namespace)
            force = bool(self.request.get('force', False))
            manifest = self.request.POST.get('manifest', None)
            if not force and not manifest or force and manifest:
                self.error(400)
                logging.error(
                    'Exactly one of "manifest" or "force" params is required')
                return

            # If a client has full knowledge of the files uploaded to a changeset,
            # the "manifest" param may be given to ensure a strongly consistent
            # commit. If given, associate the files to the changeset and finalize it.
            if manifest:
                manifest = json.loads(manifest)
                for path in manifest:
                    titan_file = files.File(path,
                                            changeset=staging_changeset,
                                            namespace=namespace,
                                            _internal=True)
                    staging_changeset.associate_file(titan_file)
                staging_changeset.finalize_associated_files()

            final_changeset = vcs.commit(staging_changeset,
                                         force=force,
                                         save_manifest=save_manifest)
            self.write_json_response(final_changeset)
            self.response.set_status(201)
        except (TypeError, ValueError):
            self.error(400)
            logging.exception('Bad request:')
Example #7
0
  def testNewStagingChangeset(self):
    changeset = self.vcs.new_staging_changeset()

    # Verify the auto_current_user_add property.
    self.assertEqual('*****@*****.**', str(changeset.created_by))

    # First changeset ever created. Should be #1 and have status of 'new'.
    self.assertEqual(changeset.num, 1)
    old_datetime = changeset.created
    self.assertTrue(isinstance(old_datetime, datetime.datetime))
    self.assertEqual(CHANGESET_STAGING, changeset.status)

    changeset = self.vcs.new_staging_changeset()
    self.assertEqual(changeset.num, 2)

    # Test Changeset __eq__ and __ne__.
    self.assertEqual(versions.Changeset(2), versions.Changeset(2))
    self.assertNotEqual(versions.Changeset(2), versions.Changeset(3))
    self.assertNotEqual(
        versions.Changeset(2, namespace='aaa'), versions.Changeset(2))
Example #8
0
  def make_namespaced_testdata(self):
    meta = {'color': 'blue'}

    # Filesystem views at final changesets (the whole filesystem, not diffs):
    #
    # DEFAULT NAMESPACE.
    #   Changeset 2:
    #     /foo: 'foo'
    #   Changeset 4:
    #     /foo: 'newfoo'
    #     /bar: 'bar'
    #
    # NAMESPACE 'aaa'.
    #   Changeset 2:
    #     /foo: 'foo-aaa'
    #     /bar: 'bar-aaa' meta:{'color':'blue'}
    #   Changeset 4:
    #     /foo: 'newfoo-aaa'
    #
    # NAMESPACE 'bbb'.
    #   Changeset 2:
    #     /foo: 'foo-bbb'
    #     /qux: 'qux-bbb'

    # Changeset 2, default namespace (was changeset 1 before commit).
    changeset = self.vcs.new_staging_changeset()
    self.assertEqual(1, changeset.num)
    self.assertIsNone(changeset.base_changeset)
    self.assertIsNone(changeset.namespace)
    files.File('/foo', changeset=changeset).write('foo')
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)
    self.assertEqual(2, final_changeset.num)
    self.assertIsNone(final_changeset.namespace)

    # Changeset 4, default namespace.
    changeset = self.vcs.new_staging_changeset()
    self.assertEqual(3, changeset.num)
    self.assertEqual(2, changeset.base_changeset.num)
    files.File('/foo', changeset=changeset).write('newfoo')
    files.File('/bar', changeset=changeset).write('bar')
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)
    self.assertEqual(4, final_changeset.num)
    self.assertIsNone(final_changeset.namespace)

    # Changeset 2, 'aaa' namespace.
    changeset = self.vcs.new_staging_changeset(namespace='aaa')
    self.assertEqual(1, changeset.num)
    self.assertIsNone(changeset.base_changeset)
    self.assertEqual('aaa', changeset.namespace)
    files.File('/foo', changeset=changeset, namespace='aaa').write('foo-aaa')
    titan_file = files.File('/bar', changeset=changeset, namespace='aaa').write(
        'bar-aaa', meta=meta)
    self.assertEqual('blue', titan_file.meta.color)
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)
    self.assertEqual(2, final_changeset.num)
    self.assertEqual('aaa', final_changeset.namespace)

    # Changeset 2, 'bbb' namespace (some filenames overlap 'aaa' files).
    changeset = self.vcs.new_staging_changeset(namespace='bbb')
    self.assertEqual(1, changeset.num)
    files.File('/foo', changeset=changeset, namespace='bbb').write('foo-bbb')
    files.File('/qux', changeset=changeset, namespace='bbb').write('qux-bbb')
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)
    self.assertEqual(2, final_changeset.num)
    self.assertEqual('bbb', final_changeset.namespace)
    self.assertEqual(
        'bbb', self.vcs.get_last_submitted_changeset(namespace='bbb').namespace)

    # Changeset 4, 'aaa' namespace.
    changeset = self.vcs.new_staging_changeset(namespace='aaa')
    self.assertEqual(3, changeset.num)
    self.assertEqual(2, changeset.base_changeset.num)
    files.File('/foo', changeset=changeset, namespace='aaa').write('newfoo-aaa')
    titan_file = files.File(
        '/bar', changeset=changeset, namespace='aaa').delete()
    # While we're here, verify that a marked-for-delete file (whose content and
    # meta have been nullified) is restored correctly if undeleted.
    titan_file = files.File(
        '/bar', changeset=changeset, namespace='aaa', _allow_deleted_files=True)
    self.assertRaises(AttributeError, lambda: titan_file.meta.color)
    # This should restore the file's existence, but the metadata should NOT
    # be present since it's a brand new file after deletion.
    titan_file.write('bar-restored-aaa')
    self.assertEqual('bar-restored-aaa', titan_file.content)
    self.assertRaises(AttributeError, lambda: titan_file.meta.color)
    # However, if the file is reverted THEN written, metadata should restore.
    changeset.revert_file(titan_file)
    titan_file.write(meta={'foo': 'foo'})
    self.assertEqual('bar-aaa', titan_file.content)
    self.assertEqual('blue', titan_file.meta.color)
    # Delete it again, since that's the state we actually want.
    files.File('/bar', changeset=changeset, namespace='aaa').delete()
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)
    self.assertEqual(4, final_changeset.num)
    self.assertEqual('aaa', final_changeset.namespace)

    # Cannot revert a file in a submitted changeset.
    changeset = versions.Changeset(4, namespace='aaa')
    titan_file = files.File('/foo', changeset=changeset, namespace='aaa')
    self.assertRaises(
        versions.ChangesetError, lambda: changeset.revert_file(titan_file))

    # File namespaces must match their associated changeset namespace.
    self.assertRaises(
        versions.NamespaceMismatchError,
        lambda: files.File('/foo', changeset=changeset))
    self.assertRaises(
        versions.NamespaceMismatchError,
        lambda: files.File('/foo', changeset=changeset, namespace='bbb'))
Example #9
0
  def testManifestedViewsAndRebase(self):
    # None of the following behaviors, including new_staging_changeset's setting
    # of base_changeset, should rely on eventually-consistent queries.
    # Verify this behavior by simulating a never-eventually-consistent HRD.
    policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=0)
    self.datastore_stub.SetConsistencyPolicy(policy)

    self.make_namespaced_testdata()

    # Staging changeset 5.
    changeset = self.vcs.new_staging_changeset()
    self.assertEqual(5, changeset.num)
    self.assertEqual(4, changeset.base_changeset.num)
    files.File('/bar', changeset=changeset).delete()
    files.File('/qux', changeset=changeset).write('qux')

    # Changeset 6 and 7.
    changeset = self.vcs.new_staging_changeset()
    files.File('/foo', changeset=changeset).write('NEWfoo')
    changeset.finalize_associated_files()
    final_changeset = self.vcs.commit(changeset)
    self.assertEqual(7, final_changeset.num)

    # Changeset 8 and 9.
    changeset = self.vcs.new_staging_changeset()
    for i in range(3200):
      files.File('/foo{}'.format(i), changeset=changeset).write('')
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)

    # MANIFESTED FILESYSTEM VIEWS.
    # - Files read through a 'new' or 'submitted' changeset: first pull
    #   from the changeset, then fallback to the base_changeset's manifest.
    #   The manifested filesystem should by overlaid by the changeset's files.
    # - Files read through a 'deleted' or 'deleted-by-submit' changeset:
    #   always pull from the base_changeset's manifest, ignore the changeset
    #   since its changes will manifest when viewed at it's corresponding
    #   submitted changeset.
    # - File modifications must go through 'new' changesets.
    changeset = versions.Changeset(1)  # 'deleted-by-submit'
    self.assertFalse(files.File('/foo', changeset=changeset).exists)
    self.assertFalse(files.File('/bar', changeset=changeset).exists)
    self.assertIsNone(changeset._num_manifest_shards)

    changeset = versions.Changeset(2)  # 'submitted'
    self.assertEqual('foo', files.File('/foo', changeset=changeset).content)
    self.assertFalse(files.File('/bar', changeset=changeset).exists)
    self.assertEqual(1, changeset._num_manifest_shards)

    changeset = versions.Changeset(3)  # 'deleted-by-submit'
    self.assertEqual('foo', files.File('/foo', changeset=changeset).content)
    self.assertFalse(files.File('/bar', changeset=changeset).exists)
    self.assertIsNone(changeset._num_manifest_shards)

    changeset = versions.Changeset(4)  # 'submitted'
    self.assertEqual('newfoo', files.File('/foo', changeset=changeset).content)
    self.assertEqual('bar', files.File('/bar', changeset=changeset).content)
    self.assertFalse(files.File('/qux', changeset=changeset).exists)
    self.assertEqual(1, changeset._num_manifest_shards)

    changeset = versions.Changeset(5)  # 'staging'
    self.assertEqual('newfoo', files.File('/foo', changeset=changeset).content)
    self.assertEqual('qux', files.File('/qux', changeset=changeset).content)
    self.assertFalse(files.File('/bar', changeset=changeset).exists)
    self.assertIsNone(changeset._num_manifest_shards)

    changeset = versions.Changeset(6)  # 'deleted-by-submit'
    self.assertEqual('newfoo', files.File('/foo', changeset=changeset).content)
    self.assertEqual('bar', files.File('/bar', changeset=changeset).content)
    self.assertIsNone(changeset._num_manifest_shards)

    changeset = versions.Changeset(7)  # 'submitted'
    self.assertEqual('NEWfoo', files.File('/foo', changeset=changeset).content)
    self.assertEqual('bar', files.File('/bar', changeset=changeset).content)
    self.assertEqual(1, changeset._num_manifest_shards)

    changeset = versions.Changeset(9)  # 'submitted'
    self.assertEqual(4, changeset._num_manifest_shards)
    # Verify that hash is evenly distributing the paths over the shards by
    # verifying the shards are not nearly full to 1000 paths.
    manifest_shard = changeset._get_manifest_shard_ent('/foo0')
    self.assertLess(900, len(manifest_shard.paths_to_changeset_num))

    # Rebase the staging changeset and verify the new manifest files.
    changeset = versions.Changeset(5)  # 'staging'
    changeset.rebase(versions.Changeset(7))
    self.assertEqual('NEWfoo', files.File('/foo', changeset=changeset).content)
    self.assertFalse(files.File('/bar', changeset=changeset).exists)
    self.assertEqual('qux', files.File('/qux', changeset=changeset).content)

    # Cannot rebase to anything but submitted changesets.
    self.assertRaises(
        versions.ChangesetRebaseError, changeset.rebase, versions.Changeset(5))
    self.assertRaises(
        versions.ChangesetRebaseError, changeset.rebase, versions.Changeset(6))
    # Namespaces must match.
    self.assertRaises(
        versions.NamespaceMismatchError, changeset.rebase,
        versions.Changeset(7, namespace='aaa'))
Example #10
0
  def testListFiles(self):
    changeset = self.vcs.new_staging_changeset()
    # Test list files within first staging changeset.
    self.assertEqual(
        [], changeset.list_files('/').keys())
    self.assertEqual(
        [], changeset.list_files('/', include_manifested=True).keys())
    files.File('/foo', changeset=changeset).write('foo')
    files.File('/a/foo', changeset=changeset).write('foo')
    files.File('/a/bar', changeset=changeset).write('bar')
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)  # Changeset 2.

    changeset = self.vcs.new_staging_changeset()
    files.File('/foo', changeset=changeset).delete()
    changeset.finalize_associated_files()
    self.vcs.commit(changeset)  # Changeset 4.

    titan_files = versions.Changeset(2).list_files(dir_path='/')
    self.assertEqual(['/foo'], titan_files.keys())
    titan_files = versions.Changeset(2).list_files(dir_path='/', recursive=True)
    self.assertEqual(['/a/bar', '/a/foo', '/foo'], titan_files.keys())
    titan_files = versions.Changeset(2).list_files(dir_path='/a/')
    self.assertEqual(['/a/bar', '/a/foo'], titan_files.keys())
    # Test limit and offset.
    titan_files = versions.Changeset(2).list_files(
        dir_path='/', recursive=True, limit=1, offset=1)
    self.assertEqual(['/a/foo'], titan_files.keys())

    # Test included_deleted.
    titan_files = versions.Changeset(4).list_files(dir_path='/')
    self.assertEqual(['/foo'], titan_files.keys())
    self.assertEqual(
        versions.FileStatus.deleted, titan_files.values()[0].meta.status)
    titan_files = versions.Changeset(4).list_files(
        dir_path='/', include_deleted=False)
    self.assertEqual([], titan_files.keys())

    # Test include_manifested on a finalized changeset.
    titan_files = versions.Changeset(4).list_files(
        '/', include_deleted=False, include_manifested=True)
    self.assertEqual([], titan_files.keys())
    titan_files = versions.Changeset(4).list_files('/', include_manifested=True)
    self.assertEqual(['/foo'], titan_files.keys())
    titan_files = versions.Changeset(4).list_files(
        '/', recursive=True, include_manifested=True)
    self.assertEqual(['/a/bar', '/a/foo', '/foo'], titan_files.keys())
    titan_files = versions.Changeset(4).list_files(
        '/a/', recursive=True, include_manifested=True)
    self.assertEqual(['/a/bar', '/a/foo'], titan_files.keys())
    titan_files = versions.Changeset(4).list_files(
        '/b', recursive=True, include_manifested=True)
    self.assertEqual([], titan_files.keys())

    # Test include_manifested on a staging changeset.
    changeset = self.vcs.new_staging_changeset()  # Changeset 5 (staging).
    files.File('/a/qux', changeset=changeset).write('qux')
    titan_files = versions.Changeset(5).list_files(
        '/', include_deleted=False, include_manifested=True)
    self.assertEqual([], titan_files.keys())
    titan_files = versions.Changeset(5).list_files(
        '/', recursive=True, include_manifested=True)
    self.assertEqual(['/a/bar', '/a/foo', '/a/qux'], titan_files.keys())
    titan_files = versions.Changeset(5).list_files(
        '/a/', recursive=True, include_manifested=True)
    self.assertEqual(['/a/bar', '/a/foo', '/a/qux'], titan_files.keys())
    titan_files = versions.Changeset(5).list_files(
        '/b', recursive=True, include_manifested=True)
    self.assertEqual([], titan_files.keys())
    # Verify reading a manifested and a non-manifested file.
    titan_files = versions.Changeset(5).list_files(
        '/', recursive=True, include_manifested=True)
    self.assertEqual('foo', titan_files['/a/foo'].content)
    self.assertEqual('qux', titan_files['/a/qux'].content)

    # Error handling.
    self.assertRaises(
        ValueError,
        versions.Changeset(2).list_files,
        dir_path='/', include_manifested=True, depth=1, filters=[], order=[])
    self.assertRaises(
        ValueError, versions.Changeset(2).list_files,
        '/', include_manifested=True, limit=10, offset=1)