1/*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <chrono>
17
18#include "finite_state_machine.h"
19
20#include "mock_thread_handler.h"
21
22namespace OHOS {
23namespace UserIam {
24namespace FaceAuth {
25using namespace testing;
26using namespace testing::ext;
27
28enum State : uint32_t {
29    STATE_INIT,
30    STATE_VERIFY_STARING,
31    STATE_COLLECT_STARING,
32    STATE_AUTH_PROCESSING,
33    STATE_VERIFY_STOPPING,
34    STATE_COLLECT_STOPPING,
35    STATE_END
36};
37
38enum Event : uint32_t {
39    EVENT_START_AUTH,
40    EVENT_VERIFY_STARTED,
41    EVENT_COLLECT_STARTED,
42    EVENT_AUTH_RESULT_GET,
43    EVENT_VERIFY_STOPPED,
44    EVENT_COLLECT_STOP,
45    EVENT_USER_CANCEL,
46    EVENT_TIME_OUT,
47};
48
49class StateMachineTest : public testing::Test {
50public:
51    static void SetUpTestCase();
52
53    static void TearDownTestCase();
54
55    void SetUp() override;
56
57    void TearDown() override;
58};
59
60void StateMachineTest::SetUpTestCase()
61{
62}
63
64void StateMachineTest::TearDownTestCase()
65{
66}
67
68void StateMachineTest::SetUp()
69{
70    ThreadHandler::GetSingleThreadInstance()->EnsureTask(nullptr);
71}
72
73void StateMachineTest::TearDown()
74{
75    ThreadHandler::GetSingleThreadInstance()->EnsureTask(nullptr);
76}
77
78HWTEST_F(StateMachineTest, MachineCreateSelfReturn, TestSize.Level0)
79{
80    auto handler = ThreadHandler::GetSingleThreadInstance();
81    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine0", STATE_INIT);
82    EXPECT_NE(machineBuilder, nullptr);
83
84    auto ret1 = machineBuilder->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING);
85    EXPECT_NE(ret1, nullptr);
86
87    auto ret2 = ret1->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING);
88    EXPECT_EQ(ret2, ret1);
89}
90
91HWTEST_F(StateMachineTest, MachineCreateOnlyBuildOnce, TestSize.Level0)
92{
93    auto handler = ThreadHandler::GetSingleThreadInstance();
94    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine1", STATE_INIT);
95    EXPECT_NE(machineBuilder, nullptr);
96
97    machineBuilder->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING)
98        ->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING);
99
100    auto first = machineBuilder->Build();
101    EXPECT_NE(first, nullptr);
102
103    auto second = machineBuilder->Build();
104    EXPECT_EQ(second, nullptr);
105}
106
107HWTEST_F(StateMachineTest, MachineCreateCheckTransition, TestSize.Level0)
108{
109    auto handler = ThreadHandler::GetSingleThreadInstance();
110    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine2", STATE_INIT);
111    EXPECT_NE(machineBuilder, nullptr);
112
113    machineBuilder->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING)
114        ->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_COLLECT_STARING);
115
116    auto machine = machineBuilder->Build();
117    EXPECT_EQ(machine, nullptr);
118}
119
120HWTEST_F(StateMachineTest, MachineCreateInitialState, TestSize.Level0)
121{
122    auto handler = ThreadHandler::GetSingleThreadInstance();
123    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine3", STATE_COLLECT_STOPPING);
124    ASSERT_NE(machineBuilder, nullptr);
125
126    machineBuilder->MakeTransition(STATE_COLLECT_STOPPING, EVENT_START_AUTH, STATE_VERIFY_STARING);
127    auto machine = machineBuilder->Build();
128    ASSERT_NE(machine, nullptr);
129    EXPECT_EQ(machine->GetCurrentState(), STATE_COLLECT_STOPPING);
130}
131
132HWTEST_F(StateMachineTest, MachineCreateNameCheck, TestSize.Level0)
133{
134    auto handler = ThreadHandler::GetSingleThreadInstance();
135    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine4", STATE_COLLECT_STOPPING);
136    ASSERT_NE(machineBuilder, nullptr);
137
138    machineBuilder->MakeTransition(STATE_COLLECT_STOPPING, EVENT_START_AUTH, STATE_VERIFY_STARING);
139    auto machine = machineBuilder->Build();
140    ASSERT_NE(machine, nullptr);
141    EXPECT_EQ(machine->GetMachineName(), "testMachine4");
142}
143
144HWTEST_F(StateMachineTest, MachineScheduleStepIn, TestSize.Level0)
145{
146    auto handler = ThreadHandler::GetSingleThreadInstance();
147    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine5", STATE_INIT);
148    ASSERT_NE(machineBuilder, nullptr);
149
150    machineBuilder->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING)
151        ->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING);
152
153    auto machine = machineBuilder->Build();
154    ASSERT_NE(machine, nullptr);
155    EXPECT_EQ(machine->GetCurrentState(), STATE_INIT);
156
157    machine->Schedule(EVENT_START_AUTH);
158    EXPECT_EQ(machine->EnsureCurrentState(), STATE_VERIFY_STARING);
159    machine->Schedule(EVENT_VERIFY_STARTED);
160    EXPECT_EQ(machine->EnsureCurrentState(), STATE_COLLECT_STARING);
161    machine->Schedule(EVENT_VERIFY_STARTED);
162    EXPECT_EQ(machine->EnsureCurrentState(), STATE_COLLECT_STARING);
163}
164
165HWTEST_F(StateMachineTest, MachineScheduleWithAction, TestSize.Level0)
166{
167    auto handler = ThreadHandler::GetSingleThreadInstance();
168    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine6", STATE_INIT);
169    ASSERT_NE(machineBuilder, nullptr);
170
171    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action1;
172    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action2;
173    {
174        auto init = [](FiniteStateMachine &machine) { return machine.GetCurrentState() == STATE_INIT; };
175        auto verify = [](FiniteStateMachine &machine) { return machine.GetCurrentState() == EVENT_VERIFY_STARTED; };
176        InSequence s;
177        EXPECT_CALL(action1, Call(Truly(init), EVENT_START_AUTH)).Times(Exactly(1));
178        EXPECT_CALL(action2, Call(Truly(verify), EVENT_VERIFY_STARTED)).Times(Exactly(1));
179    }
180
181    machineBuilder
182        ->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING,
183            [&action1](FiniteStateMachine &machine, uint32_t event) { action1.Call(machine, event); })
184        ->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING,
185            [&action2](FiniteStateMachine &machine, uint32_t event) { action2.Call(machine, event); });
186
187    auto machine = machineBuilder->Build();
188    ASSERT_NE(machine, nullptr);
189    EXPECT_EQ(machine->EnsureCurrentState(), STATE_INIT);
190
191    machine->Schedule(EVENT_START_AUTH);
192    EXPECT_EQ(machine->EnsureCurrentState(), STATE_VERIFY_STARING);
193    machine->Schedule(EVENT_VERIFY_STARTED);
194    EXPECT_EQ(machine->EnsureCurrentState(), STATE_COLLECT_STARING);
195    machine->Schedule(EVENT_VERIFY_STARTED);
196    EXPECT_EQ(machine->EnsureCurrentState(), STATE_COLLECT_STARING);
197}
198
199HWTEST_F(StateMachineTest, MachineScheduleWithComplexActionDirectly, TestSize.Level0)
200{
201    auto handler = MockThreadHandler::InvokeDirectly();
202    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine7", STATE_INIT);
203    ASSERT_NE(machineBuilder, nullptr);
204
205    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action1;
206    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action2;
207    {
208        auto init = [](FiniteStateMachine &machine) { return machine.GetCurrentState() == STATE_INIT; };
209        auto verify = [](FiniteStateMachine &machine) { return machine.GetCurrentState() == EVENT_VERIFY_STARTED; };
210
211        InSequence s;
212        EXPECT_CALL(action1, Call(Truly(init), EVENT_START_AUTH)).Times(Exactly(1));
213        EXPECT_CALL(action2, Call(Truly(verify), EVENT_VERIFY_STARTED)).Times(Exactly(1));
214    }
215
216    machineBuilder
217        ->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING,
218            [&action1](FiniteStateMachine &machine, uint32_t event) {
219                action1.Call(machine, event);
220                machine.Schedule(EVENT_VERIFY_STARTED);
221            })
222        ->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING,
223            [&action2](FiniteStateMachine &machine, uint32_t event) {
224                action2.Call(machine, event);
225                machine.Schedule(EVENT_COLLECT_STARTED);
226            })
227        ->MakeTransition(STATE_COLLECT_STARING, EVENT_COLLECT_STARTED, STATE_AUTH_PROCESSING, nullptr);
228
229    auto machine = machineBuilder->Build();
230    ASSERT_NE(machine, nullptr);
231    EXPECT_EQ(machine->EnsureCurrentState(), STATE_INIT);
232
233    machine->Schedule(EVENT_START_AUTH);
234    EXPECT_EQ(machine->EnsureCurrentState(), STATE_AUTH_PROCESSING);
235}
236
237HWTEST_F(StateMachineTest, MachineScheduleWithComplexActionBackGround, TestSize.Level0)
238{
239    auto handler = ThreadHandler::GetSingleThreadInstance();
240    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine8", STATE_INIT);
241    ASSERT_NE(machineBuilder, nullptr);
242
243    machineBuilder
244        ->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING,
245            [](FiniteStateMachine &machine, [[maybe_unused]] uint32_t event) {
246                machine.Schedule(EVENT_VERIFY_STARTED);
247                machine.Schedule(EVENT_COLLECT_STARTED);
248                machine.Schedule(EVENT_USER_CANCEL);
249            })
250        ->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING, nullptr)
251        ->MakeTransition(STATE_COLLECT_STARING, EVENT_COLLECT_STARTED, STATE_AUTH_PROCESSING, nullptr)
252        ->MakeTransition(STATE_AUTH_PROCESSING, EVENT_USER_CANCEL, STATE_END, nullptr);
253    auto machine = machineBuilder->Build();
254    ASSERT_NE(machine, nullptr);
255    EXPECT_EQ(machine->EnsureCurrentState(), STATE_INIT);
256
257    machine->Schedule(EVENT_START_AUTH);
258    EXPECT_EQ(machine->EnsureCurrentState(), STATE_END);
259}
260
261HWTEST_F(StateMachineTest, MachineScheduleDeadlock, TestSize.Level0)
262{
263    auto handler = ThreadHandler::GetSingleThreadInstance();
264    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine9", STATE_INIT);
265    ASSERT_NE(machineBuilder, nullptr);
266    machineBuilder->MakeTransition(STATE_INIT, STATE_INIT, STATE_INIT, [](FiniteStateMachine &machine, uint32_t event) {
267        machine.Schedule(STATE_INIT);
268        machine.Schedule(STATE_INIT);
269        machine.Schedule(STATE_INIT);
270    });
271    auto machine = machineBuilder->Build();
272    ASSERT_NE(machine, nullptr);
273    EXPECT_EQ(machine->EnsureCurrentState(), STATE_INIT);
274
275    machine->Schedule(EVENT_START_AUTH);
276    EXPECT_EQ(machine->EnsureCurrentState(), STATE_INIT);
277}
278
279HWTEST_F(StateMachineTest, MachineScheduleContinues, TestSize.Level0)
280{
281    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action;
282    EXPECT_CALL(action, Call(_, STATE_INIT)).Times(Exactly(3));
283    auto handler = ThreadHandler::GetSingleThreadInstance();
284    {
285        auto machineBuilder = FiniteStateMachine::Builder::New("testMachine10", STATE_INIT);
286        ASSERT_NE(machineBuilder, nullptr);
287        machineBuilder->MakeTransition(STATE_INIT, STATE_INIT, STATE_INIT,
288            [&action](FiniteStateMachine &machine, [[maybe_unused]] uint32_t event) {
289                action.Call(machine, STATE_INIT);
290            });
291        auto machine = machineBuilder->Build();
292        ASSERT_NE(machine, nullptr);
293        machine->Schedule(STATE_INIT);
294        machine->Schedule(STATE_INIT);
295        machine->Schedule(STATE_INIT);
296        handler->EnsureTask(nullptr);
297    }
298}
299
300HWTEST_F(StateMachineTest, MachineScheduleExpireNodeTimeout, TestSize.Level0)
301{
302    auto handler = ThreadHandler::GetSingleThreadInstance();
303
304    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action;
305    EXPECT_CALL(action, Call(_, STATE_INIT)).Times(Exactly(5));
306    {
307        auto machineBuilder = FiniteStateMachine::Builder::New("testMachine11", STATE_INIT);
308        ASSERT_NE(machineBuilder, nullptr);
309        machineBuilder->MakeTransition(STATE_INIT, STATE_INIT, STATE_INIT,
310            [&action](FiniteStateMachine &machine, [[maybe_unused]] uint32_t event) {
311                std::this_thread::sleep_for(std::chrono::milliseconds(200));
312                action.Call(machine, STATE_INIT);
313            });
314        auto machine = machineBuilder->Build();
315        ASSERT_NE(machine, nullptr);
316
317        machine->SetThreadHandler(handler);
318        handler->EnsureTask(nullptr);
319        machine->Schedule(STATE_INIT);
320        machine->Schedule(STATE_INIT);
321        machine->Schedule(STATE_INIT);
322        machine->Schedule(STATE_INIT);
323        machine->Schedule(STATE_INIT);
324        std::this_thread::sleep_for(std::chrono::milliseconds(100));
325        machine = nullptr;
326        handler->EnsureTask(nullptr);
327    }
328}
329
330HWTEST_F(StateMachineTest, MachineScheduleExpireNodeExpire, TestSize.Level0)
331{
332    auto handler = ThreadHandler::GetSingleThreadInstance();
333    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action;
334    EXPECT_CALL(action, Call(_, STATE_INIT)).Times(Exactly(0));
335    {
336        auto machineBuilder = FiniteStateMachine::Builder::New("testMachine12", STATE_INIT);
337        ASSERT_NE(machineBuilder, nullptr);
338        machineBuilder->MakeTransition(STATE_INIT, STATE_INIT, STATE_INIT,
339            [&action](FiniteStateMachine &machine, [[maybe_unused]] uint32_t event) {
340                action.Call(machine, STATE_INIT);
341            });
342        auto machine = machineBuilder->Build();
343        ASSERT_NE(machine, nullptr);
344        handler->PostTask([]() { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); });
345        machine->Schedule(STATE_INIT);
346        machine->Schedule(STATE_INIT);
347        machine->Schedule(STATE_INIT);
348        machine->Schedule(STATE_INIT);
349        machine->Schedule(STATE_INIT);
350        machine = nullptr;
351    }
352    handler->EnsureTask(nullptr);
353}
354
355static void GetTestMachine(std::shared_ptr<FiniteStateMachine> &machine,
356    MockFunction<void(FiniteStateMachine &machine, uint32_t event)> &action,
357    MockFunction<void(FiniteStateMachine &machine, uint32_t event)> &enter,
358    MockFunction<void(FiniteStateMachine &machine, uint32_t event)> &leave)
359{
360    auto machineBuilder = FiniteStateMachine::Builder::New("testMachine13", STATE_INIT);
361    ASSERT_NE(machineBuilder, nullptr);
362
363    machineBuilder->MakeTransition(STATE_INIT, EVENT_START_AUTH, STATE_VERIFY_STARING,
364        [&action](FiniteStateMachine &machine, uint32_t event) {
365            action.Call(machine, event);
366            machine.Schedule(EVENT_VERIFY_STARTED);
367            machine.Schedule(EVENT_COLLECT_STARTED);
368        });
369    machineBuilder->MakeTransition(STATE_VERIFY_STARING, EVENT_VERIFY_STARTED, STATE_COLLECT_STARING,
370        [&action](FiniteStateMachine &machine, uint32_t event) { action.Call(machine, event); });
371    machineBuilder->MakeTransition(STATE_COLLECT_STARING, EVENT_COLLECT_STARTED, STATE_AUTH_PROCESSING,
372        [&action](FiniteStateMachine &machine, uint32_t event) { action.Call(machine, event); });
373    machineBuilder->MakeTransition(STATE_AUTH_PROCESSING, EVENT_USER_CANCEL, STATE_END,
374        [&action](FiniteStateMachine &machine, uint32_t event) { action.Call(machine, event); });
375
376    machineBuilder->MakeOnStateEnter(STATE_INIT,
377        [&enter](FiniteStateMachine &machine, uint32_t event) { enter.Call(machine, event); });
378    machineBuilder->MakeOnStateLeave(STATE_INIT,
379        [&leave](FiniteStateMachine &machine, uint32_t event) { leave.Call(machine, event); });
380
381    machineBuilder->MakeOnStateEnter(STATE_VERIFY_STARING,
382        [&enter](FiniteStateMachine &machine, uint32_t event) { enter.Call(machine, event); });
383    machineBuilder->MakeOnStateLeave(STATE_VERIFY_STARING,
384        [&leave](FiniteStateMachine &machine, uint32_t event) { leave.Call(machine, event); });
385
386    machineBuilder->MakeOnStateEnter(STATE_COLLECT_STARING,
387        [&enter](FiniteStateMachine &machine, uint32_t event) { enter.Call(machine, event); });
388    machineBuilder->MakeOnStateLeave(STATE_COLLECT_STARING,
389        [&leave](FiniteStateMachine &machine, uint32_t event) { leave.Call(machine, event); });
390
391    machineBuilder->MakeOnStateEnter(STATE_AUTH_PROCESSING,
392        [&enter](FiniteStateMachine &machine, uint32_t event) { enter.Call(machine, event); });
393    machineBuilder->MakeOnStateLeave(STATE_AUTH_PROCESSING,
394        [&leave](FiniteStateMachine &machine, uint32_t event) { leave.Call(machine, event); });
395
396    machineBuilder->MakeOnStateEnter(STATE_END,
397        [&enter](FiniteStateMachine &machine, uint32_t event) { enter.Call(machine, event); });
398
399    machine = machineBuilder->Build();
400}
401
402HWTEST_F(StateMachineTest, MachineScheduleEnterAndLeave, TestSize.Level0)
403{
404    auto handler = ThreadHandler::GetSingleThreadInstance();
405    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> action;
406    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> enter;
407    MockFunction<void(FiniteStateMachine & machine, uint32_t event)> leave;
408
409    InSequence s;
410    EXPECT_CALL(action, Call(_, EVENT_START_AUTH));
411    EXPECT_CALL(leave, Call(_, STATE_INIT));
412    EXPECT_CALL(enter, Call(_, STATE_VERIFY_STARING));
413
414    EXPECT_CALL(action, Call(_, EVENT_VERIFY_STARTED));
415    EXPECT_CALL(leave, Call(_, STATE_VERIFY_STARING));
416    EXPECT_CALL(enter, Call(_, STATE_COLLECT_STARING));
417
418    EXPECT_CALL(action, Call(_, EVENT_COLLECT_STARTED));
419    EXPECT_CALL(leave, Call(_, STATE_COLLECT_STARING));
420    EXPECT_CALL(enter, Call(_, STATE_AUTH_PROCESSING));
421
422    EXPECT_CALL(action, Call(_, EVENT_USER_CANCEL));
423    EXPECT_CALL(leave, Call(_, STATE_AUTH_PROCESSING));
424    EXPECT_CALL(enter, Call(_, STATE_END));
425
426    std::shared_ptr<FiniteStateMachine> machine;
427    GetTestMachine(machine, action, enter, leave);
428    ASSERT_NE(machine, nullptr);
429
430    machine->SetThreadHandler(handler);
431    handler->EnsureTask(nullptr);
432    machine->Schedule(EVENT_START_AUTH);
433    handler->EnsureTask(nullptr);
434    machine->Schedule(EVENT_USER_CANCEL);
435    EXPECT_EQ(STATE_END, machine->EnsureCurrentState());
436
437    handler->EnsureTask(nullptr);
438}
439} // namespace FaceAuth
440} // namespace UserIam
441} // namespace OHOS