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