aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2006-12-07 02:20:15 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2006-12-07 02:20:15 +0000
commit558d9af4849ff4fd1374f8852201b890689c7a51 (patch)
tree8ab7ac3751b0a81ac04c50e494737bde605a1904
parentb808a1281c94b0bd006bbb16a69a114f962f9cc3 (diff)
downloadpaludis-558d9af4849ff4fd1374f8852201b890689c7a51.tar.gz
paludis-558d9af4849ff4fd1374f8852201b890689c7a51.tar.xz
Split out common cache code. Make the cache more sensitive to misuse.
-rw-r--r--paludis/files.m41
-rw-r--r--paludis/repositories/portage/portage_repository.cc76
-rw-r--r--paludis/repositories/vdb/vdb_repository.cc114
-rw-r--r--paludis/repositories/vdb/vdb_repository.hh1
-rw-r--r--paludis/repository.hh1
-rw-r--r--paludis/repository_name_cache.cc185
-rw-r--r--paludis/repository_name_cache.hh56
-rw-r--r--paludis/repository_name_cache_TEST.cc171
-rwxr-xr-xpaludis/repository_name_cache_TEST_cleanup.sh10
-rwxr-xr-xpaludis/repository_name_cache_TEST_setup.sh21
10 files changed, 484 insertions, 152 deletions
diff --git a/paludis/files.m4 b/paludis/files.m4
index 068c8df..cd6fc06 100644
--- a/paludis/files.m4
+++ b/paludis/files.m4
@@ -28,6 +28,7 @@ add(`paludis', `hh', `cc')
add(`portage_dep_lexer', `hh', `cc', `test')
add(`portage_dep_parser', `hh', `cc', `test')
add(`repository', `hh', `cc', `sr')
+add(`repository_name_cache', `hh', `cc', `test', `testscript')
add(`repository_so_loader', `cc')
add(`syncer', `hh', `cc', `sr')
add(`version_metadata', `hh', `cc', `sr')
diff --git a/paludis/repositories/portage/portage_repository.cc b/paludis/repositories/portage/portage_repository.cc
index 2187695..72adc00 100644
--- a/paludis/repositories/portage/portage_repository.cc
+++ b/paludis/repositories/portage/portage_repository.cc
@@ -36,6 +36,7 @@
#include <paludis/package_database.hh>
#include <paludis/package_database_entry.hh>
#include <paludis/portage_dep_parser.hh>
+#include <paludis/repository_name_cache.hh>
#include <paludis/syncer.hh>
#include <paludis/util/collection_concrete.hh>
#include <paludis/util/dir_iterator.hh>
@@ -93,9 +94,6 @@ namespace paludis
/// Map for virtuals.
typedef MakeHashedMap<QualifiedPackageName, PackageDepAtom::ConstPointer>::Type VirtualsMap;
- /// Map for name caches.
- typedef MakeHashedMap<PackageNamePart, std::list<CategoryNamePart> >::Type NameCacheMap;
-
/**
* Implementation data for a PortageRepository.
*
@@ -176,11 +174,7 @@ namespace paludis
/// Have we loaded our virtuals?
bool has_our_virtuals;
- /// Name cache map
- mutable NameCacheMap name_cache_map;
-
- /// Can we use our name cache map?
- mutable bool can_use_name_cache_map;
+ RepositoryNameCache::Pointer names_cache;
PortageRepository * const repo;
};
@@ -199,7 +193,7 @@ namespace paludis
entries_ptr(PortageRepositoryEntriesMaker::get_instance()->find_maker(
params.entry_format)(params.environment, r, p)),
has_our_virtuals(false),
- can_use_name_cache_map(true),
+ names_cache(new RepositoryNameCache(p.names_cache, r)),
repo(r)
{
}
@@ -1024,72 +1018,18 @@ PortageRepository::do_use_expand_value(const UseFlagName & u) const
void
PortageRepository::regenerate_cache() const
{
- if (_imp->params.names_cache == FSEntry("/var/empty"))
- return;
-
- Context context("When generating Portage repository names cache at '"
- + stringify(_imp->params.names_cache) + "':");
-
- if (_imp->params.names_cache.is_directory())
- for (DirIterator i(_imp->params.names_cache, true), i_end ; i != i_end ; ++i)
- FSEntry(*i).unlink();
-
- _imp->params.names_cache.dirname().mkdir();
- FSEntry(_imp->params.names_cache).mkdir();
-
- MakeHashedMap<std::string, std::string>::Type m;
-
- CategoryNamePartCollection::ConstPointer cats(category_names());
- for (CategoryNamePartCollection::Iterator c(cats->begin()), c_end(cats->end()) ;
- c != c_end ; ++c)
- {
- QualifiedPackageNameCollection::ConstPointer pkgs(package_names(*c));
- for (QualifiedPackageNameCollection::Iterator p(pkgs->begin()), p_end(pkgs->end()) ;
- p != p_end ; ++p)
- m[stringify(p->package)].append(stringify(*c) + "\n");
- }
-
- for (MakeHashedMap<std::string, std::string>::Type::const_iterator e(m.begin()), e_end(m.end()) ;
- e != e_end ; ++e)
- {
- std::ofstream f(stringify(_imp->params.names_cache / stringify(e->first)).c_str());
- if (! f)
- {
- Log::get_instance()->message(ll_warning, lc_context, "Cannot write to '"
- + stringify(_imp->params.names_cache) + "'");
- continue;
- }
- f << e->second;
- }
+ _imp->names_cache->regenerate_cache();
}
CategoryNamePartCollection::ConstPointer
PortageRepository::do_category_names_containing_package(const PackageNamePart & p) const
{
- if (_imp->params.names_cache == FSEntry("/var/empty") || ! _imp->can_use_name_cache_map)
+ if (! _imp->names_cache->usable())
return Repository::do_category_names_containing_package(p);
- CategoryNamePartCollection::Pointer result(new CategoryNamePartCollection::Concrete);
- NameCacheMap::iterator r(_imp->name_cache_map.find(p));
-
- if (_imp->name_cache_map.end() == r)
- {
- r = _imp->name_cache_map.insert(std::make_pair(p, std::list<CategoryNamePart>())).first;
-
- FSEntry ff(_imp->params.names_cache / stringify(p));
- if (ff.exists())
- {
- std::ifstream f(stringify(ff).c_str());
- if (! f)
- Log::get_instance()->message(ll_warning, lc_context, "Cannot read '" + stringify(ff) + "'");
- std::string line;
- while (std::getline(f, line))
- r->second.push_back(CategoryNamePart(line));
- }
- }
-
- std::copy(r->second.begin(), r->second.end(), result->inserter());
+ CategoryNamePartCollection::ConstPointer result(
+ _imp->names_cache->category_names_containing_package(p));
- return result;
+ return result ? result : Repository::do_category_names_containing_package(p);
}
diff --git a/paludis/repositories/vdb/vdb_repository.cc b/paludis/repositories/vdb/vdb_repository.cc
index 2c4cca5..b7d14d5 100644
--- a/paludis/repositories/vdb/vdb_repository.cc
+++ b/paludis/repositories/vdb/vdb_repository.cc
@@ -27,6 +27,7 @@
#include <paludis/config_file.hh>
#include <paludis/match_package.hh>
#include <paludis/package_database.hh>
+#include <paludis/repository_name_cache.hh>
#include <paludis/util/collection_concrete.hh>
#include <paludis/util/dir_iterator.hh>
@@ -59,8 +60,6 @@ using namespace paludis;
namespace
{
- typedef MakeHashedMap<PackageNamePart, std::list<CategoryNamePart> >::Type NameCacheMap;
-
/**
* Holds an entry in a VDB.
*/
@@ -320,8 +319,6 @@ namespace paludis
/// Provides cache
FSEntry provides_cache;
- FSEntry names_cache;
-
/// Do we have entries loaded?
mutable bool entries_valid;
@@ -341,10 +338,12 @@ namespace paludis
/// Provieds data
mutable RepositoryProvidesInterface::ProvidesCollection::Pointer provides;
- mutable NameCacheMap name_cache_map;
+ const FSEntry names_cache_dir;
+
+ RepositoryNameCache::Pointer names_cache;
/// Constructor.
- Implementation(const VDBRepositoryParams &);
+ Implementation(const VDBRepository * const, const VDBRepositoryParams &);
/// Destructor.
~Implementation();
@@ -353,7 +352,8 @@ namespace paludis
void invalidate() const;
};
- Implementation<VDBRepository>::Implementation(const VDBRepositoryParams & p) :
+ Implementation<VDBRepository>::Implementation(const VDBRepository * const r,
+ const VDBRepositoryParams & p) :
db(p.package_database),
env(p.environment),
location(p.location),
@@ -361,9 +361,10 @@ namespace paludis
buildroot(p.buildroot),
world_file(p.world),
provides_cache(p.provides_cache),
- names_cache(p.names_cache),
entries_valid(false),
- provides(0)
+ provides(0),
+ names_cache_dir(p.names_cache),
+ names_cache(new RepositoryNameCache(names_cache_dir, r))
{
}
@@ -506,7 +507,7 @@ VDBRepository::VDBRepository(const VDBRepositoryParams & p) :
.mirrors_interface(0)
.provides_interface(this)
.virtuals_interface(0)),
- PrivateImplementationPattern<VDBRepository>(new Implementation<VDBRepository>(p))
+ PrivateImplementationPattern<VDBRepository>(new Implementation<VDBRepository>(this, p))
{
RepositoryInfoSection::Pointer config_info(new RepositoryInfoSection("Configuration information"));
@@ -515,7 +516,7 @@ VDBRepository::VDBRepository(const VDBRepositoryParams & p) :
config_info->add_kv("format", "vdb");
config_info->add_kv("world", stringify(_imp->world_file));
config_info->add_kv("provides_cache", stringify(_imp->provides_cache));
- config_info->add_kv("names_cache", stringify(_imp->names_cache));
+ config_info->add_kv("names_cache", stringify(_imp->names_cache_dir));
config_info->add_kv("buildroot", stringify(_imp->buildroot));
_info->add_section(config_info);
@@ -1253,10 +1254,20 @@ VDBRepository::load_provided_using_cache() const
std::string version;
std::getline(provides_cache, version);
- if (version != "paludis-1")
+ if (version != "paludis-2")
+ {
+ Log::get_instance()->message(ll_warning, lc_no_context, "Can't use provides cache at '"
+ + stringify(_imp->provides_cache) + "' because format '" + version + "' is not 'paludis-2'");
+ return false;
+ }
+
+ std::string for_name;
+ std::getline(provides_cache, for_name);
+ if (for_name != stringify(name()))
{
Log::get_instance()->message(ll_warning, lc_no_context, "Can't use provides cache at '"
- + stringify(_imp->provides_cache) + "' because format '" + version + "' is not 'paludis-1'");
+ + stringify(_imp->provides_cache) + "' because it was generated for repository '"
+ + for_name + "'. You must not have multiple name caches at the same location.");
return false;
}
@@ -1357,7 +1368,7 @@ void
VDBRepository::regenerate_cache() const
{
regenerate_provides_cache();
- regenerate_names_cache();
+ _imp->names_cache->regenerate_cache();
}
void
@@ -1383,7 +1394,8 @@ VDBRepository::regenerate_provides_cache() const
return;
}
- f << "paludis-1" << std::endl;
+ f << "paludis-2" << std::endl;
+ f << name() << std::endl;
for (std::vector<VDBEntry>::const_iterator c(_imp->entries.begin()), c_end(_imp->entries.end()) ;
c != c_end ; ++c)
@@ -1402,80 +1414,16 @@ VDBRepository::regenerate_provides_cache() const
}
}
-void
-VDBRepository::regenerate_names_cache() const
-{
- if (_imp->names_cache == FSEntry("/var/empty"))
- return;
-
- Context context("When generating VDB repository names cache at '"
- + stringify(_imp->names_cache) + "':");
-
- if (_imp->names_cache.is_directory())
- for (DirIterator i(_imp->names_cache, true), i_end ; i != i_end ; ++i)
- FSEntry(*i).unlink();
-
- _imp->names_cache.dirname().mkdir();
- FSEntry(_imp->names_cache).mkdir();
-
- MakeHashedMap<std::string, std::string>::Type m;
-
- CategoryNamePartCollection::ConstPointer cats(category_names());
- for (CategoryNamePartCollection::Iterator c(cats->begin()), c_end(cats->end()) ;
- c != c_end ; ++c)
- {
- QualifiedPackageNameCollection::ConstPointer pkgs(package_names(*c));
- for (QualifiedPackageNameCollection::Iterator p(pkgs->begin()), p_end(pkgs->end()) ;
- p != p_end ; ++p)
- m[stringify(p->package)].append(stringify(*c) + "\n");
- }
-
- for (MakeHashedMap<std::string, std::string>::Type::const_iterator e(m.begin()), e_end(m.end()) ;
- e != e_end ; ++e)
- {
- std::ofstream f(stringify(_imp->names_cache / stringify(e->first)).c_str());
- if (! f)
- {
- Log::get_instance()->message(ll_warning, lc_context, "Cannot write to '"
- + stringify(_imp->names_cache) + "'");
- continue;
- }
- f << e->second;
- }
-
-}
-
CategoryNamePartCollection::ConstPointer
VDBRepository::do_category_names_containing_package(const PackageNamePart & p) const
{
- if (_imp->names_cache == FSEntry("/var/empty"))
+ if (! _imp->names_cache->usable())
return Repository::do_category_names_containing_package(p);
- CategoryNamePartCollection::Pointer result(new CategoryNamePartCollection::Concrete);
- NameCacheMap::iterator r(_imp->name_cache_map.find(p));
-
- if (_imp->name_cache_map.end() == r)
- {
- Log::get_instance()->message(ll_debug, lc_context, "Loading names cache for '" +
- stringify(p) + "'");
-
- r = _imp->name_cache_map.insert(std::make_pair(p, std::list<CategoryNamePart>())).first;
-
- FSEntry ff(_imp->names_cache / stringify(p));
- if (ff.exists())
- {
- std::ifstream f(stringify(ff).c_str());
- if (! f)
- Log::get_instance()->message(ll_warning, lc_context, "Cannot read '" + stringify(ff) + "'");
- std::string line;
- while (std::getline(f, line))
- r->second.push_back(CategoryNamePart(line));
- }
- }
-
- std::copy(r->second.begin(), r->second.end(), result->inserter());
+ CategoryNamePartCollection::ConstPointer result(
+ _imp->names_cache->category_names_containing_package(p));
- return result;
+ return result ? result : Repository::do_category_names_containing_package(p);
}
#ifdef PALUDIS_ENABLE_VISIBILITY
diff --git a/paludis/repositories/vdb/vdb_repository.hh b/paludis/repositories/vdb/vdb_repository.hh
index 64c1375..9ed53de 100644
--- a/paludis/repositories/vdb/vdb_repository.hh
+++ b/paludis/repositories/vdb/vdb_repository.hh
@@ -60,7 +60,6 @@ namespace paludis
void load_provided_the_slow_way() const;
void regenerate_provides_cache() const;
- void regenerate_names_cache() const;
protected:
virtual bool do_has_category_named(const CategoryNamePart &) const;
diff --git a/paludis/repository.hh b/paludis/repository.hh
index 876c023..bd0af96 100644
--- a/paludis/repository.hh
+++ b/paludis/repository.hh
@@ -46,6 +46,7 @@
namespace paludis
{
class Environment;
+ class RepositoryNameCache;
class RepositoryInstallableInterface;
class RepositoryInstalledInterface;
diff --git a/paludis/repository_name_cache.cc b/paludis/repository_name_cache.cc
new file mode 100644
index 0000000..4fcdfd2
--- /dev/null
+++ b/paludis/repository_name_cache.cc
@@ -0,0 +1,185 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "repository_name_cache.hh"
+#include <paludis/repository.hh>
+#include <paludis/hashed_containers.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/collection_concrete.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <list>
+#include <fstream>
+
+using namespace paludis;
+
+namespace paludis
+{
+ typedef MakeHashedMap<PackageNamePart, std::list<CategoryNamePart> >::Type NameCacheMap;
+
+ template<>
+ struct Implementation<RepositoryNameCache> :
+ InternalCounted<Implementation<RepositoryNameCache> >
+ {
+ const FSEntry location;
+ const Repository * const repo;
+
+ mutable NameCacheMap name_cache_map;
+ mutable bool checked_name_cache_map;
+
+ Implementation(const FSEntry & l, const Repository * const r) :
+ location(l),
+ repo(r),
+ checked_name_cache_map(false)
+ {
+ }
+ };
+}
+
+RepositoryNameCache::RepositoryNameCache(
+ const FSEntry & location,
+ const Repository * const repo) :
+ PrivateImplementationPattern<RepositoryNameCache>(new Implementation<RepositoryNameCache>(
+ location, repo)),
+ _usable(_imp->location != FSEntry("/var/empty"))
+{
+}
+
+RepositoryNameCache::~RepositoryNameCache()
+{
+}
+
+CategoryNamePartCollection::ConstPointer
+RepositoryNameCache::category_names_containing_package(const PackageNamePart & p) const
+{
+ if (! usable())
+ return CategoryNamePartCollection::ConstPointer(0);
+
+ CategoryNamePartCollection::Pointer result(new CategoryNamePartCollection::Concrete);
+ NameCacheMap::iterator r(_imp->name_cache_map.find(p));
+
+ if (_imp->name_cache_map.end() == r)
+ {
+ r = _imp->name_cache_map.insert(std::make_pair(p, std::list<CategoryNamePart>())).first;
+
+ if (! _imp->checked_name_cache_map)
+ {
+ FSEntry vv(_imp->location / "_VERSION_");
+ if (vv.exists())
+ {
+ std::ifstream vvf(stringify(vv).c_str());
+ std::string line;
+ std::getline(vvf, line);
+ if (line != "paludis-2")
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Names cache for '" + stringify(_imp->repo->name())
+ + "' has version string '" + line + "', which is not supported. Was it generated using "
+ "a different Paludis version?");
+ _usable = false;
+ return CategoryNamePartCollection::ConstPointer(0);
+ }
+ std::getline(vvf, line);
+ if (line != stringify(_imp->repo->name()))
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Names cache for '" + stringify(_imp->repo->name())
+ + "' was generated fpr repository '" + line + "', so it cannot be used. You must not "
+ "have multiple name caches at the same location.");
+ _usable = false;
+ return CategoryNamePartCollection::ConstPointer(0);
+ }
+ _imp->checked_name_cache_map = true;
+ }
+ else
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Names cache for '" + stringify(_imp->repo->name())
+ + "' has no version information, so cannot be used. Either it was generated using "
+ "an older Paludis version or it has not yet been generated.");
+ _usable = false;
+ return CategoryNamePartCollection::ConstPointer(0);
+ }
+ }
+
+ FSEntry ff(_imp->location / stringify(p));
+ if (ff.exists())
+ {
+ std::ifstream f(stringify(ff).c_str());
+ if (! f)
+ Log::get_instance()->message(ll_warning, lc_context, "Cannot read '" + stringify(ff) + "'");
+ std::string line;
+ while (std::getline(f, line))
+ r->second.push_back(CategoryNamePart(line));
+ }
+ }
+
+ std::copy(r->second.begin(), r->second.end(), result->inserter());
+
+ return result;
+}
+
+void
+RepositoryNameCache::regenerate_cache() const
+{
+ if (_imp->location == FSEntry("/var/empty"))
+ return;
+
+ Context context("When generating repository names cache at '"
+ + stringify(_imp->location) + "':");
+
+ if (_imp->location.is_directory())
+ for (DirIterator i(_imp->location, true), i_end ; i != i_end ; ++i)
+ FSEntry(*i).unlink();
+
+ _imp->location.dirname().mkdir();
+ FSEntry(_imp->location).mkdir();
+
+ MakeHashedMap<std::string, std::string>::Type m;
+
+ CategoryNamePartCollection::ConstPointer cats(_imp->repo->category_names());
+ for (CategoryNamePartCollection::Iterator c(cats->begin()), c_end(cats->end()) ;
+ c != c_end ; ++c)
+ {
+ QualifiedPackageNameCollection::ConstPointer pkgs(_imp->repo->package_names(*c));
+ for (QualifiedPackageNameCollection::Iterator p(pkgs->begin()), p_end(pkgs->end()) ;
+ p != p_end ; ++p)
+ m[stringify(p->package)].append(stringify(*c) + "\n");
+ }
+
+ for (MakeHashedMap<std::string, std::string>::Type::const_iterator e(m.begin()), e_end(m.end()) ;
+ e != e_end ; ++e)
+ {
+ std::ofstream f(stringify(_imp->location / stringify(e->first)).c_str());
+ if (! f)
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Cannot write to '"
+ + stringify(_imp->location) + "'");
+ continue;
+ }
+ f << e->second;
+ }
+
+ std::ofstream f(stringify(_imp->location / "_VERSION_").c_str());
+ if (f)
+ {
+ f << "paludis-2" << std::endl;
+ f << _imp->repo->name() << std::endl;
+ }
+ else
+ Log::get_instance()->message(ll_warning, lc_context, "Cannot write to '"
+ + stringify(_imp->location) + "'");
+}
diff --git a/paludis/repository_name_cache.hh b/paludis/repository_name_cache.hh
new file mode 100644
index 0000000..0b7a716
--- /dev/null
+++ b/paludis/repository_name_cache.hh
@@ -0,0 +1,56 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_REPOSITORY_NAME_CACHE_HH
+#define PALUDIS_GUARD_PALUDIS_REPOSITORY_NAME_CACHE_HH 1
+
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/name.hh>
+
+namespace paludis
+{
+ class FSEntry;
+ class Repository;
+
+ class RepositoryNameCache :
+ public InternalCounted<RepositoryNameCache>,
+ private PrivateImplementationPattern<RepositoryNameCache>
+ {
+ private:
+ mutable bool _usable;
+
+ public:
+ RepositoryNameCache(
+ const FSEntry & location,
+ const Repository * const repo);
+ virtual ~RepositoryNameCache();
+
+ CategoryNamePartCollection::ConstPointer category_names_containing_package(
+ const PackageNamePart & p) const;
+
+ bool usable() const PALUDIS_ATTRIBUTE((nothrow))
+ {
+ return _usable;
+ }
+
+ void regenerate_cache() const;
+ };
+}
+
+#endif
diff --git a/paludis/repository_name_cache_TEST.cc b/paludis/repository_name_cache_TEST.cc
new file mode 100644
index 0000000..6942d60
--- /dev/null
+++ b/paludis/repository_name_cache_TEST.cc
@@ -0,0 +1,171 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaranm@ciaranm.org>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "repository_name_cache.hh"
+#include <paludis/environment/test/test_environment.hh>
+#include <paludis/repositories/fake/fake_repository.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/join.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct NamesCacheEmptyTest : TestCase
+ {
+ NamesCacheEmptyTest() : TestCase("empty") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("/var/empty"), repo.raw_pointer());
+ TEST_CHECK(! cache.usable());
+ }
+ } test_names_cache_empty;
+
+ struct NamesCacheNotGeneratedTest : TestCase
+ {
+ NamesCacheNotGeneratedTest() : TestCase("not generated") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("repository_name_cache_TEST_dir/not_generated"), repo.raw_pointer());
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(! cache.category_names_containing_package(PackageNamePart("foo")));
+ TEST_CHECK(! cache.usable());
+ }
+ } test_names_cache_not_generated;
+
+ struct NamesCacheNotExistingTest : TestCase
+ {
+ NamesCacheNotExistingTest() : TestCase("not existing") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("repository_name_cache_TEST_dir/not_existing"), repo.raw_pointer());
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(! cache.category_names_containing_package(PackageNamePart("foo")));
+ TEST_CHECK(! cache.usable());
+ }
+ } test_names_cache_not_existing;
+
+ struct NamesCacheOldFormatTest : TestCase
+ {
+ NamesCacheOldFormatTest() : TestCase("old format") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("repository_name_cache_TEST_dir/old_format"), repo.raw_pointer());
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(! cache.category_names_containing_package(PackageNamePart("foo")));
+ TEST_CHECK(! cache.usable());
+ }
+ } test_names_cache_old_format;
+
+ struct NamesCacheBadRepoTest : TestCase
+ {
+ NamesCacheBadRepoTest() : TestCase("bad repo") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("repository_name_cache_TEST_dir/bad_repo"), repo.raw_pointer());
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(! cache.category_names_containing_package(PackageNamePart("foo")));
+ TEST_CHECK(! cache.usable());
+ }
+ } test_names_cache_bad_repo;
+
+ struct NamesCacheGoodTest : TestCase
+ {
+ NamesCacheGoodTest() : TestCase("good") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("repository_name_cache_TEST_dir/good_repo"), repo.raw_pointer());
+ TEST_CHECK(cache.usable());
+
+ CategoryNamePartCollection::ConstPointer foo(cache.category_names_containing_package(PackageNamePart("foo")));
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(foo);
+ TEST_CHECK_EQUAL(join(foo->begin(), foo->end(), " "), "bar baz");
+
+ CategoryNamePartCollection::ConstPointer moo(cache.category_names_containing_package(PackageNamePart("moo")));
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(moo);
+ TEST_CHECK(moo->empty());
+ }
+ } test_names_cache_good;
+
+ struct NamesCacheGenerateTest : TestCase
+ {
+ NamesCacheGenerateTest() : TestCase("generate") { }
+
+ void run()
+ {
+ TestEnvironment env;
+ FakeRepository::Pointer repo(new FakeRepository(RepositoryName("repo")));
+ env.package_database()->add_repository(repo);
+
+ RepositoryNameCache cache(FSEntry("repository_name_cache_TEST_dir/generated"), repo.raw_pointer());
+ repo->add_package(QualifiedPackageName("bar/foo"));
+ repo->add_package(QualifiedPackageName("baz/foo"));
+
+ TEST_CHECK(cache.usable());
+ cache.regenerate_cache();
+ TEST_CHECK(cache.usable());
+
+ CategoryNamePartCollection::ConstPointer foo(cache.category_names_containing_package(PackageNamePart("foo")));
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(foo);
+ TEST_CHECK_EQUAL(join(foo->begin(), foo->end(), " "), "bar baz");
+
+ CategoryNamePartCollection::ConstPointer moo(cache.category_names_containing_package(PackageNamePart("moo")));
+ TEST_CHECK(cache.usable());
+ TEST_CHECK(moo);
+ TEST_CHECK(moo->empty());
+ }
+ } test_names_cache_generate;
+}
+
diff --git a/paludis/repository_name_cache_TEST_cleanup.sh b/paludis/repository_name_cache_TEST_cleanup.sh
new file mode 100755
index 0000000..e2bd542
--- /dev/null
+++ b/paludis/repository_name_cache_TEST_cleanup.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d repository_name_cache_TEST_dir ] ; then
+ rm -fr repository_name_cache_TEST_dir
+else
+ true
+fi
+
+
diff --git a/paludis/repository_name_cache_TEST_setup.sh b/paludis/repository_name_cache_TEST_setup.sh
new file mode 100755
index 0000000..0ac7f32
--- /dev/null
+++ b/paludis/repository_name_cache_TEST_setup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir repository_name_cache_TEST_dir || exit 1
+cd repository_name_cache_TEST_dir || exit 1
+
+mkdir -p not_generated
+
+mkdir -p old_format
+echo "paludis-1" > old_format/_VERSION_
+
+mkdir -p bad_repo
+echo "paludis-2" > bad_repo/_VERSION_
+echo "monkey" >> bad_repo/_VERSION_
+
+mkdir -p good_repo
+echo "paludis-2" > good_repo/_VERSION_
+echo "repo" >> good_repo/_VERSION_
+echo "bar" > good_repo/foo
+echo "baz" >> good_repo/foo
+