/* Basic test harness for the Variant class. This does NOT test all cases, * but should be a good starting point for the rest of your code. * This test assumes that there is a file called variant.h containing the * Variant class definition; the implementation should be in its own .cpp * file (probably variant.cpp) * * Good luck, and happy testing! */ #include #include #include #include #include #include #include // For time #include // For rand, srand #include // For sort, next_permutation, replace #include "variant.h" #include "variant.h" // Check for multiple-inclusion problems; see Handout #7 using namespace std; /* Helper function that checks that a given condition is true, failing * if unable to do so. */ const int WRAP_COUNTER = 23; void DoCheckCondition(const bool shouldBeTrue, const string& message, const int lineNumber) { static int numLines = 0; if(!shouldBeTrue) { cout << "FAILED: " << message << endl; cout << " (Test failed at line #" << lineNumber << ')' << endl; } else cout << "PASS: " << message << endl; if(++numLines % WRAP_COUNTER == 0) { cout << "Press any key to continue..." << endl; string line; getline(cin, line); } } /* Macro that invokes DoCheckCondition with the current line number. */ #define CheckCondition(cond, msg) DoCheckCondition((cond), (msg), __LINE__) /* Checks the functionality exported by Task 0. */ void BasicFunctionalityTest() { /* Default ctor testing. */ { // New scope keeps all of these variables local to this block. Variant one; CheckCondition(one.getType() == Variant::IntType, "Default ctor should set Variant to hold an integer."); CheckCondition(one.asInt() == 0, "Default ctor should set Variant to hold the value 0."); } /* Integer ctor testing. */ { Variant two = 137; CheckCondition(two.getType() == Variant::IntType, "Integer ctor should set Variant to hold an integer."); CheckCondition(two.asInt() == 137, "Storing 137 should return value 137."); } /* C string ctor testing. */ { Variant three = "Check me!"; CheckCondition(three.getType() == Variant::StringType, "C string ctor should set Variant to hold a string."); CheckCondition(three.asString() == "Check me!", "Stored \"Check me!\", should get back \"Check me!\""); } /* C++ string ctor testing. */ { Variant four = string("Check me!"); CheckCondition(four.getType() == Variant::StringType, "C++ string ctor should set Variant to hold a string."); CheckCondition(four.asString() == "Check me!", "Stored \"Check me!\", should get back \"Check me!\""); } } /* Checking the copying behavior (Task 1) */ void CopyingBehaviorTest() { /* Checking copy constructor. */ { Variant one = 137, two = "Check me!"; Variant three = one; Variant four = two; CheckCondition(three.getType() == Variant::IntType, "Copying an integer should make copy have integer type."); CheckCondition(three.asInt() == 137, "Copying an integer should copy the correct value."); CheckCondition(one.getType() == Variant::IntType, "Copying an integer should not change type of original."); CheckCondition(one.asInt() == 137, "Copying an integer should not change the original value."); CheckCondition(four.getType() == Variant::StringType, "Copying a string should make copy have string type."); CheckCondition(four.asString() == "Check me!", "Copying an string should copy the correct value."); CheckCondition(two.getType() == Variant::StringType, "Copying an string should not change type of original."); CheckCondition(two.asString() == "Check me!", "Copying an string should not change the original value."); } /* Checking assignment operator. */ { Variant one = 137, two = "Check me!", three, four; three = one; four = two; CheckCondition(three.getType() == Variant::IntType, "Copying an integer with op= should make copy have integer type."); CheckCondition(three.asInt() == 137, "Copying an integer with op= should copy the correct value."); CheckCondition(one.getType() == Variant::IntType, "Copying an integer with op= should not change type of original."); CheckCondition(one.asInt() == 137, "Copying an integer with op= should not change the original value."); CheckCondition(four.getType() == Variant::StringType, "Copying a string with op= should make copy have string type."); CheckCondition(four.asString() == "Check me!", "Copying an string with op= should copy the correct value."); CheckCondition(two.getType() == Variant::StringType, "Copying an string with op= should not change type of original."); CheckCondition(two.asString() == "Check me!", "Copying an string with op= should not change the original value."); } /* Checking self-assignment. */ { Variant one = 137; one = one = one = one; CheckCondition(one.getType() == Variant::IntType, "Self-assigning an integer should not change its type."); CheckCondition(one.asInt() == 137, "Self-assigning an integer should not change its value."); } { Variant one = "Check me!"; one = one = one = one; CheckCondition(one.getType() == Variant::StringType, "Self-assigning a string should not change its type."); CheckCondition(one.asString() == "Check me!", "Self-assigning a string should not change its value."); } } /* Checking the stream insertion operator (Task 2). * * If you see random output printed to the screen in addition to * failing tests, check to see that your stream insertion operator * is printing to the parameter of the operator << function rather * than directly to cout. */ void StreamInsertionTest() { /* See if integers print correctly. */ { ostringstream output; output << Variant(137); CheckCondition(output.str() == "137", "Writing a variant of value 137 into a stream should yield string 137."); } /* See if strings print correctly. */ { ostringstream output; output << Variant("Hello, world!"); CheckCondition(output.str() == "Hello, world!", "Writing a string variant should preserve the string."); } /* Check if stream manipulators still work. If this test begins failing, * don't worry about it too much, but it might indicate that your * insertion operator is too complicated. */ { ostringstream output; output << oct << Variant(137); CheckCondition(output.str() == "211", "Stream insertion should respect oct."); } /* Same, except for more complicated manipulators. */ { ostringstream output; output << setw(5) << setfill('A') << Variant(137); CheckCondition(output.str() == "AA137", "Stream insertion should respect fill and width."); } } /* Helper function which generates a random variant. */ Variant GetRandomVariant() { /* 50% chance to return an int. */ if(rand() % 2 == 0) return rand(); /* Otherwise, generate a random string of alphanumeric characters. */ const int MAX_STRING_LENGTH = 10; string result(rand() % MAX_STRING_LENGTH, ' '); /* Fill with random values. */ for(string::iterator itr = result.begin(); itr != result.end(); ++itr) *itr = (rand() % ('z' - 'A')) + 'A'; /* To make debugging easier, replace the [ and ] characters. */ replace(result.begin(), result.end(), ']', '*'); replace(result.begin(), result.end(), '[', '^'); return result; } /* Helper function which checks if two Variants are comparable. */ void CheckComparability(const Variant& one, const Variant& two) { Variant::Comparator comp; if(!comp(one, two) && !comp(two, one) && ((one.getType() == Variant::IntType && two.getType() == Variant::IntType && one.asInt() != two.asInt()) || (one.getType() == Variant::StringType && two.getType() == Variant::StringType && one.asString() != two.asString()) || (one.getType() != two.getType()))) { ostringstream reporter; reporter << "Cannot compare [" << one << "] and [" << two << ']'; CheckCondition(false, reporter.str()); } } /* Helper function which checks that two Variants are asymmetric in the * compare function (i.e. a < b => !(b < a)) */ void CheckAsymmetry(const Variant& one, const Variant& two) { Variant::Comparator comp; if(comp(one, two) && comp(two, one)) { ostringstream reporter; reporter << "[" << one << "] and [" << two << "] both compare less than each other"; CheckCondition(false, reporter.str()); } } /* Helper function which checks that the comparator is transitive * (i.e. if a < b and b < c, then a < c. */ void CheckTransitivity(const Variant& one, const Variant& two, const Variant& three) { /* This code uses the next_permutation algorithm to check all permutations of the * elements and see if transitivity fails in any of them. Putting the next_permutation * algorithm into a loop will exhaustively list all permutations of the input data. * Pretty snazzy, isn't it? */ vector elems; elems.push_back(&one); elems.push_back(&two); elems.push_back(&three); /* We need to sort these elements into ascending order by memory address so that * next_permutation works correctly. */ sort(elems.begin(), elems.end()); Variant::Comparator comp; do { if(comp(*elems[0], *elems[1]) && comp(*elems[1], *elems[2]) && !comp(*elems[0], *elems[2])) { ostringstream reporter; reporter << "[" << *elems[0] << "] < [" << *elems[1] << "] and " << "[" << *elems[1] << "] < [" << *elems[2] << "], but !(" << "[" << *elems[0] << "] < " << "[" << *elems[2] << "])"; CheckCondition(false, reporter.str()); } } while(next_permutation(elems.begin(), elems.end())); } /* Checking the comparator (Task 3) */ void ComparatorTest() { /* Generate multiple random values and ensure that they're comparable. */ const int NUM_TRIALS = 1000; for(int i = 0; i < NUM_TRIALS; ++i) { Variant one = GetRandomVariant(), two = GetRandomVariant(), three = GetRandomVariant(); /* Check that all three are comparable somehow. */ CheckComparability(one, two); CheckComparability(two, three); CheckComparability(one, three); /* Check for asymmetry. */ CheckAsymmetry(one, two); CheckAsymmetry(two, three); CheckAsymmetry(one, three); /* Check for transitivity. */ CheckTransitivity(one, two, three); } /* Testing the STL set. This may generate some nasty compiler errors * if your implementation is buggy. Also note that if your implementation * of the comparator is buggy that this may result in runtime errors. */ { typedef set VariantSet; VariantSet mySet; mySet.insert(42); mySet.insert(137); mySet.insert("Hello, world!"); mySet.insert("Hello, world!"); mySet.insert("Check me!"); CheckCondition(mySet.size() == 4, "There should be four elements in the set."); CheckCondition(mySet.find(42) != mySet.end(), "The set should contain 42."); CheckCondition(mySet.find(137) != mySet.end(), "The set should contain 137"); CheckCondition(mySet.find("Hello, world!") != mySet.end(), "The set should contain \"Hello, world!\""); CheckCondition(mySet.find("Check me!") != mySet.end(), "The set should contain \"Check me!\""); CheckCondition(mySet.find(0) == mySet.end(), "The set should not contain 0."); CheckCondition(mySet.erase(2718) == 0, "Removing 2718 should fail."); CheckCondition(mySet.erase(42) == 1, "Removing 42 should be successful."); CheckCondition(mySet.size() == 3, "There should be three elements in the set."); } } /* Seed the randomizer with the current time. */ void Randomize() { srand(static_cast(time(NULL))); } int main() { Randomize(); BasicFunctionalityTest(); CopyingBehaviorTest(); StreamInsertionTest(); ComparatorTest(); return 0; }