1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_work_queue/work_queue.h"
16
17 #include "pw_function/function.h"
18 #include "pw_log/log.h"
19 #include "pw_sync/thread_notification.h"
20 #include "pw_thread/thread.h"
21 #include "pw_unit_test/framework.h"
22 #include "pw_work_queue/test_thread.h"
23
24 namespace pw::work_queue {
25 namespace {
26
TEST(WorkQueue,PingPongOneRequestType)27 TEST(WorkQueue, PingPongOneRequestType) {
28 struct {
29 int counter = 0;
30 sync::ThreadNotification worker_ping;
31 } context;
32
33 WorkQueueWithBuffer<10> work_queue;
34
35 // Start the worker thread.
36 Thread work_thread(test::WorkQueueThreadOptions(), work_queue);
37
38 // Pick a number bigger than the circular buffer to ensure we loop around.
39 const int kPingPongs = 300;
40
41 for (int i = 0; i < kPingPongs; ++i) {
42 // Ping: throw work at the queue that will increment our counter.
43 EXPECT_EQ(OkStatus(), work_queue.PushWork([&context] {
44 context.counter++;
45 PW_LOG_INFO("Send pong...");
46 context.worker_ping.release();
47 }));
48
49 // Throw a distraction in the queue.
50 EXPECT_EQ(OkStatus(), work_queue.PushWork([] {
51 PW_LOG_INFO("I'm a random task in the work queue; nothing to see here!");
52 }));
53
54 // Pong: wait for the callback to notify us from the worker thread.
55 context.worker_ping.acquire();
56 }
57
58 // Wait for the worker thread to terminate.
59 work_queue.RequestStop();
60 work_thread.join();
61
62 EXPECT_EQ(context.counter, kPingPongs);
63 }
64
TEST(WorkQueue,PingPongTwoRequestTypesWithExtraRequests)65 TEST(WorkQueue, PingPongTwoRequestTypesWithExtraRequests) {
66 struct {
67 int counter = 0;
68 sync::ThreadNotification worker_ping;
69 } context_a, context_b;
70
71 WorkQueueWithBuffer<10> work_queue;
72
73 // Start the worker thread.
74 Thread work_thread(test::WorkQueueThreadOptions(), work_queue);
75
76 // Pick a number bigger than the circular buffer to ensure we loop around.
77 const int kPingPongs = 300;
78
79 // Run a bunch of work items in the queue.
80 for (int i = 0; i < kPingPongs; ++i) {
81 // Other requests...
82 EXPECT_EQ(OkStatus(),
83 work_queue.PushWork([] { PW_LOG_INFO("Chopping onions"); }));
84
85 // Ping A: throw work at the queue that will increment our counter.
86 EXPECT_EQ(OkStatus(), work_queue.PushWork([&context_a] {
87 context_a.counter++;
88 context_a.worker_ping.release();
89 }));
90
91 // Other requests...
92 EXPECT_EQ(OkStatus(),
93 work_queue.PushWork([] { PW_LOG_INFO("Dicing carrots"); }));
94 EXPECT_EQ(OkStatus(),
95 work_queue.PushWork([] { PW_LOG_INFO("Blanching spinach"); }));
96
97 // Ping B: throw work at the queue that will increment our counter.
98 EXPECT_EQ(OkStatus(), work_queue.PushWork([&context_b] {
99 context_b.counter++;
100 context_b.worker_ping.release();
101 }));
102
103 // Other requests...
104 EXPECT_EQ(OkStatus(),
105 work_queue.PushWork([] { PW_LOG_INFO("Peeling potatoes"); }));
106
107 // Pong A & B: wait for the callbacks to notify us from the worker thread.
108 context_a.worker_ping.acquire();
109 context_b.worker_ping.acquire();
110 }
111
112 // Wait for the worker thread to terminate.
113 work_queue.RequestStop();
114 work_thread.join();
115
116 EXPECT_EQ(context_a.counter, kPingPongs);
117 EXPECT_EQ(context_b.counter, kPingPongs);
118 }
119
120 // TODO(ewout): Add unit tests for the metrics once they have been restructured.
121
122 } // namespace
123 } // namespace pw::work_queue
124