#
#
# patch "ChangeLog"
# from [9810ac424b8cbf50d4cff1ada017f42a66fbca63]
# to [0e56d5cc8c66c8de9e9a294a4c2f13eed17723e7]
#
# patch "commands.cc"
# from [50b3887eb01f57759a86a6ea6af692b2a83bcb9d]
# to [1ad5a06a41749d05fdf9b0ba1543095a4d743678]
#
# patch "restrictions.cc"
# from [b5729cdde64bdc8bba05b6285627e216b7ea9dc4]
# to [9000266d8470672a6d623df3c5d0c4ad866678de]
#
# patch "restrictions.hh"
# from [0eb6233836902c52790331da782bdebc731c22e3]
# to [997d9f85ddfb423b9a535acdef8a4c44bc384395]
#
============================================================
--- ChangeLog 9810ac424b8cbf50d4cff1ada017f42a66fbca63
+++ ChangeLog 0e56d5cc8c66c8de9e9a294a4c2f13eed17723e7
@@ -1,3 +1,10 @@
+2006-02-20 Derek Scherger
+
+ * commands.cc (status, ls_known, find_unknown_and_ignored,
+ find_missing, commit, diff, revert, log): add excludes to restrictions
+ * restrictions.{cc,hh}: preliminary support for excludes and unit
+ tests to see if it works
+
2006-02-13 Derek Scherger
* app_state.{cc,hh}:
============================================================
--- commands.cc 50b3887eb01f57759a86a6ea6af692b2a83bcb9d
+++ commands.cc 1ad5a06a41749d05fdf9b0ba1543095a4d743678
@@ -1295,7 +1295,7 @@
app.require_working_copy();
get_base_and_current_roster_shape(old_roster, new_roster, app);
- restriction mask(args, old_roster, new_roster);
+ restriction mask(args, app.exclude_patterns, old_roster, new_roster);
update_current_roster_from_filesystem(new_roster, mask, app);
make_restricted_csets(old_roster, new_roster, included, excluded, mask);
@@ -1655,7 +1655,7 @@
app.require_working_copy();
get_base_and_current_roster_shape(old_roster, new_roster, app);
- restriction mask(args, new_roster);
+ restriction mask(args, app.exclude_patterns, new_roster);
node_map const & nodes = new_roster.all_nodes();
for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
@@ -1683,7 +1683,7 @@
get_base_and_current_roster_shape(old_roster, new_roster, app);
- restriction mask(args, new_roster);
+ restriction mask(args, app.exclude_patterns, new_roster);
new_roster.extract_path_set(known);
@@ -1716,7 +1716,7 @@
get_base_and_current_roster_shape(old_roster, new_roster, app);
- restriction mask(args, new_roster);
+ restriction mask(args, app.exclude_patterns, new_roster);
node_map const & nodes = new_roster.all_nodes();
for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
@@ -2306,7 +2306,7 @@
app.require_working_copy();
get_base_and_current_roster_shape(old_roster, new_roster, app);
- restriction mask(args, old_roster, new_roster);
+ restriction mask(args, app.exclude_patterns, old_roster, new_roster);
update_current_roster_from_filesystem(new_roster, mask, app);
make_restricted_csets(old_roster, new_roster, included, excluded, mask);
@@ -2698,7 +2698,7 @@
get_base_and_current_roster_shape(old_roster, new_roster, app);
get_revision_id(old_rid);
- restriction mask(args, old_roster, new_roster);
+ restriction mask(args, app.exclude_patterns, old_roster, new_roster);
update_current_roster_from_filesystem(new_roster, mask, app);
make_restricted_csets(old_roster, new_roster, included, excluded, mask);
@@ -2722,7 +2722,7 @@
// FIXME: handle no ancestor case
// N(r_new.edges.size() == 1, F("current revision has no ancestor"));
- restriction mask(args, old_roster, new_roster);
+ restriction mask(args, app.exclude_patterns, old_roster, new_roster);
update_current_roster_from_filesystem(new_roster, mask, app);
make_restricted_csets(old_roster, new_roster, included, excluded, mask);
@@ -2746,7 +2746,7 @@
app.db.get_roster(r_old_id, old_roster);
app.db.get_roster(r_new_id, new_roster);
- restriction mask(args, old_roster, new_roster);
+ restriction mask(args, app.exclude_patterns, old_roster, new_roster);
// FIXME: this is *possibly* a UI bug, insofar as we
// look at the restriction name(s) you provided on the command
@@ -3277,7 +3277,9 @@
app.require_working_copy();
- vector restricted_args;
+ vector includes;
+ vector excludes;
+
if (app.missing)
{
// --missing is a further filter on the files included by a restriction
@@ -3295,16 +3297,17 @@
{
file_path fp(*i);
L(F("missing files are '%s'") % fp);
- restricted_args.push_back(fp.as_external());
+ includes.push_back(fp.as_external());
}
}
else
{
- restricted_args = args;
+ includes = args;
+ excludes = app.exclude_patterns;
}
get_base_and_current_roster_shape(old_roster, new_roster, app);
- restriction mask(restricted_args, old_roster, new_roster);
+ restriction mask(includes, excludes, old_roster, new_roster);
make_restricted_csets(old_roster, new_roster, included, excluded, mask);
@@ -3527,7 +3530,7 @@
// FIXME_RESTRICTIONS: should this add paths from the rosters of all selected revs?
- mask = restriction(args, old_roster, new_roster);
+ mask = restriction(args, app.exclude_patterns, old_roster, new_roster);
}
cert_name author_name(author_cert_name);
============================================================
--- restrictions.cc b5729cdde64bdc8bba05b6285627e216b7ea9dc4
+++ restrictions.cc 9000266d8470672a6d623df3c5d0c4ad866678de
@@ -12,63 +12,147 @@
#include "safe_map.hh"
#include "transforms.hh"
+using std::make_pair;
+
// TODO: add support for --depth (replace recursive boolean with depth value)
-// TODO: add support for --exclude
// TODO: add check for relevant rosters to be used by log
+//
// i.e. as log goes back through older and older rosters it may hit one that
// pre-dates any of the nodes in the restriction. the nodes that the restriction
// includes or excludes may not have been born in a sufficiently old roster. at
// this point log should stop because no earlier roster will include these nodes.
-restriction::restriction(vector const & args,
- roster_t const & roster)
+static void
+make_path_set(vector const & args, path_set & paths)
{
- set_paths(args);
- add_nodes(roster);
- check_paths();
+ for (vector::const_iterator i = args.begin(); i != args.end(); ++i)
+ {
+ split_path sp;
+ file_path_external(*i).split(sp);
+ paths.insert(sp);
+ }
}
-restriction::restriction(vector const & args,
- roster_t const & roster1,
- roster_t const & roster2)
+static void
+merge_states(path_state const & old_state,
+ path_state const & new_state,
+ path_state & merged_state,
+ split_path const & sp)
{
- set_paths(args);
- add_nodes(roster1);
- add_nodes(roster2);
- check_paths();
+ if (old_state == new_state)
+ {
+ merged_state = old_state;
+ }
+ else if (old_state == explicit_include && new_state == implicit_include ||
+ old_state == implicit_include && new_state == explicit_include)
+ {
+ merged_state = explicit_include;
+ }
+ else if (old_state == explicit_exclude && new_state == implicit_include ||
+ old_state == implicit_include && new_state == explicit_exclude)
+ {
+ // allowing a mix of explicit excludes and implicit includes on a path is
+ // rather questionable but is required by things like exclude x include
+ // x/y. (i.e. includes within excludes) these are also somewhat
+ // questionable themselves since they are equivalent to simply include
+ // x/y.
+ //
+ // this is also required more sensible things like include x exclude x/y
+ // include x/y/z.
+ merged_state = implicit_include;
+ }
+ else
+ {
+ L(F("path '%s' %d %d") % sp % old_state % new_state);
+ N(false, F("conflicting include/exclude on path '%s'") % sp);
+ }
}
-void
-restriction::set_paths(vector const & args)
+static void
+update_path_map(map & path_map, split_path const & sp, path_state const state)
{
- L(F("setting paths with %d args") % args.size());
+ map::iterator p = path_map.find(sp);
+ if (p != path_map.end())
+ {
+ path_state merged;
+ merge_states(p->second.state, state, merged, sp);
+ p->second.state = merged;
+ }
+ else
+ {
+ path_map.insert(make_pair(sp, path_entry(state)));
+ }
+}
- for (vector::const_iterator i = args.begin(); i != args.end(); ++i)
+static void
+add_included_paths(map & path_map, path_set const & includes)
+{
+ for (path_set::const_iterator i = includes.begin(); i != includes.end(); ++i)
{
- split_path sp;
- file_path_external(*i).split(sp);
- paths.insert(sp);
+ split_path sp(*i);
+ path_state state = explicit_include;
+
+ while (!sp.empty())
+ {
+ update_path_map(path_map, sp, state);
+ sp.pop_back();
+ state = implicit_include;
+ }
+
}
}
-void
-restriction::add_nodes(roster_t const & roster)
+static void
+add_excluded_paths(map & path_map, path_set const & excludes)
{
- for (path_set::const_iterator i = paths.begin(); i != paths.end(); ++i)
+ for (path_set::const_iterator i = excludes.begin(); i != excludes.end(); ++i)
{
+ update_path_map(path_map, *i, explicit_exclude);
+ }
+}
+
+static void
+update_node_map(map & node_map, node_id const nid, split_path const & sp, path_state const state)
+{
+ map::iterator n = node_map.find(nid);
+ if (n != node_map.end())
+ {
+ path_state merged;
+ merge_states(n->second, state, merged, sp);
+ n->second = merged;
+ }
+ else
+ {
+ node_map.insert(make_pair(nid, state));
+ }
+}
+
+static void
+add_included_nodes(map & node_map,
+ map & path_map,
+ path_set const & includes,
+ roster_t const & roster)
+{
+ for (path_set::const_iterator i = includes.begin(); i != includes.end(); ++i)
+ {
+
// TODO: (future) handle some sort of peg revision path syntax here.
// note that the idea of a --peg option doesn't work because each
// path may be relative to a different revision.
if (roster.has_node(*i))
{
+
+ // FIXME: this may be better as a list of invalid paths
+ map::iterator p = path_map.find(*i);
+ I(p != path_map.end());
+
+ p->second.roster_count++;
+
// TODO: proper recursive wildcard paths like foo/...
// for now explicit paths are recursive
// and implicit parents are non-recursive
- // TODO: possibly fail with nice error if path is already explicitly
- // in the map?
-
// currently we need to insert the parents of included nodes so that
// the included nodes are not orphaned in a restricted roster. this
// happens in cases like add "a" + add "a/b" when only "a/b" is
@@ -76,45 +160,153 @@
// isn't entirely sensible and should probably be revisited. it does
// match the current (old restrictions) semantics though.
- valid_paths.insert(*i);
+ node_id nid = roster.get_node(*i)->self;
- bool recursive = true;
- node_id nid = roster.get_node(*i)->self;
+ path_state state = explicit_include;
while (!null_node(nid))
{
split_path sp;
roster.get_name(nid, sp);
- L(F("adding nid %d path '%s' recursive %s") % nid % file_path(sp) % recursive);
- restricted_node_map[nid] |= recursive;
- restricted_path_map[sp] |= recursive;
+ update_node_map(node_map, nid, sp, state);
- recursive = false;
-
nid = roster.get_node(nid)->parent;
+ state = implicit_include;
}
}
- else
+ }
+}
+
+static void
+add_excluded_nodes(map & node_map,
+ map & path_map,
+ path_set const & excludes,
+ roster_t const & roster)
+{
+ for (path_set::const_iterator i = excludes.begin(); i != excludes.end(); ++i)
+ {
+
+ // TODO: (future) handle some sort of peg revision path syntax here.
+ // note that the idea of a --peg option doesn't work because each
+ // path may be relative to a different revision.
+
+ if (roster.has_node(*i))
{
- L(F("missed path '%s'") % *i);
+ // FIXME: this may be better as a list of invalid paths
+ map::iterator p = path_map.find(*i);
+ I(p != path_map.end());
+
+ p->second.roster_count++;
+
+ // TODO: proper recursive wildcard paths like foo/...
+ // for now explicit paths are recursive
+ // and implicit parents are non-recursive
+
+ node_id nid = roster.get_node(*i)->self;
+
+ split_path sp;
+ roster.get_name(nid, sp);
+ update_node_map(node_map, nid, sp, explicit_exclude);
}
}
}
+static void
+check_paths(map const & path_map)
+{
+ int bad = 0;
+
+ for (map::const_iterator i = path_map.begin();
+ i != path_map.end(); ++i)
+ {
+ L(F("%d %d %s") % i->second.state % i->second.roster_count % i->first);
+
+ if (i->second.state == explicit_include && i->second.roster_count == 0)
+ {
+ bad++;
+ W(F("unknown path included %s") % i->first);
+ }
+ else if (i->second.state == explicit_exclude && i->second.roster_count == 0)
+ {
+ bad++;
+ W(F("unknown path excluded %s") % i->first);
+ }
+ }
+
+ N(bad == 0, F("%d unknown paths") % bad);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// public constructors
+////////////////////////////////////////////////////////////////////////////////
+
+// FIXME: add_paths and add_nodes should take the nodes to add and their state
+// includes/explicit_include, excludes/explicit_exclude, parents/implicit_include
+// then we need things to generate lists of parent paths and nodes
+
+restriction::restriction(vector const & include_args,
+ vector const & exclude_args,
+ roster_t const & roster)
+{
+ path_set includes, excludes;
+ make_path_set(include_args, includes);
+ make_path_set(exclude_args, excludes);
+
+ add_included_paths(path_map, includes);
+ add_excluded_paths(path_map, excludes);
+
+ add_included_nodes(node_map, path_map, includes, roster);
+ add_excluded_nodes(node_map, path_map, excludes, roster);
+
+ default_result = includes.empty();
+
+ check_paths(path_map);
+}
+
+restriction::restriction(vector const & include_args,
+ vector const & exclude_args,
+ roster_t const & roster1,
+ roster_t const & roster2)
+{
+ path_set includes, excludes;
+ make_path_set(include_args, includes);
+ make_path_set(exclude_args, excludes);
+
+ add_included_paths(path_map, includes);
+ add_excluded_paths(path_map, excludes);
+
+ add_included_nodes(node_map, path_map, includes, roster1);
+ add_excluded_nodes(node_map, path_map, excludes, roster1);
+
+ add_included_nodes(node_map, path_map, includes, roster2);
+ add_excluded_nodes(node_map, path_map, excludes, roster2);
+
+ default_result = includes.empty();
+
+ check_paths(path_map);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// public api
+////////////////////////////////////////////////////////////////////////////////
+
bool
restriction::includes(roster_t const & roster, node_id nid) const
{
MM(roster);
I(roster.has_node(nid));
+ split_path sp;
+ roster.get_name(nid, sp);
+
// empty restriction includes everything
- if (restricted_node_map.empty())
+ if (node_map.empty())
{
- split_path sp;
- roster.get_name(nid, sp);
- L(F("included nid %d path '%s'") % nid % file_path(sp));
+ L(F("empty include of nid %d path '%s'") % nid % file_path(sp));
return true;
}
@@ -122,18 +314,27 @@
while (!null_node(current))
{
- split_path sp;
- roster.get_name(current, sp);
+ map::const_iterator r = node_map.find(current);
- map::const_iterator r = restricted_node_map.find(current);
-
- if (r != restricted_node_map.end())
+ if (r != node_map.end())
{
- // found exact node or a explicit/recusrive parent
- if (r->second || current == nid)
+ switch (r->second)
{
- L(F("included nid %d path '%s'") % current % file_path(sp));
+ case explicit_include:
+ L(F("explicit include of nid %d path '%s'") % current % file_path(sp));
return true;
+
+ case explicit_exclude:
+ L(F("explicit exclude of nid %d path '%s'") % current % file_path(sp));
+ return false;
+
+ case implicit_include:
+ // this is non-recursive and requires an exact match
+ if (current == nid)
+ {
+ L(F("implicit include of nid %d path '%s'") % current % file_path(sp));
+ return true;
+ }
}
}
@@ -141,19 +342,24 @@
current = node->parent;
}
- split_path sp;
- roster.get_name(nid, sp);
- L(F("excluded nid %d path '%s'\n") % nid % file_path(sp));
-
- return false;
+ if (default_result)
+ {
+ L(F("default include of nid %d path '%s'\n") % nid % file_path(sp));
+ return true;
+ }
+ else
+ {
+ L(F("default exclude of nid %d path '%s'\n") % nid % file_path(sp));
+ return false;
+ }
}
bool
restriction::includes(split_path const & sp) const
{
- if (restricted_path_map.empty())
+ if (path_map.empty())
{
- L(F("included path '%s'") % file_path(sp));
+ L(F("empty include of path '%s'") % file_path(sp));
return true;
}
@@ -161,45 +367,48 @@
while (!current.empty())
{
- L(F("checking path '%s'\n") % current);
- map::const_iterator r = restricted_path_map.find(current);
+ map::const_iterator r = path_map.find(current);
- if (r != restricted_path_map.end())
+ if (r != path_map.end())
{
- if (r->second || current == sp)
+ switch (r->second.state)
{
- L(F("included path '%s'") % file_path(sp));
+ case explicit_include:
+ L(F("explicit include of path '%s'") % file_path(sp));
return true;
+
+ case explicit_exclude:
+ L(F("explicit exclude of path '%s'") % file_path(sp));
+ return false;
+
+ case implicit_include:
+ // this is non-recursive and requires an exact match
+ if (current == sp)
+ {
+ L(F("implicit include of path '%s'") % file_path(sp));
+ return true;
+ }
}
}
current.pop_back();
}
- L(F("excluded path '%s'") % file_path(sp));
- return false;
-
-}
-
-void
-restriction::check_paths()
-{
- int bad = 0;
- for (path_set::const_iterator i = paths.begin(); i != paths.end(); ++i)
+ if (default_result)
{
- if (valid_paths.find(*i) == valid_paths.end())
- {
- bad++;
- W(F("unknown path %s") % *i);
- }
+ L(F("default include of path '%s'\n") % file_path(sp));
+ return true;
}
-
- E(bad == 0, F("%d unknown paths") % bad);
+ else
+ {
+ L(F("default exclude of path '%s'\n") % file_path(sp));
+ return false;
+ }
}
-////////////////////////////////////////////////////////////////////
-// testing
-////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// tests
+////////////////////////////////////////////////////////////////////////////////
#ifdef BUILD_UNIT_TESTS
#include "unit_tests.hh"
@@ -208,150 +417,499 @@
using std::string;
-file_id file1_id(string("1000000000000000000000000000000000000000"));
-file_id file2_id(string("2000000000000000000000000000000000000000"));
-file_id file3_id(string("3000000000000000000000000000000000000000"));
+// f's and g's are files
+// x's and y's are directories
+// and this is rather painful
-static void
-test_basic_restrictions()
+split_path sp_root;
+split_path sp_f;
+split_path sp_g;
+
+split_path sp_x;
+split_path sp_xf;
+split_path sp_xg;
+split_path sp_xx;
+split_path sp_xxf;
+split_path sp_xxg;
+split_path sp_xy;
+split_path sp_xyf;
+split_path sp_xyg;
+
+split_path sp_y;
+split_path sp_yf;
+split_path sp_yg;
+split_path sp_yx;
+split_path sp_yxf;
+split_path sp_yxg;
+split_path sp_yy;
+split_path sp_yyf;
+split_path sp_yyg;
+
+node_id nid_root;
+node_id nid_f;
+node_id nid_g;
+
+node_id nid_x;
+node_id nid_xf;
+node_id nid_xg;
+node_id nid_xx;
+node_id nid_xxf;
+node_id nid_xxg;
+node_id nid_xy;
+node_id nid_xyf;
+node_id nid_xyg;
+
+node_id nid_y;
+node_id nid_yf;
+node_id nid_yg;
+node_id nid_yx;
+node_id nid_yxf;
+node_id nid_yxg;
+node_id nid_yy;
+node_id nid_yyf;
+node_id nid_yyg;
+
+file_id fid_f(string("1000000000000000000000000000000000000000"));
+file_id fid_g(string("2000000000000000000000000000000000000000"));
+
+file_id fid_xf(string("3000000000000000000000000000000000000000"));
+file_id fid_xg(string("4000000000000000000000000000000000000000"));
+file_id fid_xxf(string("5000000000000000000000000000000000000000"));
+file_id fid_xxg(string("6000000000000000000000000000000000000000"));
+file_id fid_xyf(string("7000000000000000000000000000000000000000"));
+file_id fid_xyg(string("8000000000000000000000000000000000000000"));
+
+file_id fid_yf(string("9000000000000000000000000000000000000000"));
+file_id fid_yg(string("a000000000000000000000000000000000000000"));
+file_id fid_yxf(string("b000000000000000000000000000000000000000"));
+file_id fid_yxg(string("c000000000000000000000000000000000000000"));
+file_id fid_yyf(string("d000000000000000000000000000000000000000"));
+file_id fid_yyg(string("e000000000000000000000000000000000000000"));
+
+static void setup(roster_t & roster)
{
temp_node_id_source nis;
+
+ file_path_internal("").split(sp_root);
+ file_path_internal("f").split(sp_f);
+ file_path_internal("g").split(sp_g);
+
+ file_path_internal("x").split(sp_x);
+ file_path_internal("x/f").split(sp_xf);
+ file_path_internal("x/g").split(sp_xg);
+ file_path_internal("x/x").split(sp_xx);
+ file_path_internal("x/x/f").split(sp_xxf);
+ file_path_internal("x/x/g").split(sp_xxg);
+ file_path_internal("x/y").split(sp_xy);
+ file_path_internal("x/y/f").split(sp_xyf);
+ file_path_internal("x/y/g").split(sp_xyg);
+
+ file_path_internal("y").split(sp_y);
+ file_path_internal("y/f").split(sp_yf);
+ file_path_internal("y/g").split(sp_yg);
+ file_path_internal("y/x").split(sp_yx);
+ file_path_internal("y/x/f").split(sp_yxf);
+ file_path_internal("y/x/g").split(sp_yxg);
+ file_path_internal("y/y").split(sp_yy);
+ file_path_internal("y/y/f").split(sp_yyf);
+ file_path_internal("y/y/g").split(sp_yyg);
+
+ nid_root = roster.create_dir_node(nis);
+ nid_f = roster.create_file_node(fid_f, nis);
+ nid_g = roster.create_file_node(fid_g, nis);
+
+ nid_x = roster.create_dir_node(nis);
+ nid_xf = roster.create_file_node(fid_xf, nis);
+ nid_xg = roster.create_file_node(fid_xg, nis);
+ nid_xx = roster.create_dir_node(nis);
+ nid_xxf = roster.create_file_node(fid_xxf, nis);
+ nid_xxg = roster.create_file_node(fid_xxg, nis);
+ nid_xy = roster.create_dir_node(nis);
+ nid_xyf = roster.create_file_node(fid_xxf, nis);
+ nid_xyg = roster.create_file_node(fid_xxg, nis);
+
+ nid_y = roster.create_dir_node(nis);
+ nid_yf = roster.create_file_node(fid_yf, nis);
+ nid_yg = roster.create_file_node(fid_yg, nis);
+ nid_yx = roster.create_dir_node(nis);
+ nid_yxf = roster.create_file_node(fid_yxf, nis);
+ nid_yxg = roster.create_file_node(fid_yxg, nis);
+ nid_yy = roster.create_dir_node(nis);
+ nid_yyf = roster.create_file_node(fid_yxf, nis);
+ nid_yyg = roster.create_file_node(fid_yxg, nis);
+
+ roster.attach_node(nid_root, sp_root);
+ roster.attach_node(nid_f, sp_f);
+ roster.attach_node(nid_g, sp_g);
+
+ roster.attach_node(nid_x, sp_x);
+ roster.attach_node(nid_xf, sp_xf);
+ roster.attach_node(nid_xg, sp_xg);
+ roster.attach_node(nid_xx, sp_xx);
+ roster.attach_node(nid_xxf, sp_xxf);
+ roster.attach_node(nid_xxg, sp_xxg);
+ roster.attach_node(nid_xy, sp_xy);
+ roster.attach_node(nid_xyf, sp_xyf);
+ roster.attach_node(nid_xyg, sp_xyg);
+
+ roster.attach_node(nid_y, sp_y);
+ roster.attach_node(nid_yf, sp_yf);
+ roster.attach_node(nid_yg, sp_yg);
+ roster.attach_node(nid_yx, sp_yx);
+ roster.attach_node(nid_yxf, sp_yxf);
+ roster.attach_node(nid_yxg, sp_yxg);
+ roster.attach_node(nid_yy, sp_yy);
+ roster.attach_node(nid_yyf, sp_yyf);
+ roster.attach_node(nid_yyg, sp_yyg);
+}
+
+static void
+test_empty_restriction()
+{
roster_t roster;
+ setup(roster);
- node_id root_nid = roster.create_dir_node(nis);
- node_id file1_nid = roster.create_file_node(file1_id, nis);
- node_id file2_nid = roster.create_file_node(file2_id, nis);
- node_id file3_nid = roster.create_file_node(file3_id, nis);
+ restriction mask;
+
+ BOOST_CHECK(mask.empty());
- split_path root_path, file1_path, file2_path, file3_path;
- file_path().split(root_path);
- file_path_internal("file1").split(file1_path);
- file_path_internal("file2").split(file2_path);
- file_path_internal("file3").split(file3_path);
+ // check restricted nodes
+ BOOST_CHECK(mask.includes(roster, nid_root));
+ BOOST_CHECK(mask.includes(roster, nid_f));
+ BOOST_CHECK(mask.includes(roster, nid_g));
- roster.attach_node(root_nid, root_path);
- roster.attach_node(file1_nid, file1_path);
- roster.attach_node(file2_nid, file2_path);
- roster.attach_node(file3_nid, file3_path);
+ BOOST_CHECK(mask.includes(roster, nid_x));
+ BOOST_CHECK(mask.includes(roster, nid_xf));
+ BOOST_CHECK(mask.includes(roster, nid_xg));
+ BOOST_CHECK(mask.includes(roster, nid_xx));
+ BOOST_CHECK(mask.includes(roster, nid_xxf));
+ BOOST_CHECK(mask.includes(roster, nid_xxg));
+ BOOST_CHECK(mask.includes(roster, nid_xy));
+ BOOST_CHECK(mask.includes(roster, nid_xyf));
+ BOOST_CHECK(mask.includes(roster, nid_xyg));
- {
- // empty restriction
- restriction mask;
+ BOOST_CHECK(mask.includes(roster, nid_y));
+ BOOST_CHECK(mask.includes(roster, nid_yf));
+ BOOST_CHECK(mask.includes(roster, nid_yg));
+ BOOST_CHECK(mask.includes(roster, nid_yx));
+ BOOST_CHECK(mask.includes(roster, nid_yxf));
+ BOOST_CHECK(mask.includes(roster, nid_yxg));
+ BOOST_CHECK(mask.includes(roster, nid_yy));
+ BOOST_CHECK(mask.includes(roster, nid_yyf));
+ BOOST_CHECK(mask.includes(roster, nid_yyg));
- BOOST_CHECK(mask.empty());
+ // check restricted paths
+ BOOST_CHECK(mask.includes(sp_root));
+ BOOST_CHECK(mask.includes(sp_f));
+ BOOST_CHECK(mask.includes(sp_g));
- // check restricted nodes
- BOOST_CHECK(mask.includes(roster, root_nid));
- BOOST_CHECK(mask.includes(roster, file1_nid));
- BOOST_CHECK(mask.includes(roster, file2_nid));
- BOOST_CHECK(mask.includes(roster, file3_nid));
+ BOOST_CHECK(mask.includes(sp_x));
+ BOOST_CHECK(mask.includes(sp_xf));
+ BOOST_CHECK(mask.includes(sp_xg));
+ BOOST_CHECK(mask.includes(sp_xx));
+ BOOST_CHECK(mask.includes(sp_xxf));
+ BOOST_CHECK(mask.includes(sp_xxg));
+ BOOST_CHECK(mask.includes(sp_xy));
+ BOOST_CHECK(mask.includes(sp_xyf));
+ BOOST_CHECK(mask.includes(sp_xyg));
- // check restricted paths
- BOOST_CHECK(mask.includes(root_path));
- BOOST_CHECK(mask.includes(file1_path));
- BOOST_CHECK(mask.includes(file2_path));
- BOOST_CHECK(mask.includes(file3_path));
- }
+ BOOST_CHECK(mask.includes(sp_y));
+ BOOST_CHECK(mask.includes(sp_yf));
+ BOOST_CHECK(mask.includes(sp_yg));
+ BOOST_CHECK(mask.includes(sp_yx));
+ BOOST_CHECK(mask.includes(sp_yxf));
+ BOOST_CHECK(mask.includes(sp_yxg));
+ BOOST_CHECK(mask.includes(sp_yy));
+ BOOST_CHECK(mask.includes(sp_yyf));
+ BOOST_CHECK(mask.includes(sp_yyg));
+}
- {
- // non-empty restriction
- vector args;
- args.push_back(utf8(string("file1")));
+static void
+test_simple_include()
+{
+ roster_t roster;
+ setup(roster);
- restriction mask(args, roster);
+ vector includes, excludes;
+ includes.push_back(utf8(string("x/x")));
+ includes.push_back(utf8(string("y/y")));
- BOOST_CHECK(!mask.empty());
+ restriction mask(includes, excludes, roster);
- // check restricted nodes
- BOOST_CHECK(mask.includes(roster, root_nid));
- BOOST_CHECK(mask.includes(roster, file1_nid));
- BOOST_CHECK(!mask.includes(roster, file2_nid));
- BOOST_CHECK(!mask.includes(roster, file3_nid));
+ BOOST_CHECK(!mask.empty());
- // check restricted paths
- BOOST_CHECK(mask.includes(root_path));
- BOOST_CHECK(mask.includes(file1_path));
- BOOST_CHECK(!mask.includes(file2_path));
- BOOST_CHECK(!mask.includes(file3_path));
- }
+ // check restricted nodes
+ BOOST_CHECK( mask.includes(roster, nid_root));
+ BOOST_CHECK(!mask.includes(roster, nid_f));
+ BOOST_CHECK(!mask.includes(roster, nid_g));
- {
- // invalid paths
- // non-empty restriction
- vector args;
- args.push_back(utf8(string("file4")));
+ BOOST_CHECK( mask.includes(roster, nid_x));
+ BOOST_CHECK(!mask.includes(roster, nid_xf));
+ BOOST_CHECK(!mask.includes(roster, nid_xg));
+ BOOST_CHECK( mask.includes(roster, nid_xx));
+ BOOST_CHECK( mask.includes(roster, nid_xxf));
+ BOOST_CHECK( mask.includes(roster, nid_xxg));
+ BOOST_CHECK(!mask.includes(roster, nid_xy));
+ BOOST_CHECK(!mask.includes(roster, nid_xyf));
+ BOOST_CHECK(!mask.includes(roster, nid_xyg));
- BOOST_CHECK_THROW(restriction(args, roster), informative_failure);
- }
+ BOOST_CHECK( mask.includes(roster, nid_y));
+ BOOST_CHECK(!mask.includes(roster, nid_yf));
+ BOOST_CHECK(!mask.includes(roster, nid_yg));
+ BOOST_CHECK(!mask.includes(roster, nid_yx));
+ BOOST_CHECK(!mask.includes(roster, nid_yxf));
+ BOOST_CHECK(!mask.includes(roster, nid_yxg));
+ BOOST_CHECK( mask.includes(roster, nid_yy));
+ BOOST_CHECK( mask.includes(roster, nid_yyf));
+ BOOST_CHECK( mask.includes(roster, nid_yyg));
+ // check restricted paths
+ BOOST_CHECK( mask.includes(sp_root));
+ BOOST_CHECK(!mask.includes(sp_f));
+ BOOST_CHECK(!mask.includes(sp_g));
+
+ BOOST_CHECK( mask.includes(sp_x));
+ BOOST_CHECK(!mask.includes(sp_xf));
+ BOOST_CHECK(!mask.includes(sp_xg));
+ BOOST_CHECK( mask.includes(sp_xx));
+ BOOST_CHECK( mask.includes(sp_xxf));
+ BOOST_CHECK( mask.includes(sp_xxg));
+ BOOST_CHECK(!mask.includes(sp_xy));
+ BOOST_CHECK(!mask.includes(sp_xyf));
+ BOOST_CHECK(!mask.includes(sp_xyg));
+
+ BOOST_CHECK( mask.includes(sp_y));
+ BOOST_CHECK(!mask.includes(sp_yf));
+ BOOST_CHECK(!mask.includes(sp_yg));
+ BOOST_CHECK(!mask.includes(sp_yx));
+ BOOST_CHECK(!mask.includes(sp_yxf));
+ BOOST_CHECK(!mask.includes(sp_yxg));
+ BOOST_CHECK( mask.includes(sp_yy));
+ BOOST_CHECK( mask.includes(sp_yyf));
+ BOOST_CHECK( mask.includes(sp_yyg));
}
-static void
-test_recursive_nonrecursive()
+static void
+test_simple_exclude()
{
- temp_node_id_source nis;
roster_t roster;
+ setup(roster);
- node_id root_nid = roster.create_dir_node(nis);
+ vector includes, excludes;
+ excludes.push_back(utf8(string("x/x")));
+ excludes.push_back(utf8(string("y/y")));
- node_id dir1_nid = roster.create_dir_node(nis);
- node_id dir2_nid = roster.create_dir_node(nis);
+ restriction mask(includes, excludes, roster);
- node_id file1_nid = roster.create_file_node(file1_id, nis);
- node_id file2_nid = roster.create_file_node(file2_id, nis);
- node_id file3_nid = roster.create_file_node(file3_id, nis);
+ BOOST_CHECK(!mask.empty());
- // root/file1
- // root/dir1
- // root/dir1/file2
- // root/dir1/dir2
- // root/dir1/dir2/file3
+ // check restricted nodes
+ BOOST_CHECK( mask.includes(roster, nid_root));
+ BOOST_CHECK( mask.includes(roster, nid_f));
+ BOOST_CHECK( mask.includes(roster, nid_g));
- split_path root_path, file1_path, dir1_path, file2_path, dir2_path, file3_path;
+ BOOST_CHECK( mask.includes(roster, nid_x));
+ BOOST_CHECK( mask.includes(roster, nid_xf));
+ BOOST_CHECK( mask.includes(roster, nid_xg));
+ BOOST_CHECK(!mask.includes(roster, nid_xx));
+ BOOST_CHECK(!mask.includes(roster, nid_xxf));
+ BOOST_CHECK(!mask.includes(roster, nid_xxg));
+ BOOST_CHECK( mask.includes(roster, nid_xy));
+ BOOST_CHECK( mask.includes(roster, nid_xyf));
+ BOOST_CHECK( mask.includes(roster, nid_xyg));
- file_path().split(root_path);
- file_path_internal("file1").split(file1_path);
- file_path_internal("dir1").split(dir1_path);
- file_path_internal("dir1/file2").split(file2_path);
- file_path_internal("dir1/dir2").split(dir2_path);
- file_path_internal("dir1/dir2/file3").split(file3_path);
+ BOOST_CHECK( mask.includes(roster, nid_y));
+ BOOST_CHECK( mask.includes(roster, nid_yf));
+ BOOST_CHECK( mask.includes(roster, nid_yg));
+ BOOST_CHECK( mask.includes(roster, nid_yx));
+ BOOST_CHECK( mask.includes(roster, nid_yxf));
+ BOOST_CHECK( mask.includes(roster, nid_yxg));
+ BOOST_CHECK(!mask.includes(roster, nid_yy));
+ BOOST_CHECK(!mask.includes(roster, nid_yyf));
+ BOOST_CHECK(!mask.includes(roster, nid_yyg));
- roster.attach_node(root_nid, root_path);
- roster.attach_node(file1_nid, file1_path);
- roster.attach_node(dir1_nid, dir1_path);
- roster.attach_node(file2_nid, file2_path);
- roster.attach_node(dir2_nid, dir2_path);
- roster.attach_node(file3_nid, file3_path);
+ // check restricted paths
+ BOOST_CHECK( mask.includes(sp_root));
+ BOOST_CHECK( mask.includes(sp_f));
+ BOOST_CHECK( mask.includes(sp_g));
- vector args;
- args.push_back(utf8(string("dir1/dir2")));
+ BOOST_CHECK( mask.includes(sp_x));
+ BOOST_CHECK( mask.includes(sp_xf));
+ BOOST_CHECK( mask.includes(sp_xg));
+ BOOST_CHECK(!mask.includes(sp_xx));
+ BOOST_CHECK(!mask.includes(sp_xxf));
+ BOOST_CHECK(!mask.includes(sp_xxg));
+ BOOST_CHECK( mask.includes(sp_xy));
+ BOOST_CHECK( mask.includes(sp_xyf));
+ BOOST_CHECK( mask.includes(sp_xyg));
- restriction mask(args, roster);
+ BOOST_CHECK( mask.includes(sp_y));
+ BOOST_CHECK( mask.includes(sp_yf));
+ BOOST_CHECK( mask.includes(sp_yg));
+ BOOST_CHECK( mask.includes(sp_yx));
+ BOOST_CHECK( mask.includes(sp_yxf));
+ BOOST_CHECK( mask.includes(sp_yxg));
+ BOOST_CHECK(!mask.includes(sp_yy));
+ BOOST_CHECK(!mask.includes(sp_yyf));
+ BOOST_CHECK(!mask.includes(sp_yyg));
+}
+static void
+test_include_exclude()
+{
+ roster_t roster;
+ setup(roster);
+
+ vector includes, excludes;
+ includes.push_back(utf8(string("x")));
+ includes.push_back(utf8(string("y")));
+ excludes.push_back(utf8(string("x/x")));
+ excludes.push_back(utf8(string("y/y")));
+
+ restriction mask(includes, excludes, roster);
+
BOOST_CHECK(!mask.empty());
// check restricted nodes
- BOOST_CHECK(mask.includes(roster, root_nid));
- BOOST_CHECK(!mask.includes(roster, file1_nid));
- BOOST_CHECK(mask.includes(roster, dir1_nid));
- BOOST_CHECK(!mask.includes(roster, file2_nid));
- BOOST_CHECK(mask.includes(roster, dir2_nid));
- BOOST_CHECK(mask.includes(roster, file3_nid));
+ BOOST_CHECK( mask.includes(roster, nid_root));
+ BOOST_CHECK(!mask.includes(roster, nid_f));
+ BOOST_CHECK(!mask.includes(roster, nid_g));
+ BOOST_CHECK( mask.includes(roster, nid_x));
+ BOOST_CHECK( mask.includes(roster, nid_xf));
+ BOOST_CHECK( mask.includes(roster, nid_xg));
+ BOOST_CHECK(!mask.includes(roster, nid_xx));
+ BOOST_CHECK(!mask.includes(roster, nid_xxf));
+ BOOST_CHECK(!mask.includes(roster, nid_xxg));
+ BOOST_CHECK( mask.includes(roster, nid_xy));
+ BOOST_CHECK( mask.includes(roster, nid_xyf));
+ BOOST_CHECK( mask.includes(roster, nid_xyg));
+
+ BOOST_CHECK( mask.includes(roster, nid_y));
+ BOOST_CHECK( mask.includes(roster, nid_yf));
+ BOOST_CHECK( mask.includes(roster, nid_yg));
+ BOOST_CHECK( mask.includes(roster, nid_yx));
+ BOOST_CHECK( mask.includes(roster, nid_yxf));
+ BOOST_CHECK( mask.includes(roster, nid_yxg));
+ BOOST_CHECK(!mask.includes(roster, nid_yy));
+ BOOST_CHECK(!mask.includes(roster, nid_yyf));
+ BOOST_CHECK(!mask.includes(roster, nid_yyg));
+
// check restricted paths
- BOOST_CHECK(mask.includes(root_path));
- BOOST_CHECK(!mask.includes(file1_path));
- BOOST_CHECK(mask.includes(dir1_path));
- BOOST_CHECK(!mask.includes(file2_path));
- BOOST_CHECK(mask.includes(dir2_path));
- BOOST_CHECK(mask.includes(file3_path));
+ BOOST_CHECK( mask.includes(sp_root));
+ BOOST_CHECK(!mask.includes(sp_f));
+ BOOST_CHECK(!mask.includes(sp_g));
+
+ BOOST_CHECK( mask.includes(sp_x));
+ BOOST_CHECK( mask.includes(sp_xf));
+ BOOST_CHECK( mask.includes(sp_xg));
+ BOOST_CHECK(!mask.includes(sp_xx));
+ BOOST_CHECK(!mask.includes(sp_xxf));
+ BOOST_CHECK(!mask.includes(sp_xxg));
+ BOOST_CHECK( mask.includes(sp_xy));
+ BOOST_CHECK( mask.includes(sp_xyf));
+ BOOST_CHECK( mask.includes(sp_xyg));
+
+ BOOST_CHECK( mask.includes(sp_y));
+ BOOST_CHECK( mask.includes(sp_yf));
+ BOOST_CHECK( mask.includes(sp_yg));
+ BOOST_CHECK( mask.includes(sp_yx));
+ BOOST_CHECK( mask.includes(sp_yxf));
+ BOOST_CHECK( mask.includes(sp_yxg));
+ BOOST_CHECK(!mask.includes(sp_yy));
+ BOOST_CHECK(!mask.includes(sp_yyf));
+ BOOST_CHECK(!mask.includes(sp_yyg));
}
+static void
+test_exclude_include()
+{
+ roster_t roster;
+ setup(roster);
+
+ vector includes, excludes;
+ excludes.push_back(utf8(string("x")));
+ excludes.push_back(utf8(string("y")));
+ includes.push_back(utf8(string("x/x")));
+ includes.push_back(utf8(string("y/y")));
+
+ restriction mask(includes, excludes, roster);
+
+ BOOST_CHECK(!mask.empty());
+
+ // check restricted nodes
+ BOOST_CHECK( mask.includes(roster, nid_root));
+ BOOST_CHECK(!mask.includes(roster, nid_f));
+ BOOST_CHECK(!mask.includes(roster, nid_g));
+
+ BOOST_CHECK( mask.includes(roster, nid_x));
+ BOOST_CHECK(!mask.includes(roster, nid_xf));
+ BOOST_CHECK(!mask.includes(roster, nid_xg));
+ BOOST_CHECK( mask.includes(roster, nid_xx));
+ BOOST_CHECK( mask.includes(roster, nid_xxf));
+ BOOST_CHECK( mask.includes(roster, nid_xxg));
+ BOOST_CHECK(!mask.includes(roster, nid_xy));
+ BOOST_CHECK(!mask.includes(roster, nid_xyf));
+ BOOST_CHECK(!mask.includes(roster, nid_xyg));
+
+ BOOST_CHECK( mask.includes(roster, nid_y));
+ BOOST_CHECK(!mask.includes(roster, nid_yf));
+ BOOST_CHECK(!mask.includes(roster, nid_yg));
+ BOOST_CHECK(!mask.includes(roster, nid_yx));
+ BOOST_CHECK(!mask.includes(roster, nid_yxf));
+ BOOST_CHECK(!mask.includes(roster, nid_yxg));
+ BOOST_CHECK( mask.includes(roster, nid_yy));
+ BOOST_CHECK( mask.includes(roster, nid_yyf));
+ BOOST_CHECK( mask.includes(roster, nid_yyg));
+
+ // check restricted paths
+ BOOST_CHECK( mask.includes(sp_root));
+ BOOST_CHECK(!mask.includes(sp_f));
+ BOOST_CHECK(!mask.includes(sp_g));
+
+ BOOST_CHECK( mask.includes(sp_x));
+ BOOST_CHECK(!mask.includes(sp_xf));
+ BOOST_CHECK(!mask.includes(sp_xg));
+ BOOST_CHECK( mask.includes(sp_xx));
+ BOOST_CHECK( mask.includes(sp_xxf));
+ BOOST_CHECK( mask.includes(sp_xxg));
+ BOOST_CHECK(!mask.includes(sp_xy));
+ BOOST_CHECK(!mask.includes(sp_xyf));
+ BOOST_CHECK(!mask.includes(sp_xyg));
+
+ BOOST_CHECK( mask.includes(sp_y));
+ BOOST_CHECK(!mask.includes(sp_yf));
+ BOOST_CHECK(!mask.includes(sp_yg));
+ BOOST_CHECK(!mask.includes(sp_yx));
+ BOOST_CHECK(!mask.includes(sp_yxf));
+ BOOST_CHECK(!mask.includes(sp_yxg));
+ BOOST_CHECK( mask.includes(sp_yy));
+ BOOST_CHECK( mask.includes(sp_yyf));
+ BOOST_CHECK( mask.includes(sp_yyg));
+}
+
+static void
+test_invalid_paths()
+{
+ roster_t roster;
+ setup(roster);
+
+ vector includes, excludes;
+ includes.push_back(utf8(string("foo")));
+ excludes.push_back(utf8(string("bar")));
+
+ BOOST_CHECK_THROW(restriction(includes, excludes, roster), informative_failure);
+}
+
void
add_restrictions_tests(test_suite * suite)
{
I(suite);
- suite->add(BOOST_TEST_CASE(&test_basic_restrictions));
- suite->add(BOOST_TEST_CASE(&test_recursive_nonrecursive));
+ suite->add(BOOST_TEST_CASE(&test_empty_restriction));
+ suite->add(BOOST_TEST_CASE(&test_simple_include));
+ suite->add(BOOST_TEST_CASE(&test_simple_exclude));
+ suite->add(BOOST_TEST_CASE(&test_include_exclude));
+ suite->add(BOOST_TEST_CASE(&test_exclude_include));
+ suite->add(BOOST_TEST_CASE(&test_invalid_paths));
}
#endif // BUILD_UNIT_TESTS
============================================================
--- restrictions.hh 0eb6233836902c52790331da782bdebc731c22e3
+++ restrictions.hh 997d9f85ddfb423b9a535acdef8a4c44bc384395
@@ -50,39 +50,49 @@
//
// revision A ... included ... revision X ... excluded ... revision B
+// TODO: move these into the class below?!?
+enum path_state { explicit_include, explicit_exclude, implicit_include };
+
+struct path_entry {
+ path_state state;
+ int roster_count;
+ path_entry(path_state const s) : state(s), roster_count(0) {}
+};
+
+
class restriction
{
public:
restriction() {}
- restriction(vector const & args,
+ restriction(vector const & includes,
+ vector const & excludes,
roster_t const & roster);
-
- restriction(vector const & args,
+
+ restriction(vector const & includes,
+ vector const & excludes,
roster_t const & roster1,
roster_t const & roster2);
bool includes(roster_t const & roster, node_id nid) const;
+
bool includes(split_path const & sp) const;
- bool empty() const { return restricted_node_map.empty(); }
+ bool empty() { return node_map.empty(); }
+
+ // explicit in the sense that the path was explicitly given on the command line
+ // implicit in the sense that parent directories are included for explicit paths
+
private:
- // true in these two maps indicates an explicitly included node or path and
- // explicitly included directories are recursive. alternatively, false in
- // these maps indicated an implicitly included parent directory which is
- // specifically required for the restriction but does not include any of its
- // children
-
- map restricted_node_map;
- map restricted_path_map;
- path_set paths;
+ bool default_result;
- path_set valid_paths;
+ // we maintain maps by node_id and also by split_path which is not
+ // particularly nice, but is required for checking unknown and ignored
+ // files and used for tracking the paths that are known in the rosters
+ map node_map;
+ map path_map;
- void set_paths(vector const & args);
- void add_nodes(roster_t const & roster);
- void check_paths();
};
#endif // header guard