Merge pull request #28 from cloudant/keep_features_on_config_restart

Keep features on config process restart.
diff --git a/src/config.erl b/src/config.erl
index 7970a10..50d2a5d 100644
--- a/src/config.erl
+++ b/src/config.erl
@@ -204,16 +204,15 @@
 
 
 features() ->
-    lists:usort([list_to_atom(Key) || {Key, "true"} <- ?MODULE:get(?FEATURES)]).
-
+    application:get_env(config, enabled_features, []).
 
 enable_feature(Feature) when is_atom(Feature) ->
-    ?MODULE:set(?FEATURES, atom_to_list(Feature), "true", false).
-
+    application:set_env(config, enabled_features,
+        lists:usort([Feature | features()]), [{persistent, true}]).
 
 disable_feature(Feature) when is_atom(Feature) ->
-    ?MODULE:delete(?FEATURES, atom_to_list(Feature), false).
-
+    application:set_env(config, enabled_features,
+        features() -- [Feature], [{persistent, true}]).
 
 listen_for_changes(CallbackModule, InitialState) ->
     config_listener_mon:subscribe(CallbackModule, InitialState).
diff --git a/test/config_tests.erl b/test/config_tests.erl
index 6282822..ac3c77e 100644
--- a/test/config_tests.erl
+++ b/test/config_tests.erl
@@ -25,6 +25,7 @@
 
 
 -define(TIMEOUT, 4000).
+-define(RESTART_TIMEOUT_IN_MILLISEC, 3000).
 
 -define(CONFIG_FIXTURESDIR,
         filename:join([?BUILDDIR(), "src", "config", "test", "fixtures"])).
@@ -183,8 +184,9 @@
             fun setup/0,
             fun teardown/1,
             [
-                fun should_enable_features/0,
-                fun should_disable_features/0
+                {"enable", fun should_enable_features/0},
+                {"disable", fun should_disable_features/0},
+                {"restart config", fun should_keep_features_on_config_restart/0}
             ]
         }
     }.
@@ -670,6 +672,7 @@
 
 
 should_enable_features() ->
+    [config:disable_feature(F) || F <- config:features()],
     ?assertEqual([], config:features()),
 
     ?assertEqual(ok, config:enable_feature(snek)),
@@ -683,6 +686,7 @@
 
 
 should_disable_features() ->
+    [config:disable_feature(F) || F <- config:features()],
     ?assertEqual([], config:features()),
 
     config:enable_feature(snek),
@@ -694,6 +698,14 @@
     ?assertEqual(ok, config:disable_feature(snek)),
     ?assertEqual([], config:features()).
 
+should_keep_features_on_config_restart() ->
+    [config:disable_feature(F) || F <- config:features()],
+    ?assertEqual([], config:features()),
+
+    config:enable_feature(snek),
+    ?assertEqual([snek], config:features()),
+    with_process_restart(config),
+    ?assertEqual([snek], config:features()).
 
 spawn_config_listener() ->
     Self = self(),
@@ -777,3 +789,44 @@
 
 to_string(Term) ->
     lists:flatten(io_lib:format("~p", [Term])).
+
+with_process_restart(Name) ->
+    ok = stop_sync(whereis(Name), ?TIMEOUT),
+    Now = now_us(),
+    wait_process_restart(
+        Name, ?RESTART_TIMEOUT_IN_MILLISEC * 1000, 50, Now, Now).
+
+wait_process_restart(_Name, Timeout, _Delay, Started, Prev)
+        when Prev - Started > Timeout ->
+    timeout;
+wait_process_restart(Name, Timeout, Delay, Started, _Prev) ->
+    case whereis(Name) of
+        undefined ->
+            ok = timer:sleep(Delay),
+            wait_process_restart(Name, Timeout, Delay, Started, now_us());
+        Pid ->
+            Pid
+    end.
+
+stop_sync(Pid, Timeout) when is_pid(Pid) ->
+    MRef = erlang:monitor(process, Pid),
+    try
+        begin
+            catch unlink(Pid),
+            exit(Pid, kill),
+            receive
+            {'DOWN', MRef, _, _, _} ->
+                ok
+            after Timeout ->
+                timeout
+            end
+        end
+    after
+        erlang:demonitor(MRef, [flush])
+    end;
+stop_sync(_, _) -> error(badarg).
+
+
+now_us() ->
+    {MegaSecs, Secs, MicroSecs} = os:timestamp(),
+    (MegaSecs * 1000000 + Secs) * 1000000 + MicroSecs.