[Aptitude-devel] r2954 - in branches/aptitude-0.3/aptitude: . src/generic/problemresolver

Daniel Burrows dburrows@costa.debian.org
Fri, 08 Apr 2005 16:57:00 +0000


Author: dburrows
Date: Fri Apr  8 16:56:57 2005
New Revision: 2954

Modified:
   branches/aptitude-0.3/aptitude/ChangeLog
   branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
   branches/aptitude-0.3/aptitude/src/generic/problemresolver/test.cc
   branches/aptitude-0.3/aptitude/src/generic/problemresolver/test1.txt
Log:
Greatly enhance the test script system (although maybe Python bindings
would be more flexible?)

Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog	(original)
+++ branches/aptitude-0.3/aptitude/ChangeLog	Fri Apr  8 16:56:57 2005
@@ -1,5 +1,11 @@
 2005-04-08  Daniel Burrows  <dburrows@debian.org>
 
+	* src/generic/problemresolver/test.cc:
+
+	  Overhaul the test code to make the test fully controlled
+	  by its input file -- including support for specifying
+	  tests to run on the universe and the expected results.
+
 	* src/generic/problemresolver/problemresolver.h, src/generic/problemresolver/test.cc:
 
 	  Make dump_universe generic, move it to problemresolver.h, and

Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h	Fri Apr  8 16:56:57 2005
@@ -659,6 +659,11 @@
     version_score[ver.get_id()]+=score;
   }
 
+  /** \return the score of the version ver. */
+  int get_version_score(version &ver)
+  {
+    return version_score[ver.get_id()];
+  }
 
   /** Try to find the "next" solution: remove partial solutions from
    *  the open queue and place them in the closed queue until one of
@@ -729,12 +734,40 @@
 
     throw NoMoreSolutions();
   }
+
+  void dump_scores(std::ostream &out)
+  {
+    out << "{" << std::endl;
+    for(typename PackageUniverse::package_iterator i=universe.packages_begin();
+	i!=universe.packages_end(); ++i)
+      {
+	bool any_modified=false;
+
+	for(typename PackageUniverse::package::version_iterator j=(*i).versions_begin();
+	    j!=(*i).versions_end(); ++j)
+	  if(version_scores[(*j).get_id()]!=0)
+	    any_modified=true;
+
+	if(any_modified)
+	  {
+	    out << "  SCORE " << (*i).get_name() << " <";
+
+	    for(typename PackageUniverse::package::version_iterator j=(*i).versions_begin();
+	    j!=(*i).versions_end(); ++j)
+	      if(version_scores[(*j).get_id()]!=0)
+		out << " " << (*j).get_name();
+
+	    out << " >" << std::endl;
+	  }
+      }
+  }
 };
 
 
 template<class PackageUniverse>
-void dump_universe(const PackageUniverse &world, ostream &out)
+void dump_universe(const PackageUniverse &world, std::ostream &out)
 {
+  out << "UNIVERSE ";
   for(typename PackageUniverse::package_iterator p=world.packages_begin();
       p!=world.packages_end(); ++p)
     {

Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/test.cc
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/test.cc	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/test.cc	Fri Apr  8 16:56:57 2005
@@ -147,7 +147,7 @@
   version_iterator versions_end() const {return versions.end();}
 
   /** Returns the version corresponding to the given name or aborts */
-  dummy_version *version_from_name(const string &the_name);
+  dummy_version *version_from_name(const string &the_name) const;
 
   /** Sets the current version to the given version. */
   void set_current_version(dummy_version *v)
@@ -220,9 +220,9 @@
     delete *i;
 }
 
-dummy_version *dummy_package::version_from_name(const string &the_name)
+dummy_version *dummy_package::version_from_name(const string &the_name) const
 {
-  for(vector<dummy_version *>::iterator i=versions.begin(); i!=versions.end(); ++i)
+  for(vector<dummy_version *>::const_iterator i=versions.begin(); i!=versions.end(); ++i)
     if((*i)->get_name()==the_name)
       return *i;
 
@@ -343,6 +343,11 @@
       return version(&real_package->current_version());
     }
 
+    version version_from_name(string name) const
+    {
+      return version(real_package->version_from_name(name));
+    }
+
     wrap_ptr_iter<dummy_version, version> versions_begin() const
     {
       return real_package->versions_begin();
@@ -528,6 +533,16 @@
     }
   };
 
+  dummy_package *find_package_internal(string pkg_name)
+  {
+    map<string, dummy_package *>::const_iterator pfound=packages_by_name.find(pkg_name);
+
+    if(pfound==packages_by_name.end())
+      throw NoSuchNameError("package", pkg_name);
+
+    return pfound->second;
+  }
+
 public:
   virtual ~dummy_universe()
   {
@@ -569,6 +584,12 @@
     packages_by_name[name]=packages.back();
   }
 
+  /** Find a package by name. */
+  package find_package(string pkg_name)
+  {
+    return find_package_internal(pkg_name);
+  }
+
   /** Add a dependency to the universe.  For convenience
    *  this is string-based.
    */
@@ -576,12 +597,7 @@
 	       const vector<pair<string, string> > &target_names,
 	       bool is_conflict)
   {
-    map<string, dummy_package *>::const_iterator pfound=packages_by_name.find(pkg_name);
-
-    if(pfound==packages_by_name.end())
-      throw NoSuchNameError("package", pkg_name);
-
-    dummy_package *pkg=pfound->second;
+    dummy_package *pkg=find_package_internal(pkg_name);
 
     set<dummy_version *, compare_dummy_versions> targets;
     set<dummy_package *, compare_dummy_packages> packages;
@@ -589,12 +605,10 @@
     for(vector<pair<string, string> >::const_iterator i=target_names.begin();
 	i!=target_names.end(); ++i)
       {
-	pfound=packages_by_name.find(i->first);
-	if(pfound == packages_by_name.end())
-	  throw NoSuchNameError("package", i->first);
+	dummy_package *pkg=find_package_internal(i->first);
 
-	packages.insert(pfound->second);
-	targets.insert(pfound->second->version_from_name(i->second));
+	packages.insert(pkg);
+	targets.insert(pkg->version_from_name(i->second));
       }
 
     if(!is_conflict)
@@ -686,13 +700,18 @@
 // The syntax is quite simple: it consists of whitespace-separated
 // words, of the form:
 //
-// TEST ::= UNIVERSE
-// UNIVERSE ::= PACKAGE | DEP
-// PACKAGE ::= "PACKAGE" pkgname < vername1 ... > curver
-// DEP ::= "DEP" pkgname1 vername1 -> < pkgname2 vername2 ... >
-//       | "DEP" pkgname1 vername1 !! < pkgname2 vername2 ... >
+// SCRIPT ::= "UNIVERSE" "[" UNIVERSE "]" TEST ...
+// UNIVERSE ::= (PACKAGE | DEP) ...
+// PACKAGE ::= "PACKAGE" pkgname "<" vername1 ... ">" currentver
+// DEP ::= "DEP" pkgname1 vername1 "->" "<" pkgname2 vername2 ... ">"
+//       | "DEP" pkgname1 vername1 "!!" "<" pkgname2 vername2 ... ">"
 //
-// The latter form is a Conflicts, and is implicitly converted to
+// TEST ::= "TEST" step_score broken_score "{" SCORE ... "}" "EXPECT" "(" SOLN ... ")"
+// SCORE ::= "SCORE" pkgname "<" vername1 score1 ... ">"
+// SOLN ::= step_count "ANY"
+//       |  step_count "<" pkgname1 vername1 ... ">"
+//
+// The second DEP form is a Conflicts, and is implicitly converted to
 // dependency form internally.
 
 class ParseError:public Exception
@@ -720,6 +739,7 @@
   return pair<string, string>(pkgname, vername);
 }
 
+/** Parses a universe to the closing ']'. */
 dummy_universe *parse_universe(istream &in)
 {
   dummy_universe *rval=new dummy_universe;
@@ -729,9 +749,14 @@
     {
       string s;
 
+      if(in.eof())
+	throw ParseError("Expected ']', 'PACKAGE', or 'DEP'; got EOF");
+
       in >> s >> ws;
 
-      if(s == "PACKAGE")
+      if(s == "]")
+	break;
+      else if(s == "PACKAGE")
 	{
 	  string pkgname;
 
@@ -754,15 +779,15 @@
 	    {
 	      string vername;
 
+	      if(in.eof())
+		throw ParseError("Expected version name or '>', got EOF");
+
 	      in >> vername >> ws;
 
 	      if(vername == ">")
 		break;
 
 	      vernames.push_back(vername);
-
-	      if(!in)
-		throw ParseError("Expected version name or '>', got EOF");
 	    }
 
 	  if(in.eof())
@@ -828,55 +853,335 @@
 	  rval->add_dep(source.first, source.second, targets,
 			is_conflict);
 	}
+      else
+	throw ParseError("Expected PACKAGE or DEP, got "+s);
+
+      if(in.eof())
+	throw ParseError("Expected ']' following universe declaration, got EOF.");
     }
 
   return rval;
 }
 
-int main(int argc, char **argv)
+/** Reads the list of scores into the resolver. */
+void read_scores(istream &f,
+		 dummy_universe *universe, dummy_resolver &resolver)
 {
-  int rval=0;
+  if(!universe)
+    throw ParseError("Internal error: NULL universe in read_scores");
 
-  for(int i=1; i<argc; ++i)
+  f >> ws;
+
+  while(f)
     {
-      ifstream f(argv[i]);
+      string s;
 
-      if(!f)
-	{
-	  cerr << "Couldn't read from file " << argv[i] << "." << endl;
-	  rval=-1;
-	}
+      f >> s >> ws;
 
-      try
+      if(s == "}")
+	return;
+      else if(s == "SCORE")
 	{
-	  dummy_universe *u=parse_universe(f);
+	  if(f.eof())
+	    throw ParseError("Expected package name following SCORE, got "+s);
+
+	  string pkgname;
+
+	  f >> pkgname >> ws;
+
+	  dummy_universe::package pkg=universe->find_package(pkgname);
+
+	  if(f.eof())
+	    throw ParseError("Expected '<' following package name, got EOF");
+
+	  f >> s >> ws;
 
-	  cout << "Universe:" << endl;
-	  dump_universe(*u, cout);
-	  cout << "==============================================" << endl;
-	  cout << "Trying to solve...." << endl;
+	  if(s != "<")
+	    throw ParseError("Expected '<' following package name, got "+s);
 
-	  dummy_resolver resolver(10, 10, *u);
+	  if(f.eof())
+	    throw ParseError("Expected '>' or version name, got EOF");
 
-	  try
+	  while(f)
 	    {
-	      dummy_resolver::solution s=resolver.find_next_solution(10000);
+	      string vername;
+	      int score;
+
+	      f >> vername >> ws;
+
+	      if(vername == ">")
+		break;
+
+	      dummy_universe::version ver=pkg.version_from_name(vername);
+
+	      if(f.eof())
+		throw ParseError("Expected score, got EOF");
 
-	      cout << endl;
-	      cout << "Found solution ";
-	      s.dump(cout);
-	      cout << endl;
+	      f >> score >> ws;
+
+	      if(f.eof())
+		throw ParseError("Expected '>' or version name, got EOF");
+
+	      if(!f)
+		throw ParseError("Error reading score of " + pkgname + " " + vername);
+
+	      resolver.set_version_score(ver, score);
 	    }
-	  catch(NoMoreSolutions e)
+
+	  if(f.eof())
+	    throw ParseError("Expected '}' or SCORE, got EOF");
+	}
+      else
+	throw ParseError("Expected 'SCORE' or '}', got "+s);
+    }
+
+  throw ParseError("Unexpected error reading score list.");
+}
+
+/** Reads the tail of a non-ANY SOLN form. */
+map<dummy_universe::package, dummy_resolver::version> read_solution(istream &f, dummy_universe *universe)
+{
+  if(!universe)
+    throw ParseError("Internal error: NULL universe passed to read_solution");
+
+  map<dummy_universe::package, dummy_resolver::version> rval;
+
+  f >> ws;
+
+  while(f)
+    {
+      string s;
+
+      if(f.eof())
+	throw ParseError("Expected '>' or package, got EOF");
+
+      f >> s >> ws;
+
+      if(s == ">")
+	return rval;
+      else
+	{
+	  if(f.eof())
+	    throw ParseError("Expected version, got EOF");
+
+	  dummy_universe::package pkg=universe->find_package(s);
+
+	  f >> s >> ws;
+
+	  if(f.eof())
+	    throw ParseError("Expected '>' or package name, got EOF");
+
+	  if(s == ">")
+	    throw ParseError("Expected version name, got '>'");
+
+	  dummy_universe::version ver=pkg.version_from_name(s);
+
+	  if(rval.find(pkg) != rval.end())
+	    throw ParseError("Package "+pkg.get_name()+" bound twice in solution");
+
+	  rval[pkg]=ver;
+	}
+    }
+
+  throw ParseError("Unexpected error reading solution list");
+}
+
+void run_test_file(istream &f)
+{
+  dummy_universe *universe=NULL;
+
+  f >> ws;
+
+  try
+    {
+      while(f)
+	{
+	  string s;
+
+	  if(f.eof())
+	    // This is the only place where EOF is valid.
+	    return;
+
+	  f >> s >> ws;
+
+	  if(s == "UNIVERSE")
 	    {
-	      cout << "No solutions found." << endl;
+	      if(f.eof())
+		throw ParseError("Expected '[' following UNIVERSE, got EOF.");
+
+	      f >> s >> ws;
+
+	      if(s != "[")
+		throw ParseError("Expected '[' following UNIVERSE, got " + s);
+
+	      dummy_universe *new_universe=parse_universe(f);
+
+	      delete universe;
+	      universe=new_universe;
 	    }
-	  catch(NoMoreTime e)
+	  else if(s == "TEST")
 	    {
-	      cout << "Could not solve the problem within 10000 steps." << endl;
+	      if(!universe)
+		throw ParseError("Expected UNIVERSE before TEST");
+
+	      if(f.eof())
+		throw ParseError("Expected step_score and broken_score following 'TEST', got EOF");
+
+	      int step_score;
+	      int broken_score;
+
+	      f >> step_score >> broken_score;
+
+	      if(f.eof())
+		throw ParseError("Expected '{' following broken_score, got EOF");
+
+	      if(!f)
+		throw ParseError("Error reading step_score and broken_score after 'TEST'");
+
+	      f >> s >> ws;
+
+	      if(s != "{")
+		throw ParseError("Expected '{' following TEST, got "+s);
+
+	      dummy_resolver resolver(step_score, broken_score, *universe);
+
+	      read_scores(f, universe, resolver);
+
+	      if(f.eof())
+		throw ParseError("Expected 'EXPECT', got EOF");
+
+	      f >> s >> ws;
+
+	      if(s != "EXPECT")
+		throw ParseError("Expected 'EXPECT', got "+s);
+
+	      if(f.eof())
+		throw ParseError("Expected '(' following EXPECT, got EOF");
+
+	      f >> s >> ws;
+
+	      if(s != "(")
+		throw ParseError("Expected '(' following EXPECT, got "+s);
+
+	      while(f)
+		{
+		  if(f.eof())
+		    throw ParseError("Expected ')' or package name, got EOF");
+
+		  f >> s >> ws;
+
+		  if(s == ")")
+		    break;
+
+		  int step_count=atoi(s.c_str());
+
+		  if(step_count<=0)
+		    throw ParseError("step_count must be a positive integer, not "+s);
+
+		  f >> s >> ws;
+
+		  if(s == "ANY")
+		    {
+		      try
+			{
+			  dummy_resolver::solution next_soln=resolver.find_next_solution(step_count);
+
+			  cout << "Next solution is ";
+			  next_soln.dump(cout);
+
+			  cout << " (ignored)" << endl;
+			}
+		      catch(NoMoreTime)
+			{
+			  cout << "Ran out of steps (ignored)" << endl;
+			}
+		      catch(NoMoreSolutions)
+			{
+			  cout << "Ran out of solutions (ignored)" << endl;
+			}
+		    }
+		  else if(s == "<")
+		    {
+		      try
+			{
+			  map<dummy_universe::package, dummy_resolver::version> expected=read_solution(f, universe);
+
+			  dummy_resolver::solution next_soln=resolver.find_next_solution(step_count);
+
+
+			  cout << "Next solution is ";
+			  next_soln.dump(cout);
+
+			  bool equal=true;
+
+			  map<dummy_universe::package, dummy_universe::version>::const_iterator expect_iter=expected.begin();
+			  map<dummy_universe::package, dummy_resolver::action>::const_iterator soln_iter=next_soln.get_actions().begin();
+
+			  while(equal &&
+				expect_iter != expected.end() &&
+				soln_iter != next_soln.get_actions().end())
+			    {
+			      if(expect_iter->first != soln_iter->first ||
+				 expect_iter->second != soln_iter->second.ver)
+				equal=false;
+			    }
+
+			  if(equal)
+			    cout << " (OK)" << endl;
+			  else
+			    {
+			      cout << " (FAILED)" << endl;
+			      cout << "Expected <";
+			      for(map<dummy_universe::package, dummy_universe::version>::const_iterator i=expected.begin();
+				  i!=expected.end(); ++i)
+				cout << i->first.get_name()
+				     << ":="
+				     << i->second.get_name();
+			      cout << ">" << endl;
+			    }
+			}
+		      catch(NoMoreSolutions)
+			{
+			  cout << "Ran out of solutions (FAILED)" << endl;
+			}
+		      catch(NoMoreTime)
+			{
+			  cout << "Ran out of time (FAILED)" << endl;
+			}
+		    }
+		  else
+		    throw ParseError("Expected ANY or '<', got "+s);
+		}
 	    }
+	  else
+	    throw ParseError("Expected UNIVERSE or TEST, got "+s);
+	}
+    }
+  catch(...)
+    {
+      delete universe;
+      throw;
+    }
+}
+
+int main(int argc, char **argv)
+{
+  int rval=0;
+
+  for(int i=1; i<argc; ++i)
+    {
+      ifstream f(argv[i]);
 
-	  delete u;
+      if(!f)
+	{
+	  cerr << "Couldn't read from file " << argv[i] << "." << endl;
+	  rval=-1;
+	}
+
+      try
+	{
+	  f >> ws;
+	  run_test_file(f);
 	}
       catch(const Exception &e)
 	{

Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/test1.txt
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/test1.txt	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/test1.txt	Fri Apr  8 16:56:57 2005
@@ -1,19 +1,25 @@
-PACKAGE p1 < v1 v2 v3 > v1
-PACKAGE p2 < v1 v2 v3 > v1
-PACKAGE p3 < v1 v2 v3 > v1
-PACKAGE p4 < v1 v2 v3 > v1
+UNIVERSE [
+  PACKAGE p1 < v1 v2 v3 > v1
+  PACKAGE p2 < v1 v2 v3 > v1
+  PACKAGE p3 < v1 v2 v3 > v1
+  PACKAGE p4 < v1 v2 v3 > v1
 
 
-DEP p1 v1 -> < p2 v2  p2 v3 >
-DEP p1 v2 -> < p2 v2  p2 v3 >
-DEP p1 v3 -> < p2 v2  p2 v3 >
+  DEP p1 v1 -> < p2 v2  p2 v3 >
+  DEP p1 v2 -> < p2 v2  p2 v3 >
+  DEP p1 v3 -> < p2 v2  p2 v3 >
 
-DEP p2 v1 -> < p3 v1  p3 v2  p3 v3 >
-DEP p2 v2 -> < p3 v1  p3 v2  p3 v3 >
+  DEP p2 v1 -> < p3 v1  p3 v2  p3 v3 >
+  DEP p2 v2 -> < p3 v1  p3 v2  p3 v3 >
 
-DEP p2 v1 !! < p1 v2  p1 v3 >
+  DEP p2 v1 !! < p1 v2  p1 v3 >
 
 
-DEP p3 v1 -> < p4 v1  p4 v2  p4 v3 >
-DEP p3 v2 -> < p4 v1  p4 v2  p4 v3 >
-DEP p3 v3 -> < p4 v1  p4 v2  p4 v3 >
+  DEP p3 v1 -> < p4 v1  p4 v2  p4 v3 >
+  DEP p3 v2 -> < p4 v1  p4 v2  p4 v3 >
+  DEP p3 v3 -> < p4 v1  p4 v2  p4 v3 >
+]
+
+TEST 10 10 { } EXPECT ( 10000 ANY
+                        10000 ANY
+                        10000 ANY )
\ No newline at end of file