1 /*
2  * Copyright (C) 2019 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 <string>
18 
19 #include <android-base/expected.h>
20 #include <gtest/gtest.h>
21 #include <netdutils/ThreadUtil.h>
22 
23 namespace android::netdutils {
24 
25 namespace {
26 
getThreadName()27 android::base::expected<std::string, int> getThreadName() {
28     char name[16] = {};
29     if (const int ret = pthread_getname_np(pthread_self(), name, sizeof(name)); ret != 0) {
30         return android::base::unexpected(ret);
31     }
32     return std::string(name);
33 }
34 
35 class NoopRun {
36   public:
NoopRun(const std::string & name="")37     explicit NoopRun(const std::string& name = "") : mName(name) { instanceNum++; }
38 
39     // Destructor happens in the thread.
~NoopRun()40     ~NoopRun() {
41         if (checkName) {
42             auto expected = getThreadName();
43             EXPECT_TRUE(expected.has_value());
44             EXPECT_EQ(mExpectedName, expected.value());
45         }
46         instanceNum--;
47     }
48 
run()49     void run() {}
50 
threadName()51     std::string threadName() { return mName; }
52 
53     // Set the expected thread name which will be used to check if it matches the actual thread
54     // name which is returned from the system call. The check will happen in the destructor.
setExpectedName(const std::string & expectedName)55     void setExpectedName(const std::string& expectedName) {
56         checkName = true;
57         mExpectedName = expectedName;
58     }
59 
waitForAllReleased(int timeoutMs)60     static bool waitForAllReleased(int timeoutMs) {
61         constexpr int intervalMs = 20;
62         int limit = timeoutMs / intervalMs;
63         for (int i = 1; i < limit; i++) {
64             if (instanceNum == 0) {
65                 return true;
66             }
67             usleep(intervalMs * 1000);
68         }
69         return false;
70     }
71 
72     // To track how many instances are alive.
73     static std::atomic<int> instanceNum;
74 
75   private:
76     std::string mName;
77     std::string mExpectedName;
78     bool checkName = false;
79 };
80 
81 std::atomic<int> NoopRun::instanceNum;
82 
83 }  // namespace
84 
TEST(ThreadUtilTest,objectReleased)85 TEST(ThreadUtilTest, objectReleased) {
86     NoopRun::instanceNum = 0;
87     NoopRun* obj = new NoopRun();
88     EXPECT_EQ(1, NoopRun::instanceNum);
89     threadLaunch(obj);
90 
91     // Wait for the object released along with the thread exited.
92     EXPECT_TRUE(NoopRun::waitForAllReleased(1000));
93     EXPECT_EQ(0, NoopRun::instanceNum);
94 }
95 
TEST(ThreadUtilTest,SetThreadName)96 TEST(ThreadUtilTest, SetThreadName) {
97     NoopRun::instanceNum = 0;
98 
99     // Test thread name empty.
100     NoopRun* obj1 = new NoopRun();
101     obj1->setExpectedName("");
102 
103     // Test normal case.
104     NoopRun* obj2 = new NoopRun("TestName");
105     obj2->setExpectedName("TestName");
106 
107     // Test thread name too long.
108     std::string name("TestNameTooooLong");
109     NoopRun* obj3 = new NoopRun(name);
110     obj3->setExpectedName(name.substr(0, 15));
111 
112     // Thread names are examined in their destructors.
113     EXPECT_EQ(3, NoopRun::instanceNum);
114     threadLaunch(obj1);
115     threadLaunch(obj2);
116     threadLaunch(obj3);
117 
118     EXPECT_TRUE(NoopRun::waitForAllReleased(1000));
119     EXPECT_EQ(0, NoopRun::instanceNum);
120 }
121 
122 }  // namespace android::netdutils
123