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