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 "gtest/gtest.h"
18 #include "pw_function/function.h"
19 #include "pw_log/log.h"
20 #include "pw_sync/thread_notification.h"
21 #include "pw_thread/thread.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::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 work_queue
44 .PushWork([&context] {
45 context.counter++;
46 PW_LOG_INFO("Send pong...");
47 context.worker_ping.release();
48 })
49 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
50
51 // Throw a distraction in the queue.
52 work_queue
53 .PushWork([] {
54 PW_LOG_INFO(
55 "I'm a random task in the work queue; nothing to see here!");
56 })
57 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
58
59 // Pong: wait for the callback to notify us from the worker thread.
60 context.worker_ping.acquire();
61 }
62
63 // Wait for the worker thread to terminate.
64 work_queue.RequestStop();
65 work_thread.join();
66
67 EXPECT_EQ(context.counter, kPingPongs);
68 }
69
TEST(WorkQueue,PingPongTwoRequestTypesWithExtraRequests)70 TEST(WorkQueue, PingPongTwoRequestTypesWithExtraRequests) {
71 struct {
72 int counter = 0;
73 sync::ThreadNotification worker_ping;
74 } context_a, context_b;
75
76 WorkQueueWithBuffer<10> work_queue;
77
78 // Start the worker thread.
79 thread::Thread work_thread(test::WorkQueueThreadOptions(), work_queue);
80
81 // Pick a number bigger than the circular buffer to ensure we loop around.
82 const int kPingPongs = 300;
83
84 // Run a bunch of work items in the queue.
85 for (int i = 0; i < kPingPongs; ++i) {
86 // Other requests...
87 work_queue.PushWork([] { PW_LOG_INFO("Chopping onions"); })
88 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
89
90 // Ping A: throw work at the queue that will increment our counter.
91 work_queue
92 .PushWork([&context_a] {
93 context_a.counter++;
94 context_a.worker_ping.release();
95 })
96 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
97
98 // Other requests...
99 work_queue.PushWork([] { PW_LOG_INFO("Dicing carrots"); })
100 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
101 work_queue.PushWork([] { PW_LOG_INFO("Blanching spinach"); })
102 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
103
104 // Ping B: throw work at the queue that will increment our counter.
105 work_queue
106 .PushWork([&context_b] {
107 context_b.counter++;
108 context_b.worker_ping.release();
109 })
110 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
111
112 // Other requests...
113 work_queue.PushWork([] { PW_LOG_INFO("Peeling potatoes"); })
114 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
115
116 // Pong A & B: wait for the callbacks to notify us from the worker thread.
117 context_a.worker_ping.acquire();
118 context_b.worker_ping.acquire();
119 }
120
121 // Wait for the worker thread to terminate.
122 work_queue.RequestStop();
123 work_thread.join();
124
125 EXPECT_EQ(context_a.counter, kPingPongs);
126 EXPECT_EQ(context_b.counter, kPingPongs);
127 }
128
129 // TODO(ewout): Add unit tests for the metrics once they have been restructured.
130
131 } // namespace
132 } // namespace pw::work_queue
133