def _do_base_tests(self, rwrepo):
        """Perform all tests in the given repository, it may be bare or nonbare"""
        # Manual instantiation.
        smm = Submodule(rwrepo, "\0" * 20)
        # Name needs to be set in advance.
        self.assertRaises(AttributeError, getattr, smm, "name")

        # Iterate - 1 submodule.
        sms = Submodule.list_items(rwrepo, self.k_subm_current)
        assert len(sms) == 1
        sm = sms[0]

        # At a different time, there is None.
        assert len(Submodule.list_items(rwrepo, self.k_no_subm_tag)) == 0

        assert sm.path == "git/ext/gitdb"
        assert sm.path != sm.name  # In our case, we have ids there, which don't equal the path.
        assert sm.url.endswith("github.com/gitpython-developers/gitdb.git")
        assert sm.branch_path == "refs/heads/master"  # the default ...
        assert sm.branch_name == "master"
        assert sm.parent_commit == rwrepo.head.commit
        # Size is always 0.
        assert sm.size == 0
        # The module is not checked-out yet.
        self.assertRaises(InvalidGitRepositoryError, sm.module)

        # ...which is why we can't get the branch either - it points into the module()
        # repository.
        self.assertRaises(InvalidGitRepositoryError, getattr, sm, "branch")

        # branch_path works, as it's just a string.
        assert isinstance(sm.branch_path, str)

        # Some commits earlier we still have a submodule, but it's at a different
        # commit.
        smold = next(Submodule.iter_items(rwrepo, self.k_subm_changed))
        assert smold.binsha != sm.binsha
        assert smold != sm  # the name changed

        # Force it to reread its information.
        del smold._url
        smold.url == sm.url  # noqa: B015  # FIXME: Should this be an assertion?

        # Test config_reader/writer methods.
        sm.config_reader()
        new_smclone_path = None  # Keep custom paths for later.
        new_csmclone_path = None  #
        if rwrepo.bare:
            with self.assertRaises(InvalidGitRepositoryError):
                with sm.config_writer() as cw:
                    pass
        else:
            with sm.config_writer() as writer:
                # For faster checkout, set the url to the local path.
                new_smclone_path = Git.polish_url(osp.join(self.rorepo.working_tree_dir, sm.path))
                writer.set_value("url", new_smclone_path)
                writer.release()
                assert sm.config_reader().get_value("url") == new_smclone_path
                assert sm.url == new_smclone_path
        # END handle bare repo
        smold.config_reader()

        # Cannot get a writer on historical submodules.
        if not rwrepo.bare:
            with self.assertRaises(ValueError):
                with smold.config_writer():
                    pass
        # END handle bare repo

        # Make the old into a new - this doesn't work as the name changed.
        self.assertRaises(ValueError, smold.set_parent_commit, self.k_subm_current)
        # the sha is properly updated
        smold.set_parent_commit(self.k_subm_changed + "~1")
        assert smold.binsha != sm.binsha

        # Raises if the sm didn't exist in new parent - it keeps its parent_commit
        # unchanged.
        self.assertRaises(ValueError, smold.set_parent_commit, self.k_no_subm_tag)

        # TODO: Test that, if a path is in the .gitmodules file, but not in the index,
        # then it raises.

        # TEST UPDATE
        ##############
        # Module retrieval is not always possible.
        if rwrepo.bare:
            self.assertRaises(InvalidGitRepositoryError, sm.module)
            self.assertRaises(InvalidGitRepositoryError, sm.remove)
            self.assertRaises(InvalidGitRepositoryError, sm.add, rwrepo, "here", "there")
        else:
            # It's not checked out in our case.
            self.assertRaises(InvalidGitRepositoryError, sm.module)
            assert not sm.module_exists()

            # Currently there is only one submodule.
            assert len(list(rwrepo.iter_submodules())) == 1
            assert sm.binsha != "\0" * 20

            # TEST ADD
            ###########
            # Preliminary tests.
            # Adding existing returns exactly the existing.
            sma = Submodule.add(rwrepo, sm.name, sm.path)
            assert sma.path == sm.path

            # No url and no module at path fails.
            self.assertRaises(ValueError, Submodule.add, rwrepo, "newsubm", "pathtorepo", url=None)

            # CONTINUE UPDATE
            #################

            # Let's update it - it's a recursive one too.
            newdir = osp.join(sm.abspath, "dir")
            os.makedirs(newdir)

            # Update fails if the path already exists non-empty.
            self.assertRaises(OSError, sm.update)
            os.rmdir(newdir)

            # Dry-run does nothing.
            sm.update(dry_run=True, progress=prog)
            assert not sm.module_exists()

            assert sm.update() is sm
            sm_repopath = sm.path  # Cache for later.
            assert sm.module_exists()
            assert isinstance(sm.module(), git.Repo)
            assert sm.module().working_tree_dir == sm.abspath

            # INTERLEAVE ADD TEST
            #####################
            # url must match the one in the existing repository (if submodule name
            # suggests a new one) or we raise.
            self.assertRaises(
                ValueError,
                Submodule.add,
                rwrepo,
                "newsubm",
                sm.path,
                "git://someurl/repo.git",
            )

            # CONTINUE UPDATE
            #################
            # We should have setup a tracking branch, which is also active.
            assert sm.module().head.ref.tracking_branch() is not None

            # Delete the whole directory and re-initialize.
            assert len(sm.children()) != 0
            # shutil.rmtree(sm.abspath)
            sm.remove(force=True, configuration=False)
            assert len(sm.children()) == 0
            # Dry-run does nothing.
            sm.update(dry_run=True, recursive=False, progress=prog)
            assert len(sm.children()) == 0

            sm.update(recursive=False)
            assert len(list(rwrepo.iter_submodules())) == 2
            assert len(sm.children()) == 1  # It's not checked out yet.
            csm = sm.children()[0]
            assert not csm.module_exists()
            csm_repopath = csm.path

            # Adjust the path of the submodules module to point to the local
            # destination.
            new_csmclone_path = Git.polish_url(osp.join(self.rorepo.working_tree_dir, sm.path, csm.path))
            with csm.config_writer() as writer:
                writer.set_value("url", new_csmclone_path)
            assert csm.url == new_csmclone_path

            # Dry-run does nothing.
            assert not csm.module_exists()
            sm.update(recursive=True, dry_run=True, progress=prog)
            assert not csm.module_exists()

            # Update recursively again.
            sm.update(recursive=True)
            assert csm.module_exists()

            # Tracking branch once again.
            assert csm.module().head.ref.tracking_branch() is not None

            # This flushed in a sub-submodule.
            assert len(list(rwrepo.iter_submodules())) == 2

            # Reset both heads to the previous version, verify that to_latest_revision
            # works.
            smods = (sm.module(), csm.module())
            for repo in smods:
                repo.head.reset("HEAD~2", working_tree=1)
            # END for each repo to reset

            # Dry-run does nothing.
            self.assertRaises(
                RepositoryDirtyError,
                sm.update,
                recursive=True,
                dry_run=True,
                progress=prog,
            )
            sm.update(recursive=True, dry_run=True, progress=prog, force=True)
            for repo in smods:
                assert repo.head.commit != repo.head.ref.tracking_branch().commit
            # END for each repo to check

            self.assertRaises(RepositoryDirtyError, sm.update, recursive=True, to_latest_revision=True)
            sm.update(recursive=True, to_latest_revision=True, force=True)
            for repo in smods:
                assert repo.head.commit == repo.head.ref.tracking_branch().commit
            # END for each repo to check
            del smods

            # If the head is detached, it still works (but warns).
            smref = sm.module().head.ref
            sm.module().head.ref = "HEAD~1"
            # If there is no tracking branch, we get a warning as well.
            csm_tracking_branch = csm.module().head.ref.tracking_branch()
            csm.module().head.ref.set_tracking_branch(None)
            sm.update(recursive=True, to_latest_revision=True)

            # to_latest_revision changes the child submodule's commit, it needs an
            # update now.
            csm.set_parent_commit(csm.repo.head.commit)

            # Undo the changes.
            sm.module().head.ref = smref
            csm.module().head.ref.set_tracking_branch(csm_tracking_branch)

            # REMOVAL OF REPOSITORY
            #######################
            # Must delete something.
            self.assertRaises(ValueError, csm.remove, module=False, configuration=False)

            # module() is supposed to point to gitdb, which has a child-submodule whose
            # URL is still pointing to GitHub. To save time, we will change it to:
            csm.set_parent_commit(csm.repo.head.commit)
            with csm.config_writer() as cw:
                cw.set_value("url", self._small_repo_url())
            csm.repo.index.commit("adjusted URL to point to local source, instead of the internet")

            # We have modified the configuration, hence the index is dirty, and the
            # deletion will fail.
            # NOTE: As we did a few updates in the meanwhile, the indices were reset.
            # Hence we create some changes.
            csm.set_parent_commit(csm.repo.head.commit)
            with sm.config_writer() as writer:
                writer.set_value("somekey", "somevalue")
            with csm.config_writer() as writer:
                writer.set_value("okey", "ovalue")
            self.assertRaises(InvalidGitRepositoryError, sm.remove)
            # If we remove the dirty index, it would work.
            sm.module().index.reset()
            # Still, we have the file modified.
            self.assertRaises(InvalidGitRepositoryError, sm.remove, dry_run=True)
            sm.module().index.reset(working_tree=True)

            # Enforce the submodule to be checked out at the right spot as well.
            csm.update()
            assert csm.module_exists()
            assert csm.exists()
            assert osp.isdir(csm.module().working_tree_dir)

            # This would work.
            assert sm.remove(force=True, dry_run=True) is sm
            assert sm.module_exists()
            sm.remove(force=True, dry_run=True)
            assert sm.module_exists()

            # But... we have untracked files in the child submodule.
            fn = join_path_native(csm.module().working_tree_dir, "newfile")
            with open(fn, "w") as fd:
                fd.write("hi")
            self.assertRaises(InvalidGitRepositoryError, sm.remove)

            # Forcibly delete the child repository.
            prev_count = len(sm.children())
            self.assertRaises(ValueError, csm.remove, force=True)
            # We removed sm, which removed all submodules. However, the instance we
            # have still points to the commit prior to that, where it still existed.
            csm.set_parent_commit(csm.repo.commit(), check=False)
            assert not csm.exists()
            assert not csm.module_exists()
            assert len(sm.children()) == prev_count
            # Now we have a changed index, as configuration was altered.
            # Fix this.
            sm.module().index.reset(working_tree=True)

            # Now delete only the module of the main submodule.
            assert sm.module_exists()
            sm.remove(configuration=False, force=True)
            assert sm.exists()
            assert not sm.module_exists()
            assert sm.config_reader().get_value("url")

            # Delete the rest.
            sm_path = sm.path
            sm.remove()
            assert not sm.exists()
            assert not sm.module_exists()
            self.assertRaises(ValueError, getattr, sm, "path")

            assert len(rwrepo.submodules) == 0

            # ADD NEW SUBMODULE
            ###################
            # Add a simple remote repo - trailing slashes are no problem.
            smid = "newsub"
            osmid = "othersub"
            nsm = Submodule.add(
                rwrepo,
                smid,
                sm_repopath,
                new_smclone_path + "/",
                None,
                no_checkout=True,
            )
            assert nsm.name == smid
            assert nsm.module_exists()
            assert nsm.exists()
            # It's not checked out.
            assert not osp.isfile(join_path_native(nsm.module().working_tree_dir, Submodule.k_modules_file))
            assert len(rwrepo.submodules) == 1

            # Add another submodule, but into the root, not as submodule.
            osm = Submodule.add(rwrepo, osmid, csm_repopath, new_csmclone_path, Submodule.k_head_default)
            assert osm != nsm
            assert osm.module_exists()
            assert osm.exists()
            assert osp.isfile(join_path_native(osm.module().working_tree_dir, "setup.py"))

            assert len(rwrepo.submodules) == 2

            # Commit the changes, just to finalize the operation.
            rwrepo.index.commit("my submod commit")
            assert len(rwrepo.submodules) == 2

            # Needs update, as the head changed.
            # It thinks it's in the history of the repo otherwise.
            nsm.set_parent_commit(rwrepo.head.commit)
            osm.set_parent_commit(rwrepo.head.commit)

            # MOVE MODULE
            #############
            # Invalid input.
            self.assertRaises(ValueError, nsm.move, "doesntmatter", module=False, configuration=False)

            # Renaming to the same path does nothing.
            assert nsm.move(sm_path) is nsm

            # Rename a module.
            nmp = join_path_native("new", "module", "dir") + "/"  # New module path.
            pmp = nsm.path
            assert nsm.move(nmp) is nsm
            nmp = nmp[:-1]  # Cut last /
            nmpl = to_native_path_linux(nmp)
            assert nsm.path == nmpl
            assert rwrepo.submodules[0].path == nmpl

            mpath = "newsubmodule"
            absmpath = join_path_native(rwrepo.working_tree_dir, mpath)
            open(absmpath, "w").write("")
            self.assertRaises(ValueError, nsm.move, mpath)
            os.remove(absmpath)

            # Now it works, as we just move it back.
            nsm.move(pmp)
            assert nsm.path == pmp
            assert rwrepo.submodules[0].path == pmp

            # REMOVE 'EM ALL
            ################
            # If a submodule's repo has no remotes, it can't be added without an
            # explicit url.
            osmod = osm.module()

            osm.remove(module=False)
            for remote in osmod.remotes:
                remote.remove(osmod, remote.name)
            assert not osm.exists()
            self.assertRaises(ValueError, Submodule.add, rwrepo, osmid, csm_repopath, url=None)
        # END handle bare mode

        # Error if there is no submodule file here.
        self.assertRaises(
            IOError,
            Submodule._config_parser,
            rwrepo,
            rwrepo.commit(self.k_no_subm_tag),
            True,
        )
Perform all tests in the given repository, it may be bare or nonbare
test/test_submodule.py