(FSM) updateState with unprotected action
[folly.git] / folly / wangle / test / FSM.cpp
index 85fc5d0540bbc60b04a9c2530714d036d87ea67e..1ce78c2ee7779279c0fd8c957378f2431ac811ea 100644 (file)
@@ -21,44 +21,45 @@ using namespace folly::wangle::detail;
 
 enum class State { A, B };
 
-TEST(FSM, ctor) {
+TEST(FSM, example) {
   FSM<State> fsm(State::A);
-  EXPECT_EQ(State::A, fsm.getState());
-}
+  int count = 0;
+  int unprotectedCount = 0;
+
+  // somebody set up us the switch
+  auto tryTransition = [&]{
+    switch (fsm.getState()) {
+    case State::A:
+      return fsm.updateState(State::A, State::B, [&]{ count++; });
+    case State::B:
+      return fsm.updateState(State::B, State::A,
+                             [&]{ count--; }, [&]{ unprotectedCount--; });
+    }
+    return false; // unreachable
+  };
 
-TEST(FSM, update) {
-  FSM<State> fsm(State::A);
-  EXPECT_TRUE(fsm.updateState(State::A, State::B, []{}));
+  // keep retrying until success (like a cas)
+  while (!tryTransition()) ;
   EXPECT_EQ(State::B, fsm.getState());
-}
-
-TEST(FSM, badUpdate) {
-  FSM<State> fsm(State::A);
-  EXPECT_FALSE(fsm.updateState(State::B, State::A, []{}));
-}
-
-TEST(FSM, actionOnUpdate) {
-  FSM<State> fsm(State::A);
-  size_t count = 0;
-  fsm.updateState(State::A, State::B, [&]{ count++; });
   EXPECT_EQ(1, count);
-}
+  EXPECT_EQ(0, unprotectedCount);
 
-TEST(FSM, noActionOnBadUpdate) {
-  FSM<State> fsm(State::A);
-  size_t count = 0;
-  fsm.updateState(State::B, State::A, [&]{ count++; });
+  while (!tryTransition()) ;
+  EXPECT_EQ(State::A, fsm.getState());
   EXPECT_EQ(0, count);
+  EXPECT_EQ(-1, unprotectedCount);
 }
 
-TEST(FSM, magicMacros) {
+TEST(FSM, magicMacrosExample) {
   struct MyFSM : public FSM<State> {
-    size_t count = 0;
+    int count = 0;
+    int unprotectedCount = 0;
     MyFSM() : FSM<State>(State::A) {}
     void twiddle() {
       FSM_START
-        FSM_UPDATE(State::A, State::B, [&]{ count++; });
-        FSM_UPDATE(State::B, State::A, [&]{ count--; });
+        FSM_CASE(State::A, State::B, [&]{ count++; });
+        FSM_CASE2(State::B, State::A,
+                  [&]{ count--; }, [&]{ unprotectedCount--; });
       FSM_END
     }
   };
@@ -68,34 +69,47 @@ TEST(FSM, magicMacros) {
   fsm.twiddle();
   EXPECT_EQ(State::B, fsm.getState());
   EXPECT_EQ(1, fsm.count);
+  EXPECT_EQ(0, fsm.unprotectedCount);
 
   fsm.twiddle();
   EXPECT_EQ(State::A, fsm.getState());
   EXPECT_EQ(0, fsm.count);
+  EXPECT_EQ(-1, fsm.unprotectedCount);
 }
 
-TEST(FSM, magicMacros2) {
-  struct MyFSM : public FSM<State> {
-    size_t count = 0;
-    size_t count2 = 0;
-    MyFSM() : FSM<State>(State::A) {}
-    void twiddle() {
-      FSM_START
-        FSM_UPDATE2(State::A, State::B, [&]{ count++; }, count2++);
-        FSM_UPDATE2(State::B, State::A, [&]{ count--; }, count2--);
-      FSM_END
-    }
-  };
 
-  MyFSM fsm;
+TEST(FSM, ctor) {
+  FSM<State> fsm(State::A);
+  EXPECT_EQ(State::A, fsm.getState());
+}
 
-  fsm.twiddle();
+TEST(FSM, update) {
+  FSM<State> fsm(State::A);
+  EXPECT_TRUE(fsm.updateState(State::A, State::B, []{}));
   EXPECT_EQ(State::B, fsm.getState());
-  EXPECT_EQ(1, fsm.count);
-  EXPECT_EQ(1, fsm.count2);
+}
 
-  fsm.twiddle();
-  EXPECT_EQ(State::A, fsm.getState());
-  EXPECT_EQ(0, fsm.count);
-  EXPECT_EQ(0, fsm.count2);
+TEST(FSM, badUpdate) {
+  FSM<State> fsm(State::A);
+  EXPECT_FALSE(fsm.updateState(State::B, State::A, []{}));
+}
+
+TEST(FSM, actionOnUpdate) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  fsm.updateState(State::A, State::B, [&]{ count++; });
+  EXPECT_EQ(1, count);
+}
+
+TEST(FSM, noActionOnBadUpdate) {
+  FSM<State> fsm(State::A);
+  int count = 0;
+  fsm.updateState(State::B, State::A, [&]{ count++; });
+  EXPECT_EQ(0, count);
+}
+
+TEST(FSM, stateTransitionBeforeAction) {
+  FSM<State> fsm(State::A);
+  fsm.updateState(State::A, State::B,
+                  [&]{ EXPECT_EQ(State::B, fsm.getState()); });
 }