/* * Copyright (C) 2008-2009 Patrick Ohly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include "test.h" #include #include #include #include #include #include #include #include SE_BEGIN_CXX using namespace std; SingleFileConfigTree::SingleFileConfigTree(const boost::shared_ptr &data) : m_data(data) { readFile(); } SingleFileConfigTree::SingleFileConfigTree(const string &fullpath) : m_data(new FileDataBlob(fullpath, true)) { readFile(); } boost::shared_ptr SingleFileConfigTree::open(const string &filename) { string normalized = normalizePath(string("/") + filename); boost::shared_ptr &entry = m_nodes[normalized]; if (entry) { return entry; } string name = getRootPath() + " - " + normalized; boost::shared_ptr data; BOOST_FOREACH(const FileContent_t::value_type &file, m_content) { if (file.first == normalized) { data.reset(new StringDataBlob(name, file.second, true)); break; } } if (!data) { /* * creating new files not supported, would need support for detecting * StringDataBlob::write() */ data.reset(new StringDataBlob(name, boost::shared_ptr(), true)); } entry.reset(new IniFileConfigNode(data)); return entry; } void SingleFileConfigTree::flush() { // not implemented, cannot write anyway } void SingleFileConfigTree::remove(const string &path) { SE_THROW("internal error: SingleFileConfigTree::remove() called"); } void SingleFileConfigTree::reset() { m_nodes.clear(); readFile(); } boost::shared_ptr SingleFileConfigTree::open(const string &path, PropertyType type, const string &otherId) { string fullpath = path; if (!fullpath.empty()) { fullpath += "/"; } switch (type) { case visible: fullpath += "config.ini"; break; case hidden: fullpath += ".internal.ini"; break; case other: fullpath += ".other.ini"; break; case server: fullpath += ".server.ini"; break; } return open(fullpath); } static void checkChild(const string &normalized, const string &node, set &subdirs) { if (boost::starts_with(node, normalized)) { string remainder = node.substr(normalized.size()); size_t offset = remainder.find('/'); if (offset != remainder.npos) { // only directories underneath path matter subdirs.insert(remainder.substr(0, offset)); } } } list SingleFileConfigTree::getChildren(const string &path) { set subdirs; string normalized = normalizePath(string("/") + path); if (normalized != "/") { normalized += "/"; } // must check both actual files as well as unsaved nodes BOOST_FOREACH(const FileContent_t::value_type &file, m_content) { checkChild(normalized, file.first, subdirs); } BOOST_FOREACH(const NodeCache_t::value_type &file, m_nodes) { checkChild(normalized, file.first, subdirs); } list result; BOOST_FOREACH(const string &dir, subdirs) { result.push_back(dir); } return result; } void SingleFileConfigTree::readFile() { boost::shared_ptr in(m_data->read()); boost::shared_ptr content; string line; m_content.clear(); while (getline(*in, line)) { if (boost::starts_with(line, "=== ") && boost::ends_with(line, " ===")) { string name = line.substr(4, line.size() - 8); name = normalizePath(string("/") + name); content.reset(new string); m_content[name] = content; } else if (content) { (*content) += line; (*content) += "\n"; } } } #ifdef ENABLE_UNIT_TESTS class SingleIniTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SingleIniTest); CPPUNIT_TEST(simple); CPPUNIT_TEST_SUITE_END(); void simple() { boost::shared_ptr data(new string); data->assign("# comment\n" "# foo\n" "=== foo/config.ini ===\n" "foo = bar\n" "foo2 = bar2\n" "=== foo/.config.ini ===\n" "foo_internal = bar_internal\n" "foo2_internal = bar2_internal\n" "=== /bar/.internal.ini ===\n" "bar = foo\n" "=== sources/addressbook/config.ini ===\n" "=== sources/calendar/config.ini ===\n" "evolutionsource = Personal\n"); boost::shared_ptr blob(new StringDataBlob("test", data, true)); SingleFileConfigTree tree(blob); boost::shared_ptr node; node = tree.open("foo/config.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(node->exists()); CPPUNIT_ASSERT_EQUAL(string("test - /foo/config.ini"), node->getName()); CPPUNIT_ASSERT_EQUAL(string("bar"), node->readProperty("foo")); CPPUNIT_ASSERT_EQUAL(string("bar2"), node->readProperty("foo2")); node = tree.open("/foo/config.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(node->exists()); node = tree.open("foo//.config.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(node->exists()); CPPUNIT_ASSERT_EQUAL(string("bar_internal"), node->readProperty("foo_internal")); CPPUNIT_ASSERT_EQUAL(string("bar2_internal"), node->readProperty("foo2_internal")); node = tree.open("bar///./.internal.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(node->exists()); CPPUNIT_ASSERT_EQUAL(string("foo"), node->readProperty("bar")); node = tree.open("sources/addressbook/config.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(node->exists()); node = tree.open("sources/calendar/config.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(node->exists()); CPPUNIT_ASSERT_EQUAL(string("Personal"), node->readProperty("evolutionsource")); node = tree.open("no-such-source/config.ini"); CPPUNIT_ASSERT(node); CPPUNIT_ASSERT(!node->exists()); list dirs = tree.getChildren(""); CPPUNIT_ASSERT_EQUAL(string("bar|foo|no-such-source|sources"), boost::join(dirs, "|")); dirs = tree.getChildren("sources/"); CPPUNIT_ASSERT_EQUAL(string("addressbook|calendar"), boost::join(dirs, "|")); } }; SYNCEVOLUTION_TEST_SUITE_REGISTRATION(SingleIniTest); #endif // ENABLE_UNIT_TESTS SE_END_CXX