1/*
2 * Copyright (c) 2021 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#include <gtest/gtest.h>
16#include <thread>
17#include <string>
18
19#include "rwlock.h"
20
21using namespace testing::ext;
22using namespace std;
23
24namespace OHOS {
25namespace {
26
27class UtilsRWLockTest : public testing::Test {};
28
29// This class is designed for test RWLock. "buf" is protected by "rwLock".
30class TestRWLock {
31public:
32    TestRWLock():rwLock(), buf() {}
33
34    explicit TestRWLock(bool writeFirst):rwLock(writeFirst), buf() {}
35
36    void WriteStr(const string& str)
37    {
38        rwLock.LockWrite();
39        for (auto it = str.begin(); it != str.end(); it++) {
40            buf.push_back(*it);
41            this_thread::sleep_for(std::chrono::milliseconds(10)); // 10: Extend time of holding the lock
42        }
43        rwLock.UnLockWrite();
44        return;
45    }
46
47    void ReadStr(string& str)
48    {
49        rwLock.LockRead();
50        for (auto it = buf.begin(); it != buf.end(); it++) {
51            str.push_back(*it);
52            this_thread::sleep_for(std::chrono::milliseconds(10)); // 10: Extend time of holding the lock
53        }
54        rwLock.UnLockRead();
55        return;
56    }
57private:
58    Utils::RWLock rwLock;
59    string buf;
60};
61
62const string WRITE_IN_1("write1");
63const string WRITE_IN_2("write2");
64
65/*
66 * @tc.name: testRWLock001
67 * @tc.desc: RWLock here is under write-first mode. If there are some writing operation waiting,
68 * reading will never happen. Reading operations are likely to run at the same time, when all writing operations
69 * have finished.
70 */
71HWTEST_F(UtilsRWLockTest, testRWLock001, TestSize.Level1)
72{
73    TestRWLock test;
74
75    thread first(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_1)));
76    this_thread::sleep_for(std::chrono::milliseconds(4)); // Try our best to make `first` get the lock
77
78    string readOut1("");
79    thread second(bind(&TestRWLock::ReadStr, ref(test), ref(readOut1)));
80    thread third(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_2)));
81    string readOut2("");
82    thread fourth(bind(&TestRWLock::ReadStr, ref(test), ref(readOut2)));
83
84
85    first.join();
86    second.join();
87    third.join();
88    fourth.join();
89
90    EXPECT_EQ(readOut1, WRITE_IN_1 + WRITE_IN_2);
91    EXPECT_EQ(readOut2, WRITE_IN_1 + WRITE_IN_2);
92}
93
94/*
95 * @tc.name: testRWLock002
96 * @tc.desc: RWLock here is not under write-first mode. So if there are writing and reading operations in queue
97 * with a writing mission running, they will compete when the writing mission completing, but reading operations are
98 * likely to run at the same time.
99 */
100HWTEST_F(UtilsRWLockTest, testRWLock002, TestSize.Level1)
101{
102    TestRWLock test(false);
103
104    thread first(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_1)));
105    this_thread::sleep_for(chrono::milliseconds(4));
106
107    string readOut1("");
108    thread second(bind(&TestRWLock::ReadStr, ref(test), ref(readOut1)));
109    thread third(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_2)));
110    string readOut2("");
111    thread fourth(bind(&TestRWLock::ReadStr, ref(test), ref(readOut2)));
112
113    first.join();
114    second.join();
115    third.join();
116    fourth.join();
117
118    EXPECT_EQ(readOut1, readOut2);
119}
120}  // namespace
121}  // namespace OHOS