summaryrefslogtreecommitdiffstats
path: root/config/src/tests/subscriber
diff options
context:
space:
mode:
Diffstat (limited to 'config/src/tests/subscriber')
-rw-r--r--config/src/tests/subscriber/.gitignore7
-rw-r--r--config/src/tests/subscriber/CMakeLists.txt11
-rw-r--r--config/src/tests/subscriber/subscriber.cpp525
3 files changed, 543 insertions, 0 deletions
diff --git a/config/src/tests/subscriber/.gitignore b/config/src/tests/subscriber/.gitignore
new file mode 100644
index 00000000000..1b93a34021a
--- /dev/null
+++ b/config/src/tests/subscriber/.gitignore
@@ -0,0 +1,7 @@
+/config-bar.cpp
+/config-bar.h
+/config-foo.cpp
+/config-foo.h
+/config-baz.cpp
+/config-baz.h
+config_subscriber_test_app
diff --git a/config/src/tests/subscriber/CMakeLists.txt b/config/src/tests/subscriber/CMakeLists.txt
new file mode 100644
index 00000000000..1b1673a2512
--- /dev/null
+++ b/config/src/tests/subscriber/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(config_subscriber_test_app
+ SOURCES
+ subscriber.cpp
+ DEPENDS
+ config_cloudconfig
+)
+vespa_add_test(NAME config_subscriber_test_app COMMAND config_subscriber_test_app)
+vespa_generate_config(config_subscriber_test_app ../../test/resources/configdefinitions/foo.def)
+vespa_generate_config(config_subscriber_test_app ../../test/resources/configdefinitions/bar.def)
+vespa_generate_config(config_subscriber_test_app ../../test/resources/configdefinitions/baz.def)
diff --git a/config/src/tests/subscriber/subscriber.cpp b/config/src/tests/subscriber/subscriber.cpp
new file mode 100644
index 00000000000..0d5d2698b42
--- /dev/null
+++ b/config/src/tests/subscriber/subscriber.cpp
@@ -0,0 +1,525 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/config/config.h>
+#include <vespa/config/common/misc.h>
+#include <vespa/config/common/configholder.h>
+#include <vespa/config/subscription/configsubscription.h>
+#include <fstream>
+#include "config-foo.h"
+#include "config-bar.h"
+#include "config-baz.h"
+
+using namespace config;
+using namespace vespalib;
+
+namespace {
+
+ ConfigValue createValue(const std::string & value)
+ {
+ std::vector< vespalib::string > lines;
+ lines.push_back(value);
+ return ConfigValue(lines, calculateContentMd5(lines));
+ }
+
+ ConfigValue createFooValue(const std::string & value)
+ {
+ return createValue("fooValue \"" + value + "\"");
+ }
+
+ ConfigValue createBarValue(const std::string & value)
+ {
+ return createValue("barValue \"" + value + "\"");
+ }
+
+ ConfigValue createBazValue(const std::string & value)
+ {
+ return createValue("bazValue \"" + value + "\"");
+ }
+
+ void verifyConfig(const std::string & expected, std::unique_ptr<FooConfig> cfg)
+ {
+ ASSERT_TRUE(cfg.get() != NULL);
+ ASSERT_EQUAL(expected, cfg->fooValue);
+ }
+
+ void verifyConfig(const std::string & expected, std::unique_ptr<BarConfig> cfg)
+ {
+ ASSERT_TRUE(cfg.get() != NULL);
+ ASSERT_EQUAL(expected, cfg->barValue);
+ }
+
+ void verifyConfig(const std::string & expected, std::unique_ptr<BazConfig> cfg)
+ {
+ ASSERT_TRUE(cfg.get() != NULL);
+ ASSERT_EQUAL(expected, cfg->bazValue);
+ }
+
+ class MySource : public Source
+ {
+ void getConfig() { }
+ void close() { }
+ void reload(int64_t gen) { (void) gen; }
+ };
+
+ class MyManager : public IConfigManager
+ {
+ public:
+
+ void unsubscribeAll() { }
+ size_t numSubscribers() const { return 0; }
+
+
+ SubscriptionId idCounter;
+ std::vector<IConfigHolder::SP> _holders;
+ int numCancel;
+
+
+ MyManager() : idCounter(0), numCancel(0) { }
+
+ ConfigSubscription::SP subscribe(const ConfigKey & key, uint64_t timeoutInMillis) {
+ (void) timeoutInMillis;
+ IConfigHolder::SP holder(new ConfigHolder());
+ _holders.push_back(holder);
+
+ ConfigSubscription::SP s(new ConfigSubscription(0, key, holder, Source::UP(new MySource())));
+ return s;
+ }
+ void unsubscribe(const ConfigSubscription::SP & subscription) {
+ (void) subscription;
+ numCancel++;
+ }
+
+ void updateValue(size_t index, const ConfigValue & value, int64_t generation) {
+ ASSERT_TRUE(index < _holders.size());
+ _holders[index]->handle(ConfigUpdate::UP(new ConfigUpdate(value, true, generation)));
+ }
+
+ void updateGeneration(size_t index, int64_t generation) {
+ ASSERT_TRUE(index < _holders.size());
+ ConfigValue value;
+ // Give previous value just as API.
+ if (_holders[index]->poll()) {
+ value = _holders[index]->provide()->getValue();
+ }
+ _holders[index]->handle(ConfigUpdate::UP(new ConfigUpdate(value, false, generation)));
+ }
+
+ void reload(int64_t generation)
+ {
+ (void) generation;
+ }
+
+ };
+
+ class APIFixture : public IConfigContext
+ {
+ public:
+ MyManager & _m;
+ APIFixture(MyManager & m)
+ : _m(m)
+ {
+ }
+
+ APIFixture(const APIFixture & rhs)
+ : IConfigContext(rhs),
+ _m(rhs._m)
+ { }
+
+ IConfigManager & getManagerInstance() {
+ return _m;
+ }
+
+ IConfigManager & getManagerInstance(const SourceSpec & spec) {
+ (void) spec;
+ return getManagerInstance();
+ }
+
+ void reload() { }
+ };
+
+ struct StandardFixture {
+ MyManager & f1;
+ ConfigSubscriber s;
+ ConfigHandle<FooConfig>::UP h1;
+ ConfigHandle<BarConfig>::UP h2;
+
+ StandardFixture(MyManager & F1, APIFixture & F2) : f1(F1), s(IConfigContext::SP(new APIFixture(F2)))
+ {
+ h1 = s.subscribe<FooConfig>("myid");
+ h2 = s.subscribe<BarConfig>("myid");
+ f1.updateValue(0, createFooValue("foo"), 1);
+ f1.updateValue(1, createBarValue("bar"), 1);
+ ASSERT_TRUE(s.nextConfig(0));
+ verifyConfig("foo", h1->getConfig());
+ verifyConfig("bar", h2->getConfig());
+ }
+ };
+
+ struct SimpleFixture {
+ ConfigSet set;
+ FooConfigBuilder fooBuilder;
+ BarConfigBuilder barBuilder;
+ SimpleFixture() {
+ fooBuilder.fooValue = "bar";
+ barBuilder.barValue = "foo";
+ set.addBuilder("myid", &fooBuilder);
+ set.addBuilder("myid", &barBuilder);
+ }
+ };
+}
+
+TEST_F("requireThatSubscriberCanGetMultipleTypes", SimpleFixture()) {
+ ConfigSubscriber s(f.set);
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid");
+ ASSERT_TRUE(s.nextConfig(0));
+ std::unique_ptr<FooConfig> foo = h1->getConfig();
+ std::unique_ptr<BarConfig> bar = h2->getConfig();
+ ASSERT_EQUAL("bar", foo->fooValue);
+ ASSERT_EQUAL("foo", bar->barValue);
+}
+
+TEST_F("requireThatNextConfigMustBeCalled", SimpleFixture()) {
+ ConfigSubscriber s(f.set);
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ bool thrown = false;
+ try {
+ std::unique_ptr<FooConfig> foo = h1->getConfig();
+ } catch (const ConfigRuntimeException & e) {
+ thrown = true;
+ }
+ ASSERT_TRUE(thrown);
+}
+
+TEST_F("requireThatSubscriptionsCannotBeAddedWhenFrozen", SimpleFixture()) {
+ ConfigSubscriber s(f.set);
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ ASSERT_TRUE(s.nextConfig(0));
+ bool thrown = false;
+ try {
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid");
+ } catch (const ConfigRuntimeException & e) {
+ thrown = true;
+ }
+ ASSERT_TRUE(thrown);
+}
+
+TEST_FF("requireThatNextConfigReturnsFalseUntilSubscriptionHasSucceeded", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid");
+ ASSERT_FALSE(s.nextConfig(0));
+ ASSERT_FALSE(s.nextConfig(100));
+ f1.updateValue(0, createFooValue("foo"), 1);
+ ASSERT_FALSE(s.nextConfig(100));
+ f1.updateValue(1, createBarValue("bar"), 1);
+ ASSERT_TRUE(s.nextConfig(100));
+}
+
+TEST_FFF("requireThatNewGenerationIsFetchedOnReload", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+
+ ASSERT_FALSE(f3.s.nextConfig(1000));
+
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+
+ f1.updateValue(0, createFooValue("foo2"), 3);
+ f1.updateValue(1, createBarValue("bar2"), 3);
+
+ ASSERT_TRUE(f3.s.nextConfig(1000));
+
+ verifyConfig("foo2", f3.h1->getConfig());
+ verifyConfig("bar2", f3.h2->getConfig());
+}
+
+TEST_FFF("requireThatAllConfigsMustGetTimestampUpdate", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ f1.updateValue(0, createFooValue("foo2"), 2);
+ ASSERT_FALSE(f3.s.nextConfig(100));
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+
+ f1.updateValue(0, createFooValue("foo2"), 3);
+ f1.updateGeneration(1, 3);
+
+ ASSERT_TRUE(f3.s.nextConfig(0));
+ verifyConfig("foo2", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+}
+
+TEST_FFF("requireThatNextConfigMaySucceedIfInTheMiddleOfConfigUpdate", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ f1.updateValue(0, createFooValue("foo2"), 2);
+ ASSERT_FALSE(f3.s.nextConfig(1000));
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+
+ f1.updateGeneration(1, 2);
+ ASSERT_TRUE(f3.s.nextConfig(0));
+ verifyConfig("foo2", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+}
+
+TEST_FFF("requireThatCorrectConfigIsReturnedAfterTimestampUpdate", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ f1.updateGeneration(0, 2);
+ f1.updateGeneration(1, 2);
+ ASSERT_FALSE(f3.s.nextConfig(1000));
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+ ASSERT_TRUE(f3.s.nextGeneration(0));
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+}
+
+TEST_MT_FFF("requireThatConfigIsReturnedWhenUpdatedDuringNextConfig", 2, MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ if (thread_id == 0) {
+ FastOS_Time timer;
+ timer.SetNow();
+ ASSERT_TRUE(f3.s.nextConfig(10000));
+ ASSERT_TRUE(timer.MilliSecsToNow() > 250);
+ ASSERT_TRUE(timer.MilliSecsToNow() <= 5000);
+ verifyConfig("foo2", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+ } else {
+ FastOS_Thread::Sleep(300);
+ f1.updateValue(0, createFooValue("foo2"), 2);
+ FastOS_Thread::Sleep(300);
+ f1.updateGeneration(1, 2);
+ }
+}
+
+TEST_FFF("requireThatConfigIsReturnedWhenUpdatedBeforeNextConfig", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ FastOS_Time timer;
+ timer.SetNow();
+ ASSERT_FALSE(f3.s.nextConfig(1000));
+ ASSERT_TRUE(timer.MilliSecsToNow() > 850);
+ f1.updateGeneration(0, 2);
+ f1.updateGeneration(1, 2);
+ timer.SetNow();
+ ASSERT_TRUE(f3.s.nextGeneration(10000));
+ ASSERT_TRUE(timer.MilliSecsToNow() <= 5000);
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+}
+
+TEST_FFF("requireThatSubscriptionsAreUnsubscribedOnClose", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ ASSERT_FALSE(f3.s.isClosed());
+ f3.s.close();
+ ASSERT_TRUE(f3.s.isClosed());
+ ASSERT_EQUAL(2, f1.numCancel);
+}
+
+TEST_FFF("requireThatNothingCanBeCalledAfterClose", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ ASSERT_FALSE(f3.s.isClosed());
+ f3.s.close();
+ ASSERT_TRUE(f3.s.isClosed());
+ ASSERT_FALSE(f3.s.nextConfig(100));
+ bool thrown = false;
+ try {
+ f3.h1->getConfig();
+ } catch (const ConfigRuntimeException & e) {
+ thrown = true;
+ }
+ ASSERT_TRUE(thrown);
+}
+
+TEST_MT_FFF("requireThatNextConfigIsInterruptedOnClose", 2, MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ if (thread_id == 0) {
+ FastOS_Time timer;
+ timer.SetNow();
+ ASSERT_FALSE(f3.s.nextConfig(5000));
+ ASSERT_TRUE(timer.MilliSecsToNow() >= 500.0);
+ ASSERT_TRUE(timer.MilliSecsToNow() < 60000.0);
+ } else {
+ FastOS_Thread::Sleep(1000);
+ f3.s.close();
+ }
+}
+
+TEST_FF("requireThatHandlesAreMarkedAsChanged", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid2");
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid2");
+ ASSERT_FALSE(s.nextConfig(0));
+
+ f1.updateValue(0, createFooValue("foo"), 1);
+ f1.updateValue(1, createFooValue("bar"), 1);
+ ASSERT_TRUE(s.nextConfig(100));
+ ASSERT_TRUE(h1->isChanged());
+ ASSERT_TRUE(h2->isChanged());
+
+ ASSERT_FALSE(s.nextConfig(100));
+ ASSERT_FALSE(h1->isChanged());
+ ASSERT_FALSE(h2->isChanged());
+ f1.updateValue(0, createFooValue("bar"), 2);
+ f1.updateGeneration(1, 2);
+ ASSERT_TRUE(s.nextConfig(100));
+ ASSERT_TRUE(h1->isChanged());
+ ASSERT_FALSE(h2->isChanged());
+}
+
+TEST_FF("requireThatNextGenerationMarksChanged", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid2");
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid2");
+ f1.updateValue(0, createFooValue("foo"), 1);
+ f1.updateValue(1, createFooValue("bar"), 1);
+ ASSERT_TRUE(s.nextGeneration(0));
+ ASSERT_TRUE(h1->isChanged());
+ ASSERT_TRUE(h2->isChanged());
+
+ f1.updateValue(0, createFooValue("bar"), 2);
+ f1.updateGeneration(1, 2);
+ ASSERT_TRUE(s.nextGeneration(0));
+ ASSERT_TRUE(h1->isChanged());
+ ASSERT_FALSE(h2->isChanged());
+
+ f1.updateGeneration(0, 3);
+ f1.updateGeneration(1, 3);
+ ASSERT_TRUE(s.nextGeneration(0));
+ ASSERT_FALSE(h1->isChanged());
+ ASSERT_FALSE(h2->isChanged());
+}
+
+TEST_FF("requireThatgetGenerationIsSet", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid2");
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid2");
+ f1.updateValue(0, createFooValue("foo"), 1);
+ f1.updateValue(1, createFooValue("bar"), 1);
+ ASSERT_TRUE(s.nextGeneration(0));
+ ASSERT_EQUAL(1, s.getGeneration());
+ ASSERT_TRUE(h1->isChanged());
+ ASSERT_TRUE(h2->isChanged());
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(1, 2);
+ ASSERT_FALSE(s.nextGeneration(0));
+ ASSERT_EQUAL(1, s.getGeneration());
+ f1.updateGeneration(0, 2);
+ ASSERT_TRUE(s.nextGeneration(0));
+ ASSERT_EQUAL(2, s.getGeneration());
+}
+
+TEST_FFF("requireThatConfigHandleStillHasConfigOnTimestampUpdate", MyManager, APIFixture(f1), StandardFixture(f1, f2)) {
+ f1.updateGeneration(0, 2);
+ f1.updateGeneration(1, 2);
+ ASSERT_TRUE(f3.s.nextGeneration(0));
+ verifyConfig("foo", f3.h1->getConfig());
+ verifyConfig("bar", f3.h2->getConfig());
+}
+
+TEST_FF("requireThatTimeStamp0Works", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid");
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ ConfigHandle<BazConfig>::UP h3 = s.subscribe<BazConfig>("myid");
+ f1.updateValue(0, createBarValue("bar"), 0);
+ f1.updateValue(1, createFooValue("foo"), 0);
+ f1.updateValue(2, createBazValue("baz"), 0);
+ ASSERT_TRUE(s.nextConfig(0));
+ verifyConfig("bar", h2->getConfig());
+ verifyConfig("foo", h1->getConfig());
+ verifyConfig("baz", h3->getConfig());
+}
+
+TEST_FF("requireThatNextGenerationWorksWithManyConfigs", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<BarConfig>::UP h2 = s.subscribe<BarConfig>("myid");
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ ConfigHandle<BazConfig>::UP h3 = s.subscribe<BazConfig>("myid");
+ f1.updateValue(0, createBarValue("bar"), 1);
+ f1.updateValue(1, createFooValue("foo"), 1);
+ f1.updateValue(2, createBazValue("baz"), 1);
+ ASSERT_TRUE(s.nextGeneration(100));
+ verifyConfig("bar", h2->getConfig());
+ verifyConfig("foo", h1->getConfig());
+ verifyConfig("baz", h3->getConfig());
+ int generation = 2;
+
+ f1.updateGeneration(0, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(1, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(2, generation);
+ ASSERT_TRUE(s.nextGeneration(100));
+
+ generation++;
+ f1.updateGeneration(0, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(2, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(1, generation);
+ ASSERT_TRUE(s.nextGeneration(100));
+
+ generation++;
+ f1.updateGeneration(1, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(0, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(2, generation);
+ ASSERT_TRUE(s.nextGeneration(100));
+
+ generation++;
+ f1.updateGeneration(1, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(2, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(0, generation);
+ ASSERT_TRUE(s.nextGeneration(100));
+
+ generation++;
+ f1.updateGeneration(2, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(0, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(1, generation);
+ ASSERT_TRUE(s.nextGeneration(100));
+
+ generation++;
+ f1.updateGeneration(2, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(1, generation);
+ ASSERT_FALSE(s.nextGeneration(0));
+ f1.updateGeneration(0, generation);
+ ASSERT_TRUE(s.nextGeneration(100));
+}
+
+TEST_FF("requireThatConfigSubscriberHandlesProxyCache", MyManager, APIFixture(f1)) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ f1.updateValue(0, createFooValue("foo"), 1);
+ f1.updateGeneration(0, 2);
+ ASSERT_TRUE(s.nextConfig(0));
+ ASSERT_EQUAL(2, s.getGeneration());
+ ASSERT_TRUE(h1->isChanged());
+ verifyConfig("foo", h1->getConfig());
+
+ f1.updateGeneration(0, 3);
+ ASSERT_TRUE(s.nextGeneration(0));
+ ASSERT_EQUAL(3, s.getGeneration());
+ ASSERT_FALSE(h1->isChanged());
+ verifyConfig("foo", h1->getConfig());
+}
+
+TEST_MT_FF("requireThatConfigSubscriberWaitsUntilNextConfigSucceeds", 2, MyManager, APIFixture(f1)) {
+ if (thread_id == 0) {
+ ConfigSubscriber s(IConfigContext::SP(new APIFixture(f2)));
+ ConfigHandle<FooConfig>::UP h1 = s.subscribe<FooConfig>("myid");
+ f1.updateValue(0, createFooValue("foo"), 1);
+ ASSERT_TRUE(s.nextConfig(0));
+ f1.updateGeneration(0, 2);
+ ASSERT_FALSE(s.nextConfig(1000));
+ TEST_BARRIER();
+ ASSERT_TRUE(s.nextConfig(2000));
+ verifyConfig("foo2", h1->getConfig()); // First update is skipped
+ } else {
+ TEST_BARRIER();
+ FastOS_Thread::Sleep(1000);
+ f1.updateValue(0, createFooValue("foo2"), 3);
+ }
+}
+
+TEST_MAIN() {
+ TEST_RUN_ALL();
+}