bool App::initConfigs(Config& defaults) { bool rval = true; // set BITMUNK_HOME keyword ConfigManager* cm = getConfigManager(); Config cfg = getConfig()["monarch.app.Core"]; cm->setKeyword("BITMUNK_HOME", cfg["home"]->getString()); // set TMP_DIR keyword string tmp; if(File::getTemporaryDirectory(tmp)) { cm->setKeyword("TMP_DIR", tmp.c_str()); } // insert Bitmunk specific config groups cfg = cm->getConfig("command line", true, false); ConfigManager::ConfigId cmdLineParent = cfg[ConfigManager::PARENT]->getString(); rval = // insert configs before command line cm->addConfig(makeConfig( BITMUNK_APP ".beforeSystem.empty", "before system", cmdLineParent)) && cm->addConfig(makeConfig( BITMUNK_APP ".system.empty", "system", "before system")) && cm->addConfig(makeConfig( BITMUNK_APP ".afterSystem.empty", "after system", "system")) && cm->addConfig(makeConfig( BITMUNK_APP ".beforeSystemUser.empty", "before system user", "after system")) && cm->addConfig(makeConfig( BITMUNK_APP ".systemUser.empty", "system user", "before system user")) && cm->addConfig(makeConfig( BITMUNK_APP ".afterSystemUser.empty", "after system user", "system user")) && // change command line's parent to after system user cm->setParent("command line", "after system user"); cm->addConfig(makeConfig( BITMUNK_APP ".beforeUsers.empty", "before users", "main")) && cm->addConfig(makeConfig( BITMUNK_APP ".users.empty", "users", "before users")) && cm->addConfig(makeConfig( BITMUNK_APP ".afterUsers.empty", "after users", "users")); return rval; }
static void runConfigManagerTest(TestRunner& tr) { tr.group("ConfigManager"); tr.test("init"); { DynamicObject expect; expect->setType(Map); ConfigManager cm; Config cfg; cfg[ConfigManager::ID] = "config"; cfg[ConfigManager::MERGE]->setType(Map); assert(cm.addConfig(cfg)); assertDynoCmp(cm.getConfig("config", true), cfg); assertDynoCmp(cm.getConfig("config", false), expect); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("init & clear"); { DynamicObject expect; expect->setType(Map); ConfigManager cm; Config cfg; cfg[ConfigManager::ID] = "config"; cfg[ConfigManager::MERGE]->setType(Map); assert(cm.addConfig(cfg)); cm.clear(); Config cfg2 = cm.getConfig("config"); assert(cfg2.isNull()); } tr.passIfException(); tr.test("1 config"); { DynamicObject expect; expect->setType(Map); expect["a"] = 0; ConfigManager cm; Config cfg; cfg[ConfigManager::ID] = "config"; cfg[ConfigManager::MERGE]["a"] = 0; assert(cm.addConfig(cfg)); assertNoExceptionSet(); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("config change"); { ConfigManager cm; Config cfg; cfg[ConfigManager::ID] = "config"; cfg[ConfigManager::MERGE]["a"] = 0; assert(cm.addConfig(cfg)); DynamicObject a; a["a"] = 0; assertDynoCmp(cm.getConfig("config"), a); Config change = cm.getConfig("config", true); change[ConfigManager::MERGE]["a"] = 1; assert(cm.setConfig(change)); DynamicObject expect; expect["a"] = 1; assert(cm.getConfig("config") != a); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("invalid set config"); { ConfigManager cm; Config cfg; cfg[ConfigManager::ID] = "config"; cfg[ConfigManager::MERGE]["a"] = 0; assert(!cm.setConfig(cfg)); } tr.passIfException(); tr.test("double add config"); { ConfigManager cm; Config cfg; cfg[ConfigManager::ID] = "config"; cfg[ConfigManager::MERGE]["a"] = 0; assert(cm.addConfig(cfg)); cfg[ConfigManager::MERGE]["a"] = 1; assert(cm.addConfig(cfg)); DynamicObject expect; expect["a"] = 1; assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("add"); { DynamicObject expect; expect["a"] = 0; expect["b"] = 1; expect["c"] = 2; ConfigManager cm; Config a; a[ConfigManager::ID] = "config"; a[ConfigManager::MERGE]["a"] = 0; Config b; b[ConfigManager::ID] = "config"; b[ConfigManager::MERGE]["b"] = 1; Config c; c[ConfigManager::ID] = "config"; c[ConfigManager::MERGE]["c"] = 2; assert(cm.addConfig(a)); assertNoExceptionSet(); assert(cm.addConfig(b)); assertNoExceptionSet(); assert(cm.addConfig(c)); assertNoExceptionSet(); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("bad remove"); { ConfigManager cm; assert(!cm.removeConfig("error")); assertExceptionSet(); Exception::clear(); } tr.passIfNoException(); tr.test("remove"); { DynamicObject expect; expect["a"] = 0; expect["b"] = 1; expect["c"] = 2; ConfigManager cm; Config a; a[ConfigManager::ID] = "config a"; a[ConfigManager::GROUP] = "group"; a[ConfigManager::MERGE]["a"] = 0; Config b; b[ConfigManager::ID] = "config b"; b[ConfigManager::GROUP] = "group"; b[ConfigManager::MERGE]["b"] = 1; Config c; c[ConfigManager::ID] = "config c"; c[ConfigManager::GROUP] = "group"; c[ConfigManager::MERGE]["c"] = 2; assert(cm.addConfig(a)); assertNoExceptionSet(); assert(cm.addConfig(b)); assertNoExceptionSet(); assert(cm.addConfig(c)); assertNoExceptionSet(); assertDynoCmp(cm.getConfig("group"), expect); DynamicObject expect2; expect2["a"] = 0; expect2["c"] = 2; assert(cm.removeConfig("config b")); assertDynoCmp(cm.getConfig("group"), expect2); } tr.passIfNoException(); tr.test("default value"); { ConfigManager cm; Config a; a[ConfigManager::ID] = "config a"; a[ConfigManager::MERGE] = 1; assert(cm.addConfig(a)); assertNoExceptionSet(); Config b; b[ConfigManager::ID] = "config b"; b[ConfigManager::PARENT] = "config a"; b[ConfigManager::MERGE] = ConfigManager::DEFAULT_VALUE; assert(cm.addConfig(b)); assertNoExceptionSet(); DynamicObject expect; expect = 1; assertDynoCmp(cm.getConfig("config b"), expect); } tr.passIfNoException(); tr.test("default values"); { ConfigManager cm; Config cfga; cfga[ConfigManager::ID] = "config a"; Config& a = cfga[ConfigManager::MERGE]; a[0] = 10; a[1] = 11; a[2]["0"] = 120; a[2]["1"] = 121; assert(cm.addConfig(cfga)); assertNoExceptionSet(); Config cfgb; cfgb[ConfigManager::ID] = "config b"; cfgb[ConfigManager::PARENT] = "config a"; Config& b = cfgb[ConfigManager::MERGE]; b[0] = ConfigManager::DEFAULT_VALUE; b[1] = 21; b[2]["0"] = ConfigManager::DEFAULT_VALUE; b[2]["1"] = 221; assert(cm.addConfig(cfgb)); assertNoExceptionSet(); DynamicObject expect; expect[0] = 10; expect[1] = 21; expect[2]["0"] = 120; expect[2]["1"] = 221; assertDynoCmp(cm.getConfig("config b"), expect); } tr.passIfNoException(); tr.test("keyword substitution {RESOURCE_DIR}"); { DynamicObject expect; expect["dir"] = "/the/real/dir"; expect["dir-plus"] = "/the/real/dir/plus/more"; //expect["name"] = "Digital Bazaar, Inc."; ConfigManager cm; Config a; a[ConfigManager::ID] = "config"; a[ConfigManager::MERGE]["dir"] = "{RESOURCE_DIR}"; a[ConfigManager::MERGE]["dir-plus"] = "{RESOURCE_DIR}/plus/more"; // FIXME: only supports "{RESOURCE_DIR}" now //a[ConfigManager::MERGE]["other"] = "{DB}"; cm.setKeyword("RESOURCE_DIR", "/the/real/dir"); //cm.setKeyword("DB", "Digital Bazaar, Inc."); assert(cm.addConfig(a)); assertNoExceptionSet(); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("keyword substitution {CURRENT_DIR}"); { DynamicObject expect; string cwd; string cwdPlusMore; string absoluteDir; File configFile = File::createTempFile("test-config-file"); FileOutputStream fos(configFile); // create and populate the config file string configFileText = "{\n" "\"_id_\": \"config\",\n" "\"_merge_\": {\n" " \"dir\": \"{CURRENT_DIR}\",\n" " \"dir-plus\": \"{CURRENT_DIR}/plus/more\" }\n" "}\n"; fos.write(configFileText.c_str(), configFileText.length()); fos.close(); // modify the current working directory to the expected value absoluteDir = File::dirname(configFile->getAbsolutePath()); cwd = absoluteDir.c_str(); cwdPlusMore = cwd.c_str(); cwdPlusMore.append("/plus/more"); // set the expected values expect["dir"] = cwd.c_str(); expect["dir-plus"] = cwdPlusMore.c_str(); // create the configuration ConfigManager cm; assert(cm.addConfigFile(configFile->getAbsolutePath(), true, absoluteDir.c_str(), true, false)); assertNoExceptionSet(); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); #if 0 tr.test("user preferences"); { ConfigManager cm; // node // built in or loaded defaults DynamicObject nodec; nodec["node"]["host"] = "localhost"; nodec["node"]["port"] = 19100; nodec["node"]["modulePath"]->append("/usr/lib/bitmunk/modules"); nodec["node"]["userModulePath"] = "~/.bitmunk/modules"; assert(cm.addConfig(nodec)); assertNoExceptionSet(); // user // loaded defaults DynamicObject userc; userc["node"]["port"] = 19100; userc["node"]["comment"] = "My precious..."; assert(cm.addConfig(userc, ConfigManager::Custom)); assertNoExceptionSet(); // user makes changes during runtime DynamicObject c = cm.getConfig(); c["node"]["port"] = 19200; c["node"]["userModulePath"] = "~/.bitmunk/modules:~/.bitmunk/modules-dev"; c["node"][ConfigManager::TMP]["not in changes"] = true; // get the changes from defaults to current config // serialize this to disk as needed DynamicObject changes; cm.getChanges(changes); // check it's correct DynamicObject expect; expect["node"]["port"] = 19200; expect["node"]["comment"] = "My precious..."; expect["node"]["userModulePath"] = "~/.bitmunk/modules:~/.bitmunk/modules-dev"; // NOTE: will not have TMP var assertDynoCmp(changes, expect); } tr.passIfNoException(); #endif tr.test("versioning"); { ConfigManager cm; cm.getVersions()->clear(); Config c; c[ConfigManager::ID] = "config"; assert(cm.addConfig(c)); assertNoExceptionSet(); // config has no version - no check done - pass cm.addVersion("1"); assert(cm.addConfig(c)); assertNoExceptionSet(); // config has known version - pass c[ConfigManager::VERSION] = "1"; assert(cm.addConfig(c)); assertNoExceptionSet(); assert(cm.removeConfig("config")); // config has unknown version - fail c[ConfigManager::VERSION] = "2"; assert(!cm.addConfig(c)); assertExceptionSet(); Exception::clear(); } tr.passIfNoException(); tr.test("empty array & map"); { ConfigManager cm; DynamicObject a; a[ConfigManager::ID] = "config"; a[ConfigManager::MERGE][0]->setType(Array); a[ConfigManager::MERGE][1]->setType(Map); assert(cm.addConfig(a)); assertNoExceptionSet(); DynamicObject expect; expect[0]->setType(Array); expect[1]->setType(Map); assertDynoCmp(cm.getConfig("config"), expect); } tr.passIfNoException(); tr.test("empty group ids"); { ConfigManager cm; DynamicObject expect; expect->setType(Array); assertDynoCmp(cm.getIdsInGroup("Not-A-Group"), expect); } tr.passIfNoException(); tr.test("group ids"); { ConfigManager cm; DynamicObject c; c[ConfigManager::ID] = "c0"; c[ConfigManager::GROUP] = "c"; assert(cm.addConfig(c)); assertNoExceptionSet(); c[ConfigManager::ID] = "c1"; c[ConfigManager::GROUP] = "c"; assert(cm.addConfig(c)); assertNoExceptionSet(); DynamicObject expect; expect->setType(Array); expect[0] = "c0"; expect[1] = "c1"; assertDynoCmp(cm.getIdsInGroup("c"), expect); } tr.passIfNoException(); tr.test("replace keywords"); { ConfigManager cm; DynamicObject c; c[ConfigManager::ID] = "c"; c[ConfigManager::MERGE]["test"] = "{A}"; DynamicObject vars; vars["A"] = "a"; assertNoException(ConfigManager::replaceKeywords(c, vars)); DynamicObject expect; expect[ConfigManager::ID] = "c"; expect[ConfigManager::MERGE]["test"] = "a"; assertDynoCmp(c, expect); } tr.passIfNoException(); tr.test("replace keywords (invalid keyword)"); { ConfigManager cm; DynamicObject c; c[ConfigManager::ID] = "c"; c[ConfigManager::MERGE]["test"] = "{UNKNOWN}"; DynamicObject vars; vars["A"] = "a"; assertException(ConfigManager::replaceKeywords(c, vars)); } tr.passIfException(); tr.ungroup(); }