1#include "test.h"
2
3#include <base/system.h>
4
5#include <engine/shared/host_lookup.h>
6#include <engine/shared/jobs.h>
7
8#include <gtest/gtest.h>
9
10#include <functional>
11
12static const int TEST_NUM_THREADS = 4;
13
14class Jobs : public ::testing::Test
15{
16protected:
17 CJobPool m_Pool;
18
19 void SetUp() override
20 {
21 m_Pool.Init(NumThreads: TEST_NUM_THREADS);
22 }
23
24 void TearDown() override
25 {
26 m_Pool.Shutdown();
27 }
28
29 void Add(std::shared_ptr<IJob> pJob)
30 {
31 m_Pool.Add(pJob: std::move(pJob));
32 }
33};
34
35class CJob : public IJob
36{
37 std::function<void()> m_JobFunction;
38 void Run() override { m_JobFunction(); }
39
40public:
41 CJob(std::function<void()> &&JobFunction) :
42 m_JobFunction(JobFunction) {}
43
44 void Abortable(bool Abortable)
45 {
46 IJob::Abortable(Abortable);
47 }
48};
49
50TEST_F(Jobs, Constructor)
51{
52}
53
54TEST_F(Jobs, Simple)
55{
56 Add(pJob: std::make_shared<CJob>(args: [] {}));
57}
58
59TEST_F(Jobs, Wait)
60{
61 SEMAPHORE sphore;
62 sphore_init(sem: &sphore);
63 Add(pJob: std::make_shared<CJob>(args: [&] { sphore_signal(sem: &sphore); }));
64 sphore_wait(sem: &sphore);
65 sphore_destroy(sem: &sphore);
66}
67
68TEST_F(Jobs, AbortAbortable)
69{
70 auto pJob = std::make_shared<CJob>(args: [&] {});
71 pJob->Abortable(Abortable: true);
72 EXPECT_TRUE(pJob->IsAbortable());
73 Add(pJob);
74 EXPECT_TRUE(pJob->Abort());
75 EXPECT_EQ(pJob->State(), IJob::STATE_ABORTED);
76}
77
78TEST_F(Jobs, AbortUnabortable)
79{
80 auto pJob = std::make_shared<CJob>(args: [&] {});
81 pJob->Abortable(Abortable: false);
82 EXPECT_FALSE(pJob->IsAbortable());
83 Add(pJob);
84 EXPECT_FALSE(pJob->Abort());
85 EXPECT_NE(pJob->State(), IJob::STATE_ABORTED);
86}
87
88TEST_F(Jobs, LookupHost)
89{
90 static const char *HOST = "example.com";
91 static const int NETTYPE = NETTYPE_ALL;
92 auto pJob = std::make_shared<CHostLookup>(args&: HOST, args: NETTYPE);
93
94 EXPECT_STREQ(pJob->Hostname(), HOST);
95 EXPECT_EQ(pJob->Nettype(), NETTYPE);
96
97 Add(pJob);
98 while(pJob->State() != IJob::STATE_DONE)
99 {
100 // yay, busy loop...
101 thread_yield();
102 }
103
104 EXPECT_STREQ(pJob->Hostname(), HOST);
105 EXPECT_EQ(pJob->Nettype(), NETTYPE);
106 if(pJob->Result() == 0)
107 {
108 EXPECT_EQ(pJob->Addr().type & NETTYPE, pJob->Addr().type);
109 }
110}
111
112TEST_F(Jobs, LookupHostWebsocket)
113{
114 static const char *HOST = "ws://example.com";
115 static const int NETTYPE = NETTYPE_ALL;
116 auto pJob = std::make_shared<CHostLookup>(args&: HOST, args: NETTYPE);
117
118 EXPECT_STREQ(pJob->Hostname(), HOST);
119 EXPECT_EQ(pJob->Nettype(), NETTYPE);
120
121 Add(pJob);
122 while(pJob->State() != IJob::STATE_DONE)
123 {
124 // yay, busy loop...
125 thread_yield();
126 }
127
128 EXPECT_STREQ(pJob->Hostname(), HOST);
129 EXPECT_EQ(pJob->Nettype(), NETTYPE);
130 if(pJob->Result() == 0)
131 {
132 EXPECT_EQ(pJob->Addr().type & (NETTYPE_WEBSOCKET_IPV4 | NETTYPE_WEBSOCKET_IPV6), pJob->Addr().type);
133 }
134}
135
136TEST_F(Jobs, Many)
137{
138 std::atomic<int> ThreadsRunning(0);
139 std::vector<std::shared_ptr<IJob>> vpJobs;
140 SEMAPHORE sphore;
141 sphore_init(sem: &sphore);
142 for(int i = 0; i < TEST_NUM_THREADS; i++)
143 {
144 std::shared_ptr<IJob> pJob = std::make_shared<CJob>(args: [&] {
145 int Prev = ThreadsRunning.fetch_add(i: 1);
146 if(Prev == TEST_NUM_THREADS - 1)
147 {
148 sphore_signal(sem: &sphore);
149 }
150 });
151 EXPECT_EQ(pJob->State(), IJob::STATE_QUEUED);
152 vpJobs.push_back(x: pJob);
153 }
154 for(auto &pJob : vpJobs)
155 {
156 Add(pJob);
157 }
158 sphore_wait(sem: &sphore);
159 sphore_destroy(sem: &sphore);
160 TearDown();
161 for(auto &pJob : vpJobs)
162 {
163 EXPECT_EQ(pJob->State(), IJob::STATE_DONE);
164 }
165 SetUp();
166}
167