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