// Copyright (C) 2011 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 #include #include #include #include #include #include #include using namespace isc::datasrc; using namespace isc::data; std::string SQLITE_DBFILE_EXAMPLE_ORG = TEST_DATA_DIR "/example.org.sqlite3"; const std::string STATIC_DS_FILE = TEST_DATA_DIR "/static.zone"; const std::string ROOT_ZONE_FILE = TEST_DATA_DIR "/root.zone"; namespace { // note this helper only checks the error that is received up to the length // of the expected string. It will always pass if you give it an empty // expected_error void pathtestHelper(const std::string& file, const std::string& expected_error) { std::string error; try { DataSourceClientContainer(file, ElementPtr()); } catch (const DataSourceLibraryError& dsle) { error = dsle.what(); } ASSERT_LT(expected_error.size(), error.size()); EXPECT_EQ(expected_error, error.substr(0, expected_error.size())); } TEST(FactoryTest, paths) { // Test whether the paths are made absolute if they are not, // by inspecting the error that is raised when they are wrong const std::string error("dlopen failed for "); // With the current implementation, we can safely assume this has // been set for this test (as the loader would otherwise also fail // unless the loadable backend library happens to be installed) const std::string builddir(getenv("B10_FROM_BUILD")); // Absolute and ending with .so should have no change pathtestHelper("/no_such_file.so", error + "/no_such_file.so"); // If no ending in .so, it should get _ds.so pathtestHelper("/no_such_file", error + "/no_such_file_ds.so"); // If not starting with /, path should be added. For this test that // means the build directory as set in B10_FROM_BUILD pathtestHelper("no_such_file.so", error + builddir + "/src/lib/datasrc/.libs/no_such_file.so"); pathtestHelper("no_such_file", error + builddir + "/src/lib/datasrc/.libs/no_such_file_ds.so"); // Some tests with '.so' in the name itself pathtestHelper("no_such_file.so.something", error + builddir + "/src/lib/datasrc/.libs/no_such_file.so.something_ds.so"); pathtestHelper("/no_such_file.so.something", error + "/no_such_file.so.something_ds.so"); pathtestHelper("/no_such_file.so.something.so", error + "/no_such_file.so.something.so"); pathtestHelper("/no_such_file.so.so", error + "/no_such_file.so.so"); pathtestHelper("no_such_file.so.something", error + builddir + "/src/lib/datasrc/.libs/no_such_file.so.something_ds.so"); // Temporarily unset B10_FROM_BUILD to see that BACKEND_LIBRARY_PATH // is used unsetenv("B10_FROM_BUILD"); pathtestHelper("no_such_file.so", error + BACKEND_LIBRARY_PATH + "no_such_file.so"); // Put it back just in case setenv("B10_FROM_BUILD", builddir.c_str(), 1); // Test some bad input values ASSERT_THROW(DataSourceClientContainer("", ElementPtr()), DataSourceLibraryError); ASSERT_THROW(DataSourceClientContainer(".so", ElementPtr()), DataSourceLibraryError); } TEST(FactoryTest, sqlite3ClientBadConfig) { // We start out by building the configuration data bit by bit, // testing each form of 'bad config', until we have a good one. // Then we do some very basic operation on the client (detailed // tests are left to the implementation-specific backends) ElementPtr config; ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config = Element::create("asdf"); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config = Element::createMap(); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("class", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("class", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("class", Element::create("FOO")); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("class", Element::create("IN")); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("database_file", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("database_file", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("database_file", Element::create("/foo/bar/doesnotexist")); ASSERT_THROW(DataSourceClientContainer("sqlite3", config), DataSourceError); config->set("database_file", Element::create(SQLITE_DBFILE_EXAMPLE_ORG)); DataSourceClientContainer dsc("sqlite3", config); DataSourceClient::FindResult result1( dsc.getInstance().findZone(isc::dns::Name("example.org."))); ASSERT_EQ(result::SUCCESS, result1.code); DataSourceClient::FindResult result2( dsc.getInstance().findZone(isc::dns::Name("no.such.zone."))); ASSERT_EQ(result::NOTFOUND, result2.code); ZoneIteratorPtr iterator(dsc.getInstance().getIterator( isc::dns::Name("example.org."))); ZoneUpdaterPtr updater(dsc.getInstance().getUpdater( isc::dns::Name("example.org."), false)); } TEST(FactoryTest, memoryClient) { // We start out by building the configuration data bit by bit, // testing each form of 'bad config', until we have a good one. // Then we do some very basic operation on the client (detailed // tests are left to the implementation-specific backends) ElementPtr config; ASSERT_THROW(DataSourceClientContainer client("memory", config), DataSourceError); config = Element::create("asdf"); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config = Element::createMap(); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("type", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("type", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("type", Element::create("FOO")); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("type", Element::create("memory")); // no config at all should result in a default empty memory client ASSERT_NO_THROW(DataSourceClientContainer("memory", config)); config->set("class", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("class", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("class", Element::create("FOO")); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("class", Element::create("IN")); ASSERT_NO_THROW(DataSourceClientContainer("memory", config)); config->set("zones", ElementPtr()); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("zones", Element::create(1)); ASSERT_THROW(DataSourceClientContainer("memory", config), DataSourceError); config->set("zones", Element::createList()); DataSourceClientContainer dsc("memory", config); // Once it is able to load some zones, we should add a few tests // here to see that it does. DataSourceClient::FindResult result( dsc.getInstance().findZone(isc::dns::Name("no.such.zone."))); ASSERT_EQ(result::NOTFOUND, result.code); ASSERT_THROW(dsc.getInstance().getIterator(isc::dns::Name("example.org.")), DataSourceError); ASSERT_THROW(dsc.getInstance().getUpdater(isc::dns::Name("no.such.zone."), false), isc::NotImplemented); } TEST(FactoryTest, badType) { ASSERT_THROW(DataSourceClientContainer("foo", ElementPtr()), DataSourceError); } // Check the static data source can be loaded. TEST(FactoryTest, staticDS) { // The only configuration is the file to load. const ConstElementPtr config(new StringElement(STATIC_DS_FILE)); // Get the data source DataSourceClientContainer dsc("static", config); // And try getting something out to see if it really works. DataSourceClient::FindResult result(dsc.getInstance().findZone(isc::dns::Name("BIND"))); ASSERT_EQ(result::SUCCESS, result.code); EXPECT_EQ(isc::dns::Name("BIND"), result.zone_finder->getOrigin()); EXPECT_EQ(isc::dns::RRClass::CH(), result.zone_finder->getClass()); const isc::dns::ConstRRsetPtr version(result.zone_finder->find(isc::dns::Name("VERSION.BIND"), isc::dns::RRType::TXT())->rrset); ASSERT_NE(isc::dns::ConstRRsetPtr(), version); EXPECT_EQ(isc::dns::Name("VERSION.BIND"), version->getName()); EXPECT_EQ(isc::dns::RRClass::CH(), version->getClass()); EXPECT_EQ(isc::dns::RRType::TXT(), version->getType()); } // Check that file not containing BIND./CH is rejected // // FIXME: This test is disabled because the InMemoryZoneFinder::load does // not check if the data loaded correspond with the origin. The static // factory is not the place to fix that. TEST(FactoryTest, DISABLED_staticDSBadFile) { // The only configuration is the file to load. const ConstElementPtr config(new StringElement(STATIC_DS_FILE)); // See it does not want the file EXPECT_THROW(DataSourceClientContainer("static", config), DataSourceError); } // Check that some bad configs are rejected TEST(FactoryTest, staticDSBadConfig) { const char* configs[] = { // The file does not exist "\"/does/not/exist\"", // Bad types "null", "42", "{}", "[]", "true", NULL }; for (const char** config(configs); *config; ++config) { SCOPED_TRACE(*config); EXPECT_THROW(DataSourceClientContainer("static", Element::fromJSON(*config)), DataSourceError); } } } // end anonymous namespace