Parcourir la source

sync with trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac410@3593 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya il y a 14 ans
Parent
commit
dc5ef2b08e

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+  117.  [func]		jinmei
+	src/lib/datasrc: added new zone and zone table classes for the
+	support of in memory data source.  This is an intermediate step to
+	the bigger feature, and is not yet actually usable in practice.
+	(Trac #399, svn r3590)
+
   116.	[bug]		jerry
 	src/bin/xfrout: Xfrout and Auth will communicate by long tcp
 	connection, Auth needs to make a new connection only on the first

+ 1 - 1
src/bin/auth/asio_link.h

@@ -132,7 +132,7 @@ public:
     /// This constructor never throws an exception.
     ///
     /// \param asio_address The ASIO \c ip::address to be converted.
-    IOAddress(const asio::ip::address& asio_adress);
+    IOAddress(const asio::ip::address& asio_address);
     //@}
 
     /// \brief Convert the address to a string.

+ 2 - 2
src/bin/auth/auth_srv.h

@@ -84,7 +84,7 @@ public:
             isc::xfr::AbstractXfroutClient& xfrout_client);
     ~AuthSrv();
     //@}
-    /// \return \c true if the \message contains a response to be returned;
+    /// \return \c true if the \a message contains a response to be returned;
     /// otherwise \c false.
     bool processMessage(const asio_link::IOMessage& io_message,
                         isc::dns::Message& message,
@@ -138,7 +138,7 @@ public:
     /// containing the result of the update operation.
     isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
 
-    /// \param Returns the command and configuration session for the
+    /// \brief Returns the command and configuration session for the
     /// \c AuthSrv.
     ///
     /// This method never throws an exception.

+ 2 - 2
src/lib/bench/benchmark.h

@@ -200,7 +200,7 @@ private:
     BenchMark(const BenchMark& source);
     BenchMark& operator=(const BenchMark& source);
 public:
-    /// \bench Constructor for immediate run.
+    /// \brief Constructor for immediate run.
     ///
     /// This is the constructor that is expected to be used normally.
     /// It runs the benchmark within the constructor and prints the result,
@@ -217,7 +217,7 @@ public:
         initialize(true);
     }
 
-    /// \bench Constructor for finer-grained control.
+    /// \brief Constructor for finer-grained control.
     ///
     /// This constructor takes the third parameter, \c immediate, to control
     /// whether to run the benchmark within the constructor.

+ 1 - 0
src/lib/datasrc/Makefile.am

@@ -15,3 +15,4 @@ libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
 libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
 libdatasrc_la_SOURCES += query.h query.cc
 libdatasrc_la_SOURCES += cache.h cache.cc
+libdatasrc_la_SOURCES += zonetable.h zonetable.cc

+ 3 - 3
src/lib/datasrc/cache.h

@@ -170,9 +170,9 @@ public:
     /// then promoted to the head of the LRU queue.  (NOTE: Because
     /// of this, "retrieve" cannot be implemented as a const method.)
     ///
-    /// \param name The query name
-    /// \param rrclass The query class
-    /// \param rrtype The query type
+    /// \param qname The query name
+    /// \param qclass The query class
+    /// \param qtype The query type
     /// \param rrset Returns the RRset found, if any, to the caller
     /// \param flags Returns the flags, if any, to the caller
     ///

+ 1 - 0
src/lib/datasrc/tests/Makefile.am

@@ -24,6 +24,7 @@ run_unittests_SOURCES += static_unittest.cc
 run_unittests_SOURCES += query_unittest.cc
 run_unittests_SOURCES += cache_unittest.cc
 run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
+run_unittests_SOURCES += zonetable_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)

+ 104 - 0
src/lib/datasrc/tests/zonetable_unittest.cc

@@ -0,0 +1,104 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/zonetable.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+
+namespace {
+TEST(ZoneTest, init) {
+    Zone zone(RRClass::IN(), Name("example.com"));
+    EXPECT_EQ(Name("example.com"), zone.getOrigin());
+    EXPECT_EQ(RRClass::IN(), zone.getClass());
+
+    Zone ch_zone(RRClass::CH(), Name("example"));
+    EXPECT_EQ(Name("example"), ch_zone.getOrigin());
+    EXPECT_EQ(RRClass::CH(), ch_zone.getClass());
+}
+
+class ZoneTableTest : public ::testing::Test {
+protected:
+    ZoneTableTest() : zone1(new Zone(RRClass::IN(), Name("example.com"))),
+                      zone2(new Zone(RRClass::IN(), Name("example.net"))),
+                      zone3(new Zone(RRClass::IN(), Name("example")))
+    {}
+    ZoneTable zone_table;
+    ZonePtr zone1, zone2, zone3;
+};
+
+TEST_F(ZoneTableTest, add) {
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone1));
+    EXPECT_EQ(ZoneTable::EXIST, zone_table.add(zone1));
+    // names are compared in a case insensitive manner.
+    EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
+                  ZonePtr(new Zone(RRClass::IN(), Name("EXAMPLE.COM")))));
+
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
+
+    // Zone table is indexed only by name.  Duplicate origin name with
+    // different zone class isn't allowed.
+    EXPECT_EQ(ZoneTable::EXIST, zone_table.add(
+                  ZonePtr(new Zone(RRClass::CH(), Name("example.com")))));
+
+    /// Bogus zone (NULL)
+    EXPECT_THROW(zone_table.add(ZonePtr()), isc::InvalidParameter);
+}
+
+TEST_F(ZoneTableTest, remove) {
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone1));
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
+
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.remove(Name("example.net")));
+    EXPECT_EQ(ZoneTable::NOTFOUND, zone_table.remove(Name("example.net")));
+}
+
+TEST_F(ZoneTableTest, find) {
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone1));
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone2));
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone3));
+
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.find(Name("example.com")).code);
+    EXPECT_EQ(Name("example.com"),
+              zone_table.find(Name("example.com")).zone->getOrigin());
+
+    EXPECT_EQ(ZoneTable::NOTFOUND,
+              zone_table.find(Name("example.org")).code);
+    EXPECT_EQ(static_cast<const Zone*>(NULL),
+              zone_table.find(Name("example.org")).zone);
+
+    // there's no exact match.  the result should be the longest match,
+    // and the code should be PARTIALMATCH.
+    EXPECT_EQ(ZoneTable::PARTIALMATCH,
+              zone_table.find(Name("www.example.com")).code);
+    EXPECT_EQ(Name("example.com"),
+              zone_table.find(Name("www.example.com")).zone->getOrigin());
+
+    // make sure the partial match is indeed the longest match by adding
+    // a zone with a shorter origin and query again.
+    ZonePtr zone_com(new Zone(RRClass::IN(), Name("com")));
+    EXPECT_EQ(ZoneTable::SUCCESS, zone_table.add(zone_com));
+    EXPECT_EQ(Name("example.com"),
+              zone_table.find(Name("www.example.com")).zone->getOrigin());
+}
+}

+ 110 - 0
src/lib/datasrc/zonetable.cc

@@ -0,0 +1,110 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Note: map and utility (for 'pair') are for temporary workaround.
+// we'll soon replace them with built-in intelligent backend structure. 
+#include <map>
+#include <utility>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/zonetable.h>
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+
+struct Zone::ZoneImpl {
+    ZoneImpl(const RRClass& zone_class, const Name& origin) :
+        zone_class_(zone_class), origin_(origin)
+    {}
+    RRClass zone_class_;
+    Name origin_;
+};
+
+Zone::Zone(const RRClass& zone_class, const Name& origin) : impl_(NULL) {
+    impl_ = new ZoneImpl(zone_class, origin);
+}
+
+Zone::~Zone() {
+    delete impl_;
+}
+
+const Name&
+Zone::getOrigin() const {
+    return (impl_->origin_);
+}
+
+const RRClass&
+Zone::getClass() const {
+    return (impl_->zone_class_);
+}
+
+// This is a temporary, inefficient implementation using std::map and handmade
+// iteration to realize longest match.
+
+struct ZoneTable::ZoneTableImpl {
+    typedef map<Name, ZonePtr> ZoneMap;
+    typedef pair<Name, ZonePtr> NameAndZone;
+    ZoneMap zones;
+};
+
+ZoneTable::ZoneTable() : impl_(new ZoneTableImpl)
+{}
+
+ZoneTable::~ZoneTable() {
+    delete impl_;
+}
+
+ZoneTable::Result
+ZoneTable::add(ZonePtr zone) {
+    if (!zone) {
+        isc_throw(InvalidParameter,
+                  "Null pointer is passed to ZoneTable::add()");
+    }
+
+    if (impl_->zones.insert(
+            ZoneTableImpl::NameAndZone(zone->getOrigin(), zone)).second
+        == true) {
+        return (SUCCESS);
+    } else {
+        return (EXIST);
+    }
+}
+
+ZoneTable::Result
+ZoneTable::remove(const Name& origin) {
+    return (impl_->zones.erase(origin) == 1 ? SUCCESS : NOTFOUND);
+}
+
+ZoneTable::FindResult
+ZoneTable::find(const Name& name) const {
+    // Inefficient internal loop to find a longest match.
+    // This will be replaced with a single call to more intelligent backend.
+    for (int i = 0; i < name.getLabelCount(); ++i) {
+        Name matchname(name.split(i));
+        ZoneTableImpl::ZoneMap::const_iterator found =
+            impl_->zones.find(matchname);
+        if (found != impl_->zones.end()) {
+            return (FindResult(i == 0 ? SUCCESS : PARTIALMATCH,
+                               (*found).second.get()));
+        }
+    }
+    return (FindResult(NOTFOUND, NULL));
+}
+} // end of namespace datasrc
+} // end of namespace isc

+ 248 - 0
src/lib/datasrc/zonetable.h

@@ -0,0 +1,248 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ZONETABLE_H
+#define __ZONETABLE_H 1
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dns {
+class Name;
+class RRClass;
+};
+
+namespace datasrc {
+
+/// \brief A single authoritative zone
+///
+/// The \c Zone class represents a DNS zone as part of %data source.
+///
+/// At the moment this is provided mainly for making the \c ZoneTable class
+/// testable, and only provides a minimal set of features.
+/// This is why this class is defined in the same header file, but it may
+/// have to move to a separate header file when we understand what is
+/// necessary for this class for actual operation.
+/// Likewise, it will have more features.  For example, it will maintain
+/// information about the location of a zone file, whether it's loaded in
+/// memory, etc.
+class Zone {
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    /// \b Note:
+    /// The copy constructor and the assignment operator are intentionally
+    /// defined as private, making this class non copyable.
+    //@{
+private:
+    Zone(const Zone& source);
+    Zone& operator=(const Zone& source);
+public:
+    /// \brief Constructor from zone parameters.
+    ///
+    /// This constructor internally involves resource allocation, and if
+    /// it fails, a corresponding standard exception will be thrown.
+    /// It never throws an exception otherwise.
+    ///
+    /// \param rrclass The RR class of the zone.
+    /// \param origin The origin name of the zone.
+    Zone(const isc::dns::RRClass& rrclass, const isc::dns::Name& origin);
+
+    /// The destructor.
+    ~Zone();
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    /// These methods never throw an exception.
+    //@{
+    /// \brief Return the origin name of the zone.
+    const isc::dns::Name& getOrigin() const;
+
+    /// \brief Return the RR class of the zone.
+    const isc::dns::RRClass& getClass() const;
+    //@}
+
+private:
+    struct ZoneImpl;
+    ZoneImpl* impl_;
+};
+
+/// \brief A pointer-like type pointing to a \c Zone object.
+typedef boost::shared_ptr<Zone> ZonePtr;
+
+/// \brief A pointer-like type pointing to a \c Zone object.
+typedef boost::shared_ptr<const Zone> ConstZonePtr;
+
+/// \brief A set of authoritative zones.
+///
+/// The \c ZoneTable class represents a set of zones of the same RR class
+/// and provides a basic interface to help DNS lookup processing.
+/// For a given domain name, its \c find() method searches the set for a zone
+/// that gives a longest match against that name.
+///
+/// The set of zones are assumed to be of the same RR class, but the
+/// \c ZoneTable class does not enforce the assumption through its interface.
+/// For example, the \c add() method does not check if the new zone
+/// is of the same RR class as that of the others already in the table.
+/// It is caller's responsibility to ensure this assumption.
+///
+/// <b>Notes to developer:</b>
+///
+/// The add() method takes a (Boost) shared pointer because it would be
+/// inconvenient to require the caller to maintain the ownership of zones,
+/// while it wouldn't be safe to delete unnecessary zones inside the zone
+/// table.
+///
+/// On the other hand, the find() method returns a bare pointer, rather than
+/// the shared pointer, in order to minimize the dependency on Boost
+/// definitions in our public interfaces.  This means the caller can only
+/// refer to the returned object (via the pointer) for a short period.
+///  It should be okay for simple lookup purposes, but if we see the need
+/// for keeping a \c Zone object for a longer period of context, we may
+/// have to revisit this decision.
+///
+/// Currently, \c FindResult::zone is immutable for safety.
+/// In future versions we may want to make it changeable.  For example,
+/// we may want to allow configuration update on an existing zone.
+///
+/// In BIND 9's "zt" module, the equivalent of \c find() has an "option"
+/// parameter.  The only defined option is the one to specify the "no exact"
+/// mode, and the only purpose of that mode is to prefer a second longest match
+/// even if there is an exact match in order to deal with type DS query.
+/// This trick may help enhance performance, but it also seems to make the
+/// implementation complicated for a very limited, minor case.  So, for now,
+/// we don't introduce the special mode, and, since it was the only reason to
+/// have search options in BIND 9, our initial implementation doesn't provide
+/// a switch for options.
+class ZoneTable {
+public:
+    /// Result codes of various public methods of \c ZoneTable.
+    ///
+    /// The detailed semantics may differ in different methods.
+    /// See the description of specific methods for more details.
+    enum Result {
+        SUCCESS,  ///< The operation is successful.
+        EXIST,    ///< A zone is already stored in \c ZoneTable.
+        NOTFOUND, ///< The specified zone is not found in \c ZoneTable.
+        PARTIALMATCH ///< \c Only a partial match is found in \c find(). 
+    };
+
+    /// \brief A helper structure to represent the search result of
+    /// <code>ZoneTable::find()</code>.
+    ///
+    /// This is a straightforward pair of the result code and a pointer
+    /// to the found zone to represent the result of \c find().
+    /// We use this in order to avoid overloading the return value for both
+    /// the result code ("success" or "not found") and the found object,
+    /// i.e., avoid using \c NULL to mean "not found", etc.
+    ///
+    /// This is a simple value class with no internal state, so for
+    /// convenience we allow the applications to refer to the members
+    /// directly.
+    ///
+    /// See the description of \c find() for the semantics of the member
+    /// variables.
+    struct FindResult {
+        FindResult(Result param_code, const Zone* param_zone) :
+            code(param_code), zone(param_zone)
+        {}
+        const Result code;
+        const Zone* const zone;
+    };
+
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    /// \b Note:
+    /// The copy constructor and the assignment operator are intentionally
+    /// defined as private, making this class non copyable.
+    //@{
+private:
+    ZoneTable(const ZoneTable& source);
+    ZoneTable& operator=(const ZoneTable& source);
+
+public:
+    /// Default constructor.
+    ///
+    /// This constructor internally involves resource allocation, and if
+    /// it fails, a corresponding standard exception will be thrown.
+    /// It never throws an exception otherwise.
+    ZoneTable();
+
+    /// The destructor.
+    ~ZoneTable();
+    //@}
+
+    /// Add a \c Zone to the \c ZoneTable.
+    ///
+    /// \c zone must not be associated with a NULL pointer; otherwise
+    /// an exception of class \c InvalidParameter will be thrown.
+    /// If internal resource allocation fails, a corresponding standard
+    /// exception will be thrown.
+    /// This method never throws an exception otherwise.
+    ///
+    /// \param zone A \c Zone object to be added.
+    /// \return \c SUCCESS If the zone is successfully added to the zone table.
+    /// \return \c EXIST The zone table already stores a zone that has the
+    /// same origin.
+    Result add(ZonePtr zone);
+
+    /// Remove a \c Zone of the given origin name from the \c ZoneTable.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param origin The origin name of the zone to be removed.
+    /// \return \c SUCCESS If the zone is successfully removed from the
+    /// zone table.
+    /// \return \c NOTFOUND The zone table does not store the zone that matches
+    /// \c origin.
+    Result remove(const isc::dns::Name& origin);
+
+    /// Find a \c Zone that best matches the given name in the \c ZoneTable.
+    ///
+    /// It searches the internal storage for a \c Zone that gives the
+    /// longest match against \c name, and returns the result in the
+    /// form of a \c FindResult object as follows:
+    /// - \c code: The result code of the operation.
+    ///   - \c SUCCESS: A zone that gives an exact match is found
+    ///   - \c PARTIALMATCH: A zone whose origin is a super domain of
+    ///     \c name is found (but there is no exact match)
+    ///   - \c NOTFOUND: For all other cases.
+    /// - \c zone: A pointer to the found \c Zone object if one is found;
+    /// otherwise \c NULL.
+    ///
+    /// The pointer returned in the \c FindResult object is only valid until
+    /// the corresponding zone is removed from the zone table.
+    /// The caller must ensure that the zone is held in the zone table while
+    /// it needs to refer to it.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param name A domain name for which the search is performed.
+    /// \return A \c FindResult object enclosing the search result (see above).
+    FindResult find(const isc::dns::Name& name) const;
+
+private:
+    struct ZoneTableImpl;
+    ZoneTableImpl* impl_;
+};
+}
+}
+#endif  // __ZONETABLE_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 2 - 2
src/lib/exceptions/exceptions.h

@@ -40,7 +40,7 @@ public:
     /// file line number.
     ///
     /// @param file the file name where the exception was thrown.
-    /// @param line the line in @ref file where the exception was thrown.
+    /// @param line the line in \a file where the exception was thrown.
     /// @param what a description (type) of the exception.
     Exception(const char* file, size_t line, const char* what) :
         file_(file), line_(line), what_(what) {}
@@ -49,7 +49,7 @@ public:
     /// file line number.
     ///
     /// @param file the file name where the exception was thrown.
-    /// @param line the line in @ref file where the exception was thrown.
+    /// @param line the line in \a file where the exception was thrown.
     /// @param what a description (type) of the exception.
     Exception(const char* file, size_t line, const std::string& what) :
         file_(file), line_(line), what_(what) {}

+ 1 - 0
src/lib/python/isc/notify/notify_out.py

@@ -367,6 +367,7 @@ class NotifyOut:
                     zone_id = self._waiting_zones.pop(0) 
                     self._notify_infos[zone_id].prepare_notify_out()
                     self.notify_num += 1 
+                    self._notifying_zones.append(zone_id)
 
     def _send_notify_message_udp(self, zone_notify_info, addrinfo):
         msg, qid = self._create_notify_message(zone_notify_info.zone_name, 

+ 1 - 1
src/lib/python/isc/notify/tests/notify_out_test.py

@@ -156,7 +156,7 @@ class TestNotifyOut(unittest.TestCase):
         com_info = self._notify._notify_infos[('com.', 'IN')]
         self._notify._notify_next_target(com_info)
         self.assertEqual(2, self._notify.notify_num)
-        self.assertEqual(0, len(self._notify._notifying_zones))
+        self.assertEqual(2, len(self._notify._notifying_zones))
     
     def test_handle_notify_reply(self):
         self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg'))