• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <AtomicState.h>
18 
19 #include <chrono>
20 #include <thread>
21 
22 #include <gtest/gtest.h>
23 
24 using namespace std::chrono_literals;
25 
26 namespace android {
27 
28 enum AtomicStateTestEnum {
29   A,
30   B,
31   C,
32   D,
33   E,
34 };
35 
36 class AtomicStateTest : public testing::Test {
37  protected:
AtomicStateTest()38   AtomicStateTest() : state_(A) {}
SetUp()39   virtual void SetUp() {}
TearDown()40   virtual void TearDown() {}
41 
42   AtomicState<AtomicStateTestEnum> state_;
43 };
44 
TEST_F(AtomicStateTest,transition)45 TEST_F(AtomicStateTest, transition) {
46   ASSERT_EQ(A, state_.state_);
47 
48   // Starts as A, transition from B fails
49   ASSERT_FALSE(state_.transition(B, C));
50   ASSERT_EQ(A, state_.state_);
51 
52   // transition from A to B
53   ASSERT_TRUE(state_.transition(A, B));
54   ASSERT_EQ(B, state_.state_);
55 
56   // State is B, transition from A fails
57   ASSERT_FALSE(state_.transition(A, B));
58   ASSERT_EQ(B, state_.state_);
59 
60   // State is B, transition_or from A calls the lambda
61   bool lambda = false;
62   bool already_locked = false;
63   state_.transition_or(A, B, [&] {
64     // The lock should be held in the lambda
65     if (state_.m_.try_lock()) {
66       state_.m_.unlock();
67     } else {
68       already_locked = true;
69     }
70     lambda = true;
71     return B;
72   });
73   ASSERT_TRUE(lambda);
74   ASSERT_TRUE(already_locked);
75   ASSERT_EQ(B, state_.state_);
76 
77   // State is C, transition_or from B to C does not call the lambda
78   lambda = false;
79   state_.transition_or(B, C, [&] {
80     lambda = true;
81     return C;
82   });
83   ASSERT_FALSE(lambda);
84   ASSERT_EQ(C, state_.state_);
85 }
86 
TEST_F(AtomicStateTest,wait)87 TEST_F(AtomicStateTest, wait) {
88   ASSERT_EQ(A, state_.state_);
89 
90   // Starts as A, wait_for_either_of B, C returns false
91   ASSERT_FALSE(state_.wait_for_either_of(B, C, 10ms));
92 
93   // Starts as A, wait_for_either_of A, B returns true
94   ASSERT_TRUE(state_.wait_for_either_of(A, B, 1s));
95 
96   {
97     std::thread t([&] {
98       usleep(10000);
99       state_.set(B);
100     });
101 
102     // Wait ing for B or C returns true after state is set to B
103     ASSERT_TRUE(state_.wait_for_either_of(B, C, 1s));
104 
105     t.join();
106   }
107 
108   ASSERT_EQ(B, state_.state_);
109   {
110     std::thread t([&] {
111       usleep(10000);
112       state_.transition(B, C);
113     });
114 
115     // Waiting for A or C returns true after state is transitioned to C
116     ASSERT_TRUE(state_.wait_for_either_of(A, C, 1s));
117 
118     t.join();
119   }
120 
121   ASSERT_EQ(C, state_.state_);
122   {
123     std::thread t([&] {
124       usleep(10000);
125       state_.transition(C, D);
126     });
127 
128     // Waiting for A or B returns false after state is transitioned to D
129     ASSERT_FALSE(state_.wait_for_either_of(A, B, 100ms));
130 
131     t.join();
132   }
133 }
134 
135 }  // namespace android
136