blob: 5c810dc1e79647a0e325b6d247238a659a4eef26 [file] [log] [blame] [edit]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/cbuf.h>
#include <lib/unittest/unittest.h>
#include <zircon/errors.h>
#include <cstddef>
#include <ktl/array.h>
namespace {
bool Constructor() {
BEGIN_TEST;
// Construct, but don't initialize.
{ Cbuf cbuf; }
// Construct and initialize.
{
char buffer[32];
Cbuf cbuf;
cbuf.Initialize(sizeof(buffer), buffer);
ASSERT_FALSE(cbuf.Full());
}
END_TEST;
}
bool ReadWrite() {
BEGIN_TEST;
char buffer[4];
Cbuf cbuf;
cbuf.Initialize(sizeof(buffer), buffer);
// Nothing to read, don't wait.
ASSERT_EQ(ZX_ERR_SHOULD_WAIT, cbuf.ReadChar(false).status_value());
// Write some characters.
ktl::array data = {'A', 'B', 'C'};
for (char c : data) {
ASSERT_EQ(1U, cbuf.WriteChar(c));
}
ASSERT_TRUE(cbuf.Full());
// Read them back.
for (size_t i = 0; i < data.size(); ++i) {
auto result = cbuf.ReadCharWithContext(true);
ASSERT_TRUE(result.is_ok());
ASSERT_EQ(result->transitioned_from_full, i == 0);
ASSERT_EQ(result->c, data[i]);
}
ASSERT_FALSE(cbuf.Full());
END_TEST;
}
// This test verifies that a thread repeatedly calling ReadChar concurrently with another thread
// calling WriteChar can be cleanly killed. This is a regression test for
// https://fxbug.dev/42156534. It has no false positives (i.e. it should never spuriously fail),
// but it can have false negatives.
bool ReadWriteRace() {
BEGIN_TEST;
char buffer[4];
Cbuf cbuf;
cbuf.Initialize(sizeof(buffer), buffer);
// Define a thread that will repeatedly read from the Cbuf until it is killed.
thread_start_routine fn = [](void* arg) -> int {
Cbuf& cbuf = *reinterpret_cast<Cbuf*>(arg);
while (true) {
zx::result<char> result = cbuf.ReadChar(true);
if (!result.is_ok()) {
return result.error_value();
}
}
};
// Create and start the thread.
Thread* t = Thread::Create("cbuf race", fn, &cbuf, DEFAULT_PRIORITY);
ASSERT_TRUE(t != nullptr);
t->Resume();
// The number of loop iterations should be large enough to create an opportunity for ReadChar and
// WriteChar to race, but small enough to ensure the test completes quickly.
for (int i = 0; i < 1000; ++i) {
cbuf.WriteChar('A');
}
// Kill the thread and wait for it to terminate.
t->Kill();
int retcode;
t->Join(&retcode, ZX_TIME_INFINITE);
ASSERT_EQ(ZX_ERR_INTERNAL_INTR_KILLED, retcode);
END_TEST;
}
} // namespace
UNITTEST_START_TESTCASE(cbuf_tests)
UNITTEST("Constructor", Constructor)
UNITTEST("ReadWrite", ReadWrite)
UNITTEST("ReadWriteRace", ReadWriteRace)
UNITTEST_END_TESTCASE(cbuf_tests, "cbuf", "cbuf tests")