blob: c51d560bc9f3add2be7153f11642f46b09a0a219 [file] [log] [blame]
Matt Spinlerf60ac272019-12-11 13:47:50 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "extensions/openpower-pels/data_interface.hpp"
17#include "extensions/openpower-pels/host_notifier.hpp"
18#include "mocks.hpp"
19#include "pel_utils.hpp"
20
21#include <fcntl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <chrono>
26
27#include <gtest/gtest.h>
28
29using namespace openpower::pels;
30using ::testing::_;
31using ::testing::Invoke;
Matt Spinler56e08262020-01-27 16:14:23 -060032using ::testing::NiceMock;
Matt Spinlerf60ac272019-12-11 13:47:50 -060033using ::testing::Return;
34namespace fs = std::filesystem;
35using namespace std::chrono;
36
37const size_t actionFlags0Offset = 66;
38const size_t actionFlags1Offset = 67;
39
40class HostNotifierTest : public CleanPELFiles
41{
Matt Spinlera943b152019-12-11 14:44:50 -060042 public:
Matt Spinler56e08262020-01-27 16:14:23 -060043 HostNotifierTest() : repo(repoPath)
Matt Spinlera943b152019-12-11 14:44:50 -060044 {
45 auto r = sd_event_default(&event);
46 EXPECT_TRUE(r >= 0);
Matt Spinler56e08262020-01-27 16:14:23 -060047
48 hostIface =
49 std::make_unique<NiceMock<MockHostInterface>>(event, dataIface);
50
51 mockHostIface = reinterpret_cast<MockHostInterface*>(hostIface.get());
52
53 auto send = [this](uint32_t id, uint32_t size) {
54 return this->mockHostIface->send(0);
55 };
56
57 // Unless otherwise specified, sendNewLogCmd should always pass.
58 ON_CALL(*mockHostIface, sendNewLogCmd(_, _))
59 .WillByDefault(Invoke(send));
Matt Spinlera943b152019-12-11 14:44:50 -060060 }
61
62 ~HostNotifierTest()
63 {
64 sd_event_unref(event);
65 }
66
67 protected:
68 sd_event* event;
Matt Spinler56e08262020-01-27 16:14:23 -060069 Repository repo;
70 NiceMock<MockDataInterface> dataIface;
71 std::unique_ptr<HostInterface> hostIface;
72 MockHostInterface* mockHostIface;
Matt Spinlerf60ac272019-12-11 13:47:50 -060073};
74
75/**
76 * @brief Create PEL with the specified action flags
77 *
78 * @param[in] actionFlagsMask - Optional action flags to use
79 *
80 * @return std::unique_ptr<PEL>
81 */
82std::unique_ptr<PEL> makePEL(uint16_t actionFlagsMask = 0)
83{
84 static uint32_t obmcID = 1;
85 auto data = pelDataFactory(TestPELType::pelSimple);
86
87 data[actionFlags0Offset] |= actionFlagsMask >> 8;
88 data[actionFlags1Offset] |= actionFlagsMask & 0xFF;
89
90 auto pel = std::make_unique<PEL>(data, obmcID++);
91 pel->assignID();
92 pel->setCommitTime();
93 return pel;
94}
95
Matt Spinler7d800a42019-12-12 10:35:01 -060096/**
97 * @brief Run an iteration of the event loop.
98 *
99 * An event loop is used for:
100 * 1) timer expiration callbacks
101 * 2) Dispatches
102 * 3) host interface receive callbacks
103 *
104 * @param[in] event - The event object
105 * @param[in] numEvents - number of times to call Event::run()
106 * @param[in] timeout - timeout value for run()
107 */
108void runEvents(sdeventplus::Event& event, size_t numEvents,
109 milliseconds timeout = milliseconds(1))
110{
111 for (size_t i = 0; i < numEvents; i++)
112 {
113 event.run(timeout);
114 }
115}
116
Matt Spinlerf60ac272019-12-11 13:47:50 -0600117// Test that host state change callbacks work
118TEST_F(HostNotifierTest, TestHostStateChange)
119{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600120 bool hostState = false;
121 bool called = false;
122 DataInterfaceBase::HostStateChangeFunc func = [&hostState,
123 &called](bool state) {
124 hostState = state;
125 called = true;
126 };
127
128 dataIface.subscribeToHostStateChange("test", func);
129
130 // callback called
131 dataIface.changeHostState(true);
132 EXPECT_TRUE(called);
133 EXPECT_TRUE(hostState);
134
135 // No change, not called
136 called = false;
137 dataIface.changeHostState(true);
138 EXPECT_FALSE(called);
139
140 // Called again
141 dataIface.changeHostState(false);
142 EXPECT_FALSE(hostState);
143 EXPECT_TRUE(called);
144
145 // Shouldn't get called after an unsubscribe
146 dataIface.unsubscribeFromHostStateChange("test");
147
148 called = false;
149
150 dataIface.changeHostState(true);
151 EXPECT_FALSE(called);
152}
153
Matt Spinlera943b152019-12-11 14:44:50 -0600154// Test dealing with how acked PELs are put on the
155// notification queue.
156TEST_F(HostNotifierTest, TestPolicyAckedPEL)
157{
Matt Spinlera943b152019-12-11 14:44:50 -0600158 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
159
160 auto pel = makePEL();
161 repo.add(pel);
162
163 // This is required
164 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600165 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600166
167 // Not in the repo
168 EXPECT_FALSE(notifier.enqueueRequired(42));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600169 EXPECT_FALSE(notifier.notifyRequired(42));
Matt Spinlera943b152019-12-11 14:44:50 -0600170
171 // Now set this PEL to host acked
172 repo.setPELHostTransState(pel->id(), TransmissionState::acked);
173
174 // Since it's acked, doesn't need to be enqueued or transmitted
175 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600176 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600177}
178
179// Test the 'don't report' PEL flag
180TEST_F(HostNotifierTest, TestPolicyDontReport)
181{
Matt Spinlera943b152019-12-11 14:44:50 -0600182 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
183
184 // dontReportToHostFlagBit
185 auto pel = makePEL(0x1000);
186
187 // Double check the action flag is still set
188 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
189 EXPECT_TRUE(actionFlags.test(dontReportToHostFlagBit));
190
191 repo.add(pel);
192
193 // Don't need to send this to the host
194 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
195}
196
197// Test that hidden PELs need notification when there
198// is no HMC.
199TEST_F(HostNotifierTest, TestPolicyHiddenNoHMC)
200{
Matt Spinlera943b152019-12-11 14:44:50 -0600201 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
202
203 // hiddenFlagBit
204 auto pel = makePEL(0x4000);
205
206 // Double check the action flag is still set
207 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
208 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
209
210 repo.add(pel);
211
212 // Still need to enqueue this
213 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
Matt Spinlerf77debb2019-12-12 10:04:33 -0600214
215 // Still need to send it
216 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
Matt Spinlera943b152019-12-11 14:44:50 -0600217}
218
219// Don't need to enqueue a hidden log already acked by the HMC
220TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCAcked)
221{
Matt Spinlera943b152019-12-11 14:44:50 -0600222 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
223
224 // hiddenFlagBit
225 auto pel = makePEL(0x4000);
226
227 // Double check the action flag is still set
228 std::bitset<16> actionFlags = pel->userHeader().actionFlags();
229 EXPECT_TRUE(actionFlags.test(hiddenFlagBit));
230
231 repo.add(pel);
232
233 // No HMC yet, so required
234 EXPECT_TRUE(notifier.enqueueRequired(pel->id()));
235
236 repo.setPELHMCTransState(pel->id(), TransmissionState::acked);
237
238 // Not required anymore
239 EXPECT_FALSE(notifier.enqueueRequired(pel->id()));
240}
241
Matt Spinlerf77debb2019-12-12 10:04:33 -0600242// Test that changing the HMC manage status affects
243// the policy with hidden log notification.
244TEST_F(HostNotifierTest, TestPolicyHiddenWithHMCManaged)
245{
Matt Spinlerf77debb2019-12-12 10:04:33 -0600246 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
247
248 // hiddenFlagBit
249 auto pel = makePEL(0x4000);
250
251 repo.add(pel);
252
253 // The first time, the HMC managed is false
254 EXPECT_TRUE(notifier.notifyRequired(pel->id()));
255
256 dataIface.setHMCManaged(true);
257
258 // This time, HMC managed is true so no need to notify
259 EXPECT_FALSE(notifier.notifyRequired(pel->id()));
260}
261
Matt Spinlerf60ac272019-12-11 13:47:50 -0600262// Test that PELs are enqueued on startup
263TEST_F(HostNotifierTest, TestStartup)
264{
Matt Spinlerf60ac272019-12-11 13:47:50 -0600265 // Give the repo 10 PELs to start with
266 for (int i = 0; i < 10; i++)
267 {
268 auto pel = makePEL();
269 repo.add(pel);
270 }
271
Matt Spinlerf60ac272019-12-11 13:47:50 -0600272 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
273
274 ASSERT_EQ(notifier.queueSize(), 10);
275
276 // Now add 10 more after the notifier is watching
277 for (int i = 0; i < 10; i++)
278 {
279 auto pel = makePEL();
280 repo.add(pel);
281 }
282
283 ASSERT_EQ(notifier.queueSize(), 20);
284}
Matt Spinler7d800a42019-12-12 10:35:01 -0600285
286// Test the simple path were PELs get sent to the host
287TEST_F(HostNotifierTest, TestSendCmd)
288{
Matt Spinler7d800a42019-12-12 10:35:01 -0600289 sdeventplus::Event sdEvent{event};
290
Matt Spinler7d800a42019-12-12 10:35:01 -0600291 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
292
Matt Spinler7d800a42019-12-12 10:35:01 -0600293 // Add a PEL with the host off
294 auto pel = makePEL();
295 repo.add(pel);
296
297 EXPECT_EQ(notifier.queueSize(), 1);
298
299 dataIface.changeHostState(true);
300
301 runEvents(sdEvent, 1);
302
303 // It was sent up
Matt Spinler56e08262020-01-27 16:14:23 -0600304 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600305 EXPECT_EQ(notifier.queueSize(), 0);
306
307 // Verify the state was written to the PEL.
308 Repository::LogID id{Repository::LogID::Pel{pel->id()}};
309 auto data = repo.getPELData(id);
310 PEL pelFromRepo{*data};
311 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::sent);
312
313 // Add a few more PELs. They will get sent.
314 pel = makePEL();
315 repo.add(pel);
316
317 // Dispatch it by hitting the event loop (no commands sent yet)
318 // Don't need to test this step discretely in the future
319 runEvents(sdEvent, 1);
Matt Spinler56e08262020-01-27 16:14:23 -0600320 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600321 EXPECT_EQ(notifier.queueSize(), 0);
322
323 // Send the command
324 runEvents(sdEvent, 1);
325
Matt Spinler56e08262020-01-27 16:14:23 -0600326 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600327 EXPECT_EQ(notifier.queueSize(), 0);
328
329 pel = makePEL();
330 repo.add(pel);
331
332 // dispatch and process the command
333 runEvents(sdEvent, 2);
334
Matt Spinler56e08262020-01-27 16:14:23 -0600335 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600336 EXPECT_EQ(notifier.queueSize(), 0);
337}
338
339// Test that if the class is created with the host up,
340// it will send PELs
341TEST_F(HostNotifierTest, TestStartAfterHostUp)
342{
Matt Spinler7d800a42019-12-12 10:35:01 -0600343 // Add PELs right away
344 auto pel = makePEL();
345 repo.add(pel);
346 pel = makePEL();
347 repo.add(pel);
348
349 sdeventplus::Event sdEvent{event};
350
Matt Spinler7d800a42019-12-12 10:35:01 -0600351 // Create the HostNotifier class with the host already up
352 dataIface.changeHostState(true);
353 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
354
355 // It should start sending PELs right away
356 runEvents(sdEvent, 2);
357
Matt Spinler56e08262020-01-27 16:14:23 -0600358 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600359 EXPECT_EQ(notifier.queueSize(), 0);
360}
361
362// Test that a single failure will cause a retry
363TEST_F(HostNotifierTest, TestHostRetry)
364{
Matt Spinler7d800a42019-12-12 10:35:01 -0600365 sdeventplus::Event sdEvent{event};
366
Matt Spinler7d800a42019-12-12 10:35:01 -0600367 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
368
Matt Spinler56e08262020-01-27 16:14:23 -0600369 auto sendFailure = [this](uint32_t id, uint32_t size) {
370 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600371 };
Matt Spinler56e08262020-01-27 16:14:23 -0600372 auto sendSuccess = [this](uint32_t id, uint32_t size) {
373 return this->mockHostIface->send(0);
Matt Spinler7d800a42019-12-12 10:35:01 -0600374 };
375
Matt Spinler56e08262020-01-27 16:14:23 -0600376 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinler7d800a42019-12-12 10:35:01 -0600377 .WillOnce(Invoke(sendFailure))
378 .WillOnce(Invoke(sendSuccess))
379 .WillOnce(Invoke(sendSuccess));
380
381 dataIface.changeHostState(true);
382
383 auto pel = makePEL();
384 repo.add(pel);
385
386 // Dispatch and handle the command
387 runEvents(sdEvent, 2);
388
389 // The command failed, so the queue isn't empty
Matt Spinler56e08262020-01-27 16:14:23 -0600390 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600391 EXPECT_EQ(notifier.queueSize(), 1);
392
393 // Run the events again to let the timer expire and the
394 // command to be retried, which will be successful.
Matt Spinler56e08262020-01-27 16:14:23 -0600395 runEvents(sdEvent, 2, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600396
Matt Spinler56e08262020-01-27 16:14:23 -0600397 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler7d800a42019-12-12 10:35:01 -0600398 EXPECT_EQ(notifier.queueSize(), 0);
399
400 // This one should pass with no problems
401 pel = makePEL();
402 repo.add(pel);
403
404 // Dispatch and handle the command
405 runEvents(sdEvent, 2);
406
Matt Spinler56e08262020-01-27 16:14:23 -0600407 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler7d800a42019-12-12 10:35:01 -0600408 EXPECT_EQ(notifier.queueSize(), 0);
409}
410
411// Test that all commands fail and notifier will give up
412TEST_F(HostNotifierTest, TestHardFailure)
413{
Matt Spinler7d800a42019-12-12 10:35:01 -0600414 sdeventplus::Event sdEvent{event};
415
Matt Spinler7d800a42019-12-12 10:35:01 -0600416 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
417
418 // Every call will fail
Matt Spinler56e08262020-01-27 16:14:23 -0600419 auto sendFailure = [this](uint32_t id, uint32_t size) {
420 return this->mockHostIface->send(1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600421 };
422
Matt Spinler56e08262020-01-27 16:14:23 -0600423 EXPECT_CALL(*mockHostIface, sendNewLogCmd(_, _))
Matt Spinler7d800a42019-12-12 10:35:01 -0600424 .WillRepeatedly(Invoke(sendFailure));
425
426 dataIface.changeHostState(true);
427
428 auto pel = makePEL();
429 repo.add(pel);
430
431 // Clock more retries than necessary
Matt Spinler56e08262020-01-27 16:14:23 -0600432 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600433
434 // Should have stopped after the 15 Tries
Matt Spinler56e08262020-01-27 16:14:23 -0600435 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 15);
Matt Spinler7d800a42019-12-12 10:35:01 -0600436 EXPECT_EQ(notifier.queueSize(), 1);
437
438 // Now add another PEL, and it should start trying again
439 // though it will also eventually give up
440 pel = makePEL();
441 repo.add(pel);
442
Matt Spinler56e08262020-01-27 16:14:23 -0600443 runEvents(sdEvent, 40, mockHostIface->getReceiveRetryDelay());
Matt Spinler7d800a42019-12-12 10:35:01 -0600444
445 // Tried an additional 15 times
Matt Spinler56e08262020-01-27 16:14:23 -0600446 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 30);
Matt Spinler7d800a42019-12-12 10:35:01 -0600447 EXPECT_EQ(notifier.queueSize(), 2);
448}
449
450// Cancel an in progress command
451TEST_F(HostNotifierTest, TestCancelCmd)
452{
Matt Spinler7d800a42019-12-12 10:35:01 -0600453 sdeventplus::Event sdEvent{event};
Matt Spinler7d800a42019-12-12 10:35:01 -0600454 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
455
Matt Spinler7d800a42019-12-12 10:35:01 -0600456 dataIface.changeHostState(true);
457
458 // Add and send one PEL, but don't enter the event loop
459 // so the receive function can't run.
460 auto pel = makePEL();
461 repo.add(pel);
462
463 // Not dispatched yet
464 EXPECT_EQ(notifier.queueSize(), 1);
465
466 // Dispatch it
467 runEvents(sdEvent, 1);
468
469 // It was sent and off the queue
470 EXPECT_EQ(notifier.queueSize(), 0);
471
472 // This will cancel the receive
473 dataIface.changeHostState(false);
474
475 // Back on the queue
476 EXPECT_EQ(notifier.queueSize(), 1);
477
478 // Turn the host back on and make sure
479 // commands will work again
480 dataIface.changeHostState(true);
481
482 runEvents(sdEvent, 1);
483
Matt Spinler56e08262020-01-27 16:14:23 -0600484 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler7d800a42019-12-12 10:35:01 -0600485 EXPECT_EQ(notifier.queueSize(), 0);
486}
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600487
488// Test that acking a PEL persist across power cycles
489TEST_F(HostNotifierTest, TestPowerCycleAndAcks)
490{
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600491 sdeventplus::Event sdEvent{event};
492
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600493 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
494
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600495 // Add 2 PELs with host off
496 auto pel = makePEL();
497 repo.add(pel);
498 auto id1 = pel->id();
499
500 pel = makePEL();
501 repo.add(pel);
502 auto id2 = pel->id();
503
504 dataIface.changeHostState(true);
505
506 runEvents(sdEvent, 2);
507
508 // The were both sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600509 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600510 EXPECT_EQ(notifier.queueSize(), 0);
511
512 dataIface.changeHostState(false);
513
514 // Those PELs weren't acked, so they will get sent again
515 EXPECT_EQ(notifier.queueSize(), 2);
516
517 // Power back on and send them again
518 dataIface.changeHostState(true);
519 runEvents(sdEvent, 2);
520
Matt Spinler56e08262020-01-27 16:14:23 -0600521 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 4);
Matt Spinlercc3b64a2019-12-12 11:27:10 -0600522 EXPECT_EQ(notifier.queueSize(), 0);
523
524 // Ack them and verify the state in the PEL.
525 notifier.ackPEL(id1);
526 notifier.ackPEL(id2);
527
528 Repository::LogID id{Repository::LogID::Pel{id1}};
529 auto data = repo.getPELData(id);
530 PEL pelFromRepo1{*data};
531 EXPECT_EQ(pelFromRepo1.hostTransmissionState(), TransmissionState::acked);
532
533 id.pelID.id = id2;
534 data = repo.getPELData(id);
535 PEL pelFromRepo2{*data};
536 EXPECT_EQ(pelFromRepo2.hostTransmissionState(), TransmissionState::acked);
537
538 // Power back off, and they should't get re-added
539 dataIface.changeHostState(false);
540
541 EXPECT_EQ(notifier.queueSize(), 0);
542}
Matt Spinler41293cb2019-12-12 13:11:09 -0600543
544// Test the host full condition
545TEST_F(HostNotifierTest, TestHostFull)
546{
547 // The full interaction with the host is:
548 // BMC: new PEL available
549 // Host: ReadPELFile (not modeled here)
550 // Host: Ack(id) (if not full), or HostFull(id)
551 // BMC: if full and any new PELs come in, don't sent them
552 // Start a timer and try again
553 // Host responds with either Ack or full
554 // and repeat
555
Matt Spinler41293cb2019-12-12 13:11:09 -0600556 sdeventplus::Event sdEvent{event};
Matt Spinler41293cb2019-12-12 13:11:09 -0600557 HostNotifier notifier{repo, dataIface, std::move(hostIface)};
558
Matt Spinler41293cb2019-12-12 13:11:09 -0600559 dataIface.changeHostState(true);
560
561 // Add and dispatch/send one PEL
562 auto pel = makePEL();
563 auto id = pel->id();
564 repo.add(pel);
565 runEvents(sdEvent, 2);
566
Matt Spinler56e08262020-01-27 16:14:23 -0600567 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600568 EXPECT_EQ(notifier.queueSize(), 0);
569
570 // Host is full
571 notifier.setHostFull(id);
572
573 // It goes back on the queue
574 EXPECT_EQ(notifier.queueSize(), 1);
575
576 // The transmission state goes back to new
577 Repository::LogID i{Repository::LogID::Pel{id}};
578 auto data = repo.getPELData(i);
579 PEL pelFromRepo{*data};
580 EXPECT_EQ(pelFromRepo.hostTransmissionState(), TransmissionState::newPEL);
581
582 // Clock it, nothing should be sent still.
583 runEvents(sdEvent, 1);
584
Matt Spinler56e08262020-01-27 16:14:23 -0600585 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600586 EXPECT_EQ(notifier.queueSize(), 1);
587
588 // Add another PEL and clock it, still nothing sent
589 pel = makePEL();
590 repo.add(pel);
591 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600592 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinler41293cb2019-12-12 13:11:09 -0600593 EXPECT_EQ(notifier.queueSize(), 2);
594
595 // Let the host full timer expire to trigger a retry.
596 // Add some extra event passes just to be sure nothing new is sent.
Matt Spinler56e08262020-01-27 16:14:23 -0600597 runEvents(sdEvent, 5, mockHostIface->getHostFullRetryDelay());
Matt Spinler41293cb2019-12-12 13:11:09 -0600598
599 // The timer expiration will send just the 1, not both
Matt Spinler56e08262020-01-27 16:14:23 -0600600 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 2);
Matt Spinler41293cb2019-12-12 13:11:09 -0600601 EXPECT_EQ(notifier.queueSize(), 1);
602
603 // Host still full
604 notifier.setHostFull(id);
605
606 // Let the host full timer attempt again
Matt Spinler56e08262020-01-27 16:14:23 -0600607 runEvents(sdEvent, 2, mockHostIface->getHostFullRetryDelay());
608 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600609
610 // Add yet another PEL with the retry timer expired.
611 // It shouldn't get sent out.
612 pel = makePEL();
613 repo.add(pel);
614 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600615 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 3);
Matt Spinler41293cb2019-12-12 13:11:09 -0600616
617 // The last 2 PELs still on the queue
618 EXPECT_EQ(notifier.queueSize(), 2);
619
620 // Host no longer full, it finally acks the first PEL
621 notifier.ackPEL(id);
622
623 // Now the remaining 2 PELs will be dispatched
624 runEvents(sdEvent, 3);
625
Matt Spinler56e08262020-01-27 16:14:23 -0600626 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 5);
Matt Spinler41293cb2019-12-12 13:11:09 -0600627 EXPECT_EQ(notifier.queueSize(), 0);
628}
Matt Spinlera19b6232019-12-12 13:30:14 -0600629
630// Test when the host says it was send a malformed PEL
631TEST_F(HostNotifierTest, TestBadPEL)
632{
Matt Spinlera19b6232019-12-12 13:30:14 -0600633 sdeventplus::Event sdEvent{event};
634
635 {
Matt Spinler56e08262020-01-27 16:14:23 -0600636 Repository repo1{repoPath};
637 HostNotifier notifier{repo1, dataIface, std::move(hostIface)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600638
639 dataIface.changeHostState(true);
640
641 // Add a PEL and dispatch and send it
642 auto pel = makePEL();
643 auto id = pel->id();
Matt Spinler56e08262020-01-27 16:14:23 -0600644 repo1.add(pel);
Matt Spinlera19b6232019-12-12 13:30:14 -0600645
646 runEvents(sdEvent, 2);
Matt Spinler56e08262020-01-27 16:14:23 -0600647 EXPECT_EQ(mockHostIface->numCmdsProcessed(), 1);
Matt Spinlera19b6232019-12-12 13:30:14 -0600648 EXPECT_EQ(notifier.queueSize(), 0);
649
650 // The host rejected it.
651 notifier.setBadPEL(id);
652
653 // Doesn't go back on the queue
654 EXPECT_EQ(notifier.queueSize(), 0);
655
656 // Check the state was saved in the PEL itself
657 Repository::LogID i{Repository::LogID::Pel{id}};
Matt Spinler56e08262020-01-27 16:14:23 -0600658 auto data = repo1.getPELData(i);
Matt Spinlera19b6232019-12-12 13:30:14 -0600659 PEL pelFromRepo{*data};
660 EXPECT_EQ(pelFromRepo.hostTransmissionState(),
661 TransmissionState::badPEL);
662
663 dataIface.changeHostState(false);
664
665 // Ensure it doesn't go back on the queue on a power cycle
666 EXPECT_EQ(notifier.queueSize(), 0);
667 }
668
669 // Now restore the repo, and make sure it doesn't come back
670 {
Matt Spinler56e08262020-01-27 16:14:23 -0600671 Repository repo1{repoPath};
Matt Spinlera19b6232019-12-12 13:30:14 -0600672
Matt Spinler56e08262020-01-27 16:14:23 -0600673 std::unique_ptr<HostInterface> hostIface1 =
Matt Spinlera19b6232019-12-12 13:30:14 -0600674 std::make_unique<MockHostInterface>(event, dataIface);
675
Matt Spinler56e08262020-01-27 16:14:23 -0600676 HostNotifier notifier{repo1, dataIface, std::move(hostIface1)};
Matt Spinlera19b6232019-12-12 13:30:14 -0600677
678 EXPECT_EQ(notifier.queueSize(), 0);
679 }
680}