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