Parcourir la source

[1068] refactoring for the test code so that it will be easily used
by update tests. the semantics should be preserved at the moment.
making a commit at this point mainly as a checkpoint.

JINMEI Tatuya il y a 13 ans
Parent
commit
8fe581570c
1 fichiers modifiés avec 165 ajouts et 49 suppressions
  1. 165 49
      src/lib/datasrc/tests/database_unittest.cc

+ 165 - 49
src/lib/datasrc/tests/database_unittest.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <boost/foreach.hpp>
+
 #include <gtest/gtest.h>
 
 #include <dns/name.h>
@@ -35,6 +37,10 @@ using namespace isc::dns;
 
 namespace {
 
+// Imaginary zone IDs used in the mock accessor below.
+const int READONLY_ZONE_ID = 42;
+const int WRITABLE_ZONE_ID = 4200;
+
 /*
  * An accessor with minimum implementation, keeping the original
  * "NotImplemented" methods.
@@ -46,7 +52,7 @@ public:
 
     virtual std::pair<bool, int> getZone(const std::string& name) const {
         if (name == "example.org.") {
-            return (std::pair<bool, int>(true, 42));
+            return (std::pair<bool, int>(true, READONLY_ZONE_ID));
         } else if (name == "null.example.org.") {
             return (std::pair<bool, int>(true, 13));
         } else if (name == "empty.example.org.") {
@@ -99,9 +105,15 @@ private:
  * implementation of the optional functionality.
  */
 class MockAccessor : public NopAccessor {
+    // Type of mock database "row"s
+    typedef std::map<std::string, std::vector< std::vector<std::string> > >
+        Domains;
+
 public:
-    MockAccessor()
-    {
+    MockAccessor() {
+        readonly_records_ = &readonly_records_master_;
+        update_records_ = &update_records_master_;
+        empty_records_ = &empty_records_master_;
         fillData();
     }
 private:
@@ -122,32 +134,27 @@ private:
                 throw std::exception();
             }
 
-            if (zone_id == 42) {
-                if (subdomains) {
-                    cur_name.clear();
-                    // Just walk everything and check if it is a subdomain.
-                    // If it is, just copy all data from there.
-                    for (Domains::const_iterator
-                         i(mock_accessor.records.begin());
-                         i != mock_accessor.records.end(); ++ i) {
-                        Name local(i->first);
-                        if (local.compare(isc::dns::Name(name)).
-                            getRelation() ==
-                            isc::dns::NameComparisonResult::SUBDOMAIN) {
-                            cur_name.insert(cur_name.end(), i->second.begin(),
-                                            i->second.end());
-                        }
-                    }
-                } else {
+            cur_record_ = 0;
+            const Domains& cur_records = mock_accessor.getMockRecords(zone_id);
+            if (cur_records.count(name) > 0) {
                     // we're not aiming for efficiency in this test, simply
                     // copy the relevant vector from records
-                    if (mock_accessor.records.count(searched_name_) > 0) {
-                        cur_name = mock_accessor.records.find(searched_name_)->
-                            second;
-                    } else {
-                        cur_name.clear();
+                    cur_name = cur_records.find(name)->second;
+            } else if (subdomains) {
+                cur_name.clear();
+                // Just walk everything and check if it is a subdomain.
+                // If it is, just copy all data from there.
+                for (Domains::const_iterator i(cur_records.begin());
+                     i != cur_records.end(); ++i) {
+                    const Name local(i->first);
+                    if (local.compare(Name(name)).getRelation() ==
+                        isc::dns::NameComparisonResult::SUBDOMAIN) {
+                        cur_name.insert(cur_name.end(), i->second.begin(),
+                                        i->second.end());
                     }
                 }
+            } else {
+                cur_name.clear();
             }
         }
 
@@ -262,7 +269,7 @@ private:
     };
 public:
     virtual IteratorContextPtr getAllRecords(int id) const {
-        if (id == 42) {
+        if (id == READONLY_ZONE_ID) {
             return (IteratorContextPtr(new MockIteratorContext()));
         } else if (id == 13) {
             return (IteratorContextPtr());
@@ -278,7 +285,7 @@ public:
     virtual IteratorContextPtr getRecords(const std::string& name, int id,
                                           bool subdomains) const
     {
-        if (id == 42) {
+        if (id == READONLY_ZONE_ID) {
             return (IteratorContextPtr(new MockNameIteratorContext(*this, id,
                 name, subdomains)));
         } else {
@@ -286,15 +293,64 @@ public:
         }
     }
 
+    //
+    // Helper methods to keep track of some update related activities
+    //
+    bool isRollbacked() const {
+        return (rollbacked_);
+    }
+
+    const vector<string>& getLastAdded() const {
+        return (columns_lastadded_);
+    }
+
+    // This allows the test code to get the accessor used in an update context
+    shared_ptr<const MockAccessor> getLatestClone() const {
+        return (latest_clone_);
+    }
+
 private:
-    typedef std::map<std::string, std::vector< std::vector<std::string> > >
-        Domains;
+    // The following member variables are storage and/or update work space
+    // of the test zone.  The "master"s are the real objects that contain
+    // the data, and they are shared among by all accessors cloned from
+    // an initially created one.  The pointer members allow the sharing.
+    // "readonly" is for normal lookups.  "update" is the workspace for
+    // updates.  When update starts it will be initialized either as an
+    // empty set (when replacing the entire zone) or as a copy of the
+    // "readonly" one.  "empty" is a sentinel to produce negative results.
+    Domains readonly_records_master_;
+    Domains* readonly_records_;
+    Domains update_records_master_;
+    Domains* update_records_;
+    const Domains empty_records_master_;
+    const Domains* empty_records_;
+
     // used as temporary storage during the building of the fake data
-    Domains records;
+    //Domains records;
+
     // used as temporary storage after searchForRecord() and during
     // getNextRecord() calls, as well as during the building of the
     // fake data
-    std::vector< std::vector<std::string> > cur_name;
+    std::vector< std::vector<std::string> > cur_name_;
+
+    // The columns that were most recently added via addRecordToZone()
+    vector<string> columns_lastadded_;
+
+    // Whether rollback operation has been performed for the database.
+    // Not useful except for purely testing purpose.
+    bool rollbacked_;
+
+    // Remember the mock accessor that was last cloned
+    boost::shared_ptr<MockAccessor> latest_clone_;
+
+    const Domains& getMockRecords(int zone_id) const {
+        if (zone_id == READONLY_ZONE_ID) {
+            return (*readonly_records_);
+        } else if (zone_id == WRITABLE_ZONE_ID) {
+            return (*update_records_);
+        }
+        return (*empty_records_);
+    }
 
     // Adds one record to the current name in the database
     // The actual data will not be added to 'records' until
@@ -308,21 +364,21 @@ private:
         columns.push_back(type);
         columns.push_back(sigtype);
         columns.push_back(rdata);
-        cur_name.push_back(columns);
+        cur_name_.push_back(columns);
     }
 
     // Adds all records we just built with calls to addRecords
-    // to the actual fake database. This will clear cur_name,
+    // to the actual fake database. This will clear cur_name_,
     // so we can immediately start adding new records.
     void addCurName(const std::string& name) {
-        ASSERT_EQ(0, records.count(name));
+        ASSERT_EQ(0, readonly_records_->count(name));
         // Append the name to all of them
         for (std::vector<std::vector<std::string> >::iterator
-             i(cur_name.begin()); i != cur_name.end(); ++ i) {
+             i(cur_name_.begin()); i != cur_name_.end(); ++ i) {
             i->push_back(name);
         }
-        records[name] = cur_name;
-        cur_name.clear();
+        (*readonly_records_)[name] = cur_name_;
+        cur_name_.clear();
     }
 
     // Fills the database with zone data.
@@ -503,23 +559,44 @@ TEST(DatabaseConnectionTest, getAllRecords) {
 
 class DatabaseClientTest : public ::testing::Test {
 public:
-    DatabaseClientTest() {
+    DatabaseClientTest() : zname_("example.org"), qname_("www.example.org"),
+                           qclass_(RRClass::IN()), qtype_(RRType::A()),
+                           rrttl_(3600)
+    {
         createClient();
+
+        // set up the commonly used finder.
+        DataSourceClient::FindResult zone(client_->findZone(zname_));
+        assert(zone.code == result::SUCCESS);
+        finder_ = dynamic_pointer_cast<DatabaseClient::Finder>(
+            zone.zone_finder);
+
+        // Test IN/A RDATA to be added in update tests.  Intentionally using
+        // different data than the initial data configured in the MockAccessor.
+        rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_));
+        rrset_->addRdata(rdata::createRdata(rrset_->getType(),
+                                            rrset_->getClass(), "192.0.2.2"));
+
+        // And its RRSIG.  Also different from the configured one.
+        rrsigset_.reset(new RRset(qname_, qclass_, RRType::RRSIG(),
+                                  rrttl_));
+        rrsigset_->addRdata(rdata::createRdata(rrsigset_->getType(),
+                                               rrsigset_->getClass(),
+                                               "A 5 3 0 20000101000000 "
+                                               "20000201000000 0 example.org. "
+                                               "FAKEFAKEFAKE"));
     }
+
     /*
      * We initialize the client from a function, so we can call it multiple
      * times per test.
      */
     void createClient() {
         current_accessor_ = new MockAccessor();
-        client_.reset(new DatabaseClient(RRClass::IN(),
+        client_.reset(new DatabaseClient(qclass_,
                                          shared_ptr<DatabaseAccessor>(
                                              current_accessor_)));
     }
-    // Will be deleted by client_, just keep the current value for comparison.
-    MockAccessor* current_accessor_;
-    shared_ptr<DatabaseClient> client_;
-    const std::string database_name_;
 
     /**
      * Check the zone finder is a valid one and references the zone ID and
@@ -531,21 +608,61 @@ public:
             dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
         ASSERT_NE(shared_ptr<DatabaseClient::Finder>(), finder) <<
             "Wrong type of finder";
-        EXPECT_EQ(42, finder->zone_id());
+        EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
         EXPECT_EQ(current_accessor_, &finder->getAccessor());
     }
 
     shared_ptr<DatabaseClient::Finder> getFinder() {
-        DataSourceClient::FindResult zone(
-            client_->findZone(Name("example.org")));
+        DataSourceClient::FindResult zone(client_->findZone(zname_));
         EXPECT_EQ(result::SUCCESS, zone.code);
         shared_ptr<DatabaseClient::Finder> finder(
             dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
-        EXPECT_EQ(42, finder->zone_id());
+        EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
 
         return (finder);
     }
 
+    // Helper methods for update tests
+    //bool isRollbacked(bool expected = false) const {
+    bool isRollbacked() const {
+        return (update_accessor_->isRollbacked());
+    }
+
+    void checkLastAdded(const char* const expected[]) const {
+        int i = 0;
+        BOOST_FOREACH(const string& column,
+                      current_accessor_->getLastAdded()) {
+            EXPECT_EQ(expected[i++], column);
+        }
+    }
+
+    void setUpdateAccessor() {
+        update_accessor_ = current_accessor_->getLatestClone();
+    }
+
+    // Will be deleted by client_, just keep the current value for comparison.
+    MockAccessor* current_accessor_;
+    shared_ptr<DatabaseClient> client_;
+    const std::string database_name_;
+
+    // The zone finder of the test zone commonly used in various tests.
+    shared_ptr<DatabaseClient::Finder> finder_;
+
+    // Some shortcut variables for commonly used test parameters
+    const Name zname_; // the zone name stored in the test data source
+    const Name qname_; // commonly used name to be found
+    const RRClass qclass_;      // commonly used RR class used with qname
+    const RRType qtype_;        // commonly used RR type used with qname
+    const RRTTL rrttl_;         // commonly used RR TTL
+    RRsetPtr rrset_;            // for adding/deleting an RRset
+    RRsetPtr rrsigset_;         // for adding/deleting an RRset
+
+    // update related objects to be tested
+    ZoneUpdaterPtr updater_;
+    shared_ptr<const MockAccessor> update_accessor_;
+
+    // placeholders
+    const std::vector<std::string> empty_rdatas_; // for NXRRSET/NXDOMAIN
     std::vector<std::string> expected_rdatas_;
     std::vector<std::string> expected_sig_rdatas_;
 };
@@ -1299,8 +1416,7 @@ TEST_F(DatabaseClientTest, getOrigin) {
     ASSERT_EQ(result::SUCCESS, zone.code);
     shared_ptr<DatabaseClient::Finder> finder(
         dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
-    EXPECT_EQ(42, finder->zone_id());
+    EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
     EXPECT_EQ(isc::dns::Name("example.org"), finder->getOrigin());
 }
-
 }