/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <asm-generic/mman.h> #include <gtest/gtest.h> #include <atomic> #include <cstdlib> #include <sstream> #include <thread> #include <fmq/MessageQueue.h> #include <fmq/EventFlag.h> enum EventFlagBits : uint32_t { kFmqNotEmpty = 1 << 0, kFmqNotFull = 1 << 1, }; typedef android::hardware::MessageQueue<uint8_t, android::hardware::kSynchronizedReadWrite> MessageQueueSync; typedef android::hardware::MessageQueue<uint8_t, android::hardware::kUnsynchronizedWrite> MessageQueueUnsync; class SynchronizedReadWrites : public ::testing::Test { protected: virtual void TearDown() { delete mQueue; } virtual void SetUp() { static constexpr size_t kNumElementsInQueue = 2048; mQueue = new (std::nothrow) MessageQueueSync(kNumElementsInQueue); ASSERT_NE(nullptr, mQueue); ASSERT_TRUE(mQueue->isValid()); mNumMessagesMax = mQueue->getQuantumCount(); ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax); } MessageQueueSync* mQueue = nullptr; size_t mNumMessagesMax = 0; }; class UnsynchronizedWrite : public ::testing::Test { protected: virtual void TearDown() { delete mQueue; } virtual void SetUp() { static constexpr size_t kNumElementsInQueue = 2048; mQueue = new (std::nothrow) MessageQueueUnsync(kNumElementsInQueue); ASSERT_NE(nullptr, mQueue); ASSERT_TRUE(mQueue->isValid()); mNumMessagesMax = mQueue->getQuantumCount(); ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax); } MessageQueueUnsync* mQueue = nullptr; size_t mNumMessagesMax = 0; }; class BlockingReadWrites : public ::testing::Test { protected: virtual void TearDown() { delete mQueue; } virtual void SetUp() { static constexpr size_t kNumElementsInQueue = 2048; mQueue = new (std::nothrow) MessageQueueSync(kNumElementsInQueue); ASSERT_NE(nullptr, mQueue); ASSERT_TRUE(mQueue->isValid()); mNumMessagesMax = mQueue->getQuantumCount(); ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax); /* * Initialize the EventFlag word to indicate Queue is not full. */ std::atomic_init(&mFw, static_cast<uint32_t>(kFmqNotFull)); } MessageQueueSync* mQueue; std::atomic<uint32_t> mFw; size_t mNumMessagesMax = 0; }; class QueueSizeOdd : public ::testing::Test { protected: virtual void TearDown() { delete mQueue; } virtual void SetUp() { static constexpr size_t kNumElementsInQueue = 2049; mQueue = new (std::nothrow) MessageQueueSync(kNumElementsInQueue, true /* configureEventFlagWord */); ASSERT_NE(nullptr, mQueue); ASSERT_TRUE(mQueue->isValid()); mNumMessagesMax = mQueue->getQuantumCount(); ASSERT_EQ(kNumElementsInQueue, mNumMessagesMax); auto evFlagWordPtr = mQueue->getEventFlagWord(); ASSERT_NE(nullptr, evFlagWordPtr); /* * Initialize the EventFlag word to indicate Queue is not full. */ std::atomic_init(evFlagWordPtr, static_cast<uint32_t>(kFmqNotFull)); } MessageQueueSync* mQueue; size_t mNumMessagesMax = 0; }; class BadQueueConfig: public ::testing::Test { }; /* * Utility function to initialize data to be written to the FMQ */ inline void initData(uint8_t* data, size_t count) { for (size_t i = 0; i < count; i++) { data[i] = i & 0xFF; } } /* * This thread will attempt to read and block. When wait returns * it checks if the kFmqNotEmpty bit is actually set. * If the read is succesful, it signals Wake to kFmqNotFull. */ void ReaderThreadBlocking( android::hardware::MessageQueue<uint8_t, android::hardware::kSynchronizedReadWrite>* fmq, std::atomic<uint32_t>* fwAddr) { const size_t dataLen = 64; uint8_t data[dataLen]; android::hardware::EventFlag* efGroup = nullptr; android::status_t status = android::hardware::EventFlag::createEventFlag(fwAddr, &efGroup); ASSERT_EQ(android::NO_ERROR, status); ASSERT_NE(nullptr, efGroup); while (true) { uint32_t efState = 0; android::status_t ret = efGroup->wait(kFmqNotEmpty, &efState, 5000000000 /* timeoutNanoSeconds */); /* * Wait should not time out here after 5s */ ASSERT_NE(android::TIMED_OUT, ret); if ((efState & kFmqNotEmpty) && fmq->read(data, dataLen)) { efGroup->wake(kFmqNotFull); break; } } status = android::hardware::EventFlag::deleteEventFlag(&efGroup); ASSERT_EQ(android::NO_ERROR, status); } /* * This thread will attempt to read and block using the readBlocking() API and * passes in a pointer to an EventFlag object. */ void ReaderThreadBlocking2( android::hardware::MessageQueue<uint8_t, android::hardware::kSynchronizedReadWrite>* fmq, std::atomic<uint32_t>* fwAddr) { const size_t dataLen = 64; uint8_t data[dataLen]; android::hardware::EventFlag* efGroup = nullptr; android::status_t status = android::hardware::EventFlag::createEventFlag(fwAddr, &efGroup); ASSERT_EQ(android::NO_ERROR, status); ASSERT_NE(nullptr, efGroup); bool ret = fmq->readBlocking(data, dataLen, static_cast<uint32_t>(kFmqNotFull), static_cast<uint32_t>(kFmqNotEmpty), 5000000000 /* timeOutNanos */, efGroup); ASSERT_TRUE(ret); status = android::hardware::EventFlag::deleteEventFlag(&efGroup); ASSERT_EQ(android::NO_ERROR, status); } TEST_F(BadQueueConfig, QueueSizeTooLarge) { typedef android::hardware::MessageQueue<uint16_t, android::hardware::kSynchronizedReadWrite> MessageQueueSync16; size_t numElementsInQueue = SIZE_MAX / sizeof(uint16_t) + 1; MessageQueueSync16 * fmq = new (std::nothrow) MessageQueueSync16(numElementsInQueue); ASSERT_NE(nullptr, fmq); /* * Should fail due to size being too large to fit into size_t. */ ASSERT_FALSE(fmq->isValid()); } /* * Test that basic blocking works. This test uses the non-blocking read()/write() * APIs. */ TEST_F(BlockingReadWrites, SmallInputTest1) { const size_t dataLen = 64; uint8_t data[dataLen] = {0}; android::hardware::EventFlag* efGroup = nullptr; android::status_t status = android::hardware::EventFlag::createEventFlag(&mFw, &efGroup); ASSERT_EQ(android::NO_ERROR, status); ASSERT_NE(nullptr, efGroup); /* * Start a thread that will try to read and block on kFmqNotEmpty. */ std::thread Reader(ReaderThreadBlocking, mQueue, &mFw); struct timespec waitTime = {0, 100 * 1000000}; ASSERT_EQ(0, nanosleep(&waitTime, NULL)); /* * After waiting for some time write into the FMQ * and call Wake on kFmqNotEmpty. */ ASSERT_TRUE(mQueue->write(data, dataLen)); status = efGroup->wake(kFmqNotEmpty); ASSERT_EQ(android::NO_ERROR, status); ASSERT_EQ(0, nanosleep(&waitTime, NULL)); Reader.join(); status = android::hardware::EventFlag::deleteEventFlag(&efGroup); ASSERT_EQ(android::NO_ERROR, status); } /* * Test that basic blocking works. This test uses the * writeBlocking()/readBlocking() APIs. */ TEST_F(BlockingReadWrites, SmallInputTest2) { const size_t dataLen = 64; uint8_t data[dataLen] = {0}; android::hardware::EventFlag* efGroup = nullptr; android::status_t status = android::hardware::EventFlag::createEventFlag(&mFw, &efGroup); ASSERT_EQ(android::NO_ERROR, status); ASSERT_NE(nullptr, efGroup); /* * Start a thread that will try to read and block on kFmqNotEmpty. It will * call wake() on kFmqNotFull when the read is successful. */ std::thread Reader(ReaderThreadBlocking2, mQueue, &mFw); bool ret = mQueue->writeBlocking(data, dataLen, static_cast<uint32_t>(kFmqNotFull), static_cast<uint32_t>(kFmqNotEmpty), 5000000000 /* timeOutNanos */, efGroup); ASSERT_TRUE(ret); Reader.join(); status = android::hardware::EventFlag::deleteEventFlag(&efGroup); ASSERT_EQ(android::NO_ERROR, status); } /* * Test that basic blocking times out as intended. */ TEST_F(BlockingReadWrites, BlockingTimeOutTest) { android::hardware::EventFlag* efGroup = nullptr; android::status_t status = android::hardware::EventFlag::createEventFlag(&mFw, &efGroup); ASSERT_EQ(android::NO_ERROR, status); ASSERT_NE(nullptr, efGroup); /* Block on an EventFlag bit that no one will wake and time out in 1s */ uint32_t efState = 0; android::status_t ret = efGroup->wait(kFmqNotEmpty, &efState, 1000000000 /* timeoutNanoSeconds */); /* * Wait should time out in a second. */ EXPECT_EQ(android::TIMED_OUT, ret); status = android::hardware::EventFlag::deleteEventFlag(&efGroup); ASSERT_EQ(android::NO_ERROR, status); } /* * Test that odd queue sizes do not cause unaligned error * on access to EventFlag object. */ TEST_F(QueueSizeOdd, EventFlagTest) { const size_t dataLen = 64; uint8_t data[dataLen] = {0}; bool ret = mQueue->writeBlocking(data, dataLen, static_cast<uint32_t>(kFmqNotFull), static_cast<uint32_t>(kFmqNotEmpty), 5000000000 /* timeOutNanos */); ASSERT_TRUE(ret); } /* * Verify that a few bytes of data can be successfully written and read. */ TEST_F(SynchronizedReadWrites, SmallInputTest1) { const size_t dataLen = 16; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); ASSERT_TRUE(mQueue->write(data, dataLen)); uint8_t readData[dataLen] = {}; ASSERT_TRUE(mQueue->read(readData, dataLen)); ASSERT_EQ(0, memcmp(data, readData, dataLen)); } /* * Verify that a few bytes of data can be successfully written and read using * beginRead/beginWrite/CommitRead/CommitWrite */ TEST_F(SynchronizedReadWrites, SmallInputTest2) { const size_t dataLen = 16; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); MessageQueueSync::MemTransaction tx; ASSERT_TRUE(mQueue->beginWrite(dataLen, &tx)); ASSERT_TRUE(tx.copyTo(data, 0 /* startIdx */, dataLen)); ASSERT_TRUE(mQueue->commitWrite(dataLen)); uint8_t readData[dataLen] = {}; ASSERT_TRUE(mQueue->beginRead(dataLen, &tx)); ASSERT_TRUE(tx.copyFrom(readData, 0 /* startIdx */, dataLen)); ASSERT_TRUE(mQueue->commitRead(dataLen)); ASSERT_EQ(0, memcmp(data, readData, dataLen)); } /* * Verify that a few bytes of data can be successfully written and read using * beginRead/beginWrite/CommitRead/CommitWrite as well as getSlot(). */ TEST_F(SynchronizedReadWrites, SmallInputTest3) { const size_t dataLen = 16; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); MessageQueueSync::MemTransaction tx; ASSERT_TRUE(mQueue->beginWrite(dataLen, &tx)); auto first = tx.getFirstRegion(); auto second = tx.getSecondRegion(); ASSERT_EQ(first.getLength() + second.getLength(), dataLen); for (size_t i = 0; i < dataLen; i++) { uint8_t* ptr = tx.getSlot(i); *ptr = data[i]; } ASSERT_TRUE(mQueue->commitWrite(dataLen)); uint8_t readData[dataLen] = {}; ASSERT_TRUE(mQueue->beginRead(dataLen, &tx)); first = tx.getFirstRegion(); second = tx.getSecondRegion(); ASSERT_EQ(first.getLength() + second.getLength(), dataLen); for (size_t i = 0; i < dataLen; i++) { uint8_t* ptr = tx.getSlot(i); readData[i] = *ptr; } ASSERT_TRUE(mQueue->commitRead(dataLen)); ASSERT_EQ(0, memcmp(data, readData, dataLen)); } /* * Verify that read() returns false when trying to read from an empty queue. */ TEST_F(SynchronizedReadWrites, ReadWhenEmpty1) { ASSERT_EQ(0UL, mQueue->availableToRead()); const size_t dataLen = 2; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t readData[dataLen]; ASSERT_FALSE(mQueue->read(readData, dataLen)); } /* * Verify that beginRead() returns a MemTransaction object with null pointers when trying * to read from an empty queue. */ TEST_F(SynchronizedReadWrites, ReadWhenEmpty2) { ASSERT_EQ(0UL, mQueue->availableToRead()); const size_t dataLen = 2; ASSERT_LE(dataLen, mNumMessagesMax); MessageQueueSync::MemTransaction tx; ASSERT_FALSE(mQueue->beginRead(dataLen, &tx)); auto first = tx.getFirstRegion(); auto second = tx.getSecondRegion(); ASSERT_EQ(nullptr, first.getAddress()); ASSERT_EQ(nullptr, second.getAddress()); } /* * Write the queue until full. Verify that another write is unsuccessful. * Verify that availableToWrite() returns 0 as expected. */ TEST_F(SynchronizedReadWrites, WriteWhenFull1) { ASSERT_EQ(0UL, mQueue->availableToRead()); std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_EQ(0UL, mQueue->availableToWrite()); ASSERT_FALSE(mQueue->write(&data[0], 1)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_EQ(data, readData); } /* * Write the queue until full. Verify that beginWrite() returns * a MemTransaction object with null base pointers. */ TEST_F(SynchronizedReadWrites, WriteWhenFull2) { ASSERT_EQ(0UL, mQueue->availableToRead()); std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_EQ(0UL, mQueue->availableToWrite()); MessageQueueSync::MemTransaction tx; ASSERT_FALSE(mQueue->beginWrite(1, &tx)); auto first = tx.getFirstRegion(); auto second = tx.getSecondRegion(); ASSERT_EQ(nullptr, first.getAddress()); ASSERT_EQ(nullptr, second.getAddress()); } /* * Write a chunk of data equal to the queue size. * Verify that the write is successful and the subsequent read * returns the expected data. */ TEST_F(SynchronizedReadWrites, LargeInputTest1) { std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_EQ(data, readData); } /* * Attempt to write a chunk of data larger than the queue size. * Verify that it fails. Verify that a subsequent read fails and * the queue is still empty. */ TEST_F(SynchronizedReadWrites, LargeInputTest2) { ASSERT_EQ(0UL, mQueue->availableToRead()); const size_t dataLen = 4096; ASSERT_GT(dataLen, mNumMessagesMax); std::vector<uint8_t> data(dataLen); initData(&data[0], dataLen); ASSERT_FALSE(mQueue->write(&data[0], dataLen)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_NE(data, readData); ASSERT_EQ(0UL, mQueue->availableToRead()); } /* * After the queue is full, try to write more data. Verify that * the attempt returns false. Verify that the attempt did not * affect the pre-existing data in the queue. */ TEST_F(SynchronizedReadWrites, LargeInputTest3) { std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_FALSE(mQueue->write(&data[0], 1)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_EQ(data, readData); } /* * Verify that beginWrite() returns a MemTransaction with * null base pointers when attempting to write data larger * than the queue size. */ TEST_F(SynchronizedReadWrites, LargeInputTest4) { ASSERT_EQ(0UL, mQueue->availableToRead()); const size_t dataLen = 4096; ASSERT_GT(dataLen, mNumMessagesMax); MessageQueueSync::MemTransaction tx; ASSERT_FALSE(mQueue->beginWrite(dataLen, &tx)); auto first = tx.getFirstRegion(); auto second = tx.getSecondRegion(); ASSERT_EQ(nullptr, first.getAddress()); ASSERT_EQ(nullptr, second.getAddress()); } /* * Verify that multiple reads one after the other return expected data. */ TEST_F(SynchronizedReadWrites, MultipleRead) { const size_t chunkSize = 100; const size_t chunkNum = 5; const size_t dataLen = chunkSize * chunkNum; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); ASSERT_TRUE(mQueue->write(data, dataLen)); uint8_t readData[dataLen] = {}; for (size_t i = 0; i < chunkNum; i++) { ASSERT_TRUE(mQueue->read(readData + i * chunkSize, chunkSize)); } ASSERT_EQ(0, memcmp(readData, data, dataLen)); } /* * Verify that multiple writes one after the other happens correctly. */ TEST_F(SynchronizedReadWrites, MultipleWrite) { const int chunkSize = 100; const int chunkNum = 5; const size_t dataLen = chunkSize * chunkNum; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); for (unsigned int i = 0; i < chunkNum; i++) { ASSERT_TRUE(mQueue->write(data + i * chunkSize, chunkSize)); } uint8_t readData[dataLen] = {}; ASSERT_TRUE(mQueue->read(readData, dataLen)); ASSERT_EQ(0, memcmp(readData, data, dataLen)); } /* * Write enough messages into the FMQ to fill half of it * and read back the same. * Write mNumMessagesMax messages into the queue. This will cause a * wrap around. Read and verify the data. */ TEST_F(SynchronizedReadWrites, ReadWriteWrapAround1) { size_t numMessages = mNumMessagesMax - 1; std::vector<uint8_t> data(mNumMessagesMax); std::vector<uint8_t> readData(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], numMessages)); ASSERT_TRUE(mQueue->read(&readData[0], numMessages)); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_EQ(data, readData); } /* * Use beginRead/CommitRead/beginWrite/commitWrite APIs * to test wrap arounds are handled correctly. * Write enough messages into the FMQ to fill half of it * and read back the same. * Write mNumMessagesMax messages into the queue. This will cause a * wrap around. Read and verify the data. */ TEST_F(SynchronizedReadWrites, ReadWriteWrapAround2) { size_t dataLen = mNumMessagesMax - 1; std::vector<uint8_t> data(mNumMessagesMax); std::vector<uint8_t> readData(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], dataLen)); ASSERT_TRUE(mQueue->read(&readData[0], dataLen)); /* * The next write and read will have to deal with with wrap arounds. */ MessageQueueSync::MemTransaction tx; ASSERT_TRUE(mQueue->beginWrite(mNumMessagesMax, &tx)); auto first = tx.getFirstRegion(); auto second = tx.getSecondRegion(); ASSERT_EQ(first.getLength() + second.getLength(), mNumMessagesMax); ASSERT_TRUE(tx.copyTo(&data[0], 0 /* startIdx */, mNumMessagesMax)); ASSERT_TRUE(mQueue->commitWrite(mNumMessagesMax)); ASSERT_TRUE(mQueue->beginRead(mNumMessagesMax, &tx)); first = tx.getFirstRegion(); second = tx.getSecondRegion(); ASSERT_EQ(first.getLength() + second.getLength(), mNumMessagesMax); ASSERT_TRUE(tx.copyFrom(&readData[0], 0 /* startIdx */, mNumMessagesMax)); ASSERT_TRUE(mQueue->commitRead(mNumMessagesMax)); ASSERT_EQ(data, readData); } /* * Verify that a few bytes of data can be successfully written and read. */ TEST_F(UnsynchronizedWrite, SmallInputTest1) { const size_t dataLen = 16; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); ASSERT_TRUE(mQueue->write(data, dataLen)); uint8_t readData[dataLen] = {}; ASSERT_TRUE(mQueue->read(readData, dataLen)); ASSERT_EQ(0, memcmp(data, readData, dataLen)); } /* * Verify that read() returns false when trying to read from an empty queue. */ TEST_F(UnsynchronizedWrite, ReadWhenEmpty) { ASSERT_EQ(0UL, mQueue->availableToRead()); const size_t dataLen = 2; ASSERT_TRUE(dataLen < mNumMessagesMax); uint8_t readData[dataLen]; ASSERT_FALSE(mQueue->read(readData, dataLen)); } /* * Write the queue when full. Verify that a subsequent writes is succesful. * Verify that availableToWrite() returns 0 as expected. */ TEST_F(UnsynchronizedWrite, WriteWhenFull1) { ASSERT_EQ(0UL, mQueue->availableToRead()); std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_EQ(0UL, mQueue->availableToWrite()); ASSERT_TRUE(mQueue->write(&data[0], 1)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax)); } /* * Write the queue when full. Verify that a subsequent writes * using beginRead()/commitRead() is succesful. * Verify that the next read fails as expected for unsynchronized flavor. */ TEST_F(UnsynchronizedWrite, WriteWhenFull2) { ASSERT_EQ(0UL, mQueue->availableToRead()); std::vector<uint8_t> data(mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); MessageQueueUnsync::MemTransaction tx; ASSERT_TRUE(mQueue->beginWrite(1, &tx)); ASSERT_EQ(tx.getFirstRegion().getLength(), 1U); ASSERT_TRUE(tx.copyTo(&data[0], 0 /* startIdx */)); ASSERT_TRUE(mQueue->commitWrite(1)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax)); } /* * Write a chunk of data equal to the queue size. * Verify that the write is successful and the subsequent read * returns the expected data. */ TEST_F(UnsynchronizedWrite, LargeInputTest1) { std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_EQ(data, readData); } /* * Attempt to write a chunk of data larger than the queue size. * Verify that it fails. Verify that a subsequent read fails and * the queue is still empty. */ TEST_F(UnsynchronizedWrite, LargeInputTest2) { ASSERT_EQ(0UL, mQueue->availableToRead()); const size_t dataLen = 4096; ASSERT_GT(dataLen, mNumMessagesMax); std::vector<uint8_t> data(dataLen); initData(&data[0], dataLen); ASSERT_FALSE(mQueue->write(&data[0], dataLen)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_NE(data, readData); ASSERT_EQ(0UL, mQueue->availableToRead()); } /* * After the queue is full, try to write more data. Verify that * the attempt is succesful. Verify that the read fails * as expected. */ TEST_F(UnsynchronizedWrite, LargeInputTest3) { std::vector<uint8_t> data(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_TRUE(mQueue->write(&data[0], 1)); std::vector<uint8_t> readData(mNumMessagesMax); ASSERT_FALSE(mQueue->read(&readData[0], mNumMessagesMax)); } /* * Verify that multiple reads one after the other return expected data. */ TEST_F(UnsynchronizedWrite, MultipleRead) { const size_t chunkSize = 100; const size_t chunkNum = 5; const size_t dataLen = chunkSize * chunkNum; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); ASSERT_TRUE(mQueue->write(data, dataLen)); uint8_t readData[dataLen] = {}; for (size_t i = 0; i < chunkNum; i++) { ASSERT_TRUE(mQueue->read(readData + i * chunkSize, chunkSize)); } ASSERT_EQ(0, memcmp(readData, data, dataLen)); } /* * Verify that multiple writes one after the other happens correctly. */ TEST_F(UnsynchronizedWrite, MultipleWrite) { const size_t chunkSize = 100; const size_t chunkNum = 5; const size_t dataLen = chunkSize * chunkNum; ASSERT_LE(dataLen, mNumMessagesMax); uint8_t data[dataLen]; initData(data, dataLen); for (size_t i = 0; i < chunkNum; i++) { ASSERT_TRUE(mQueue->write(data + i * chunkSize, chunkSize)); } uint8_t readData[dataLen] = {}; ASSERT_TRUE(mQueue->read(readData, dataLen)); ASSERT_EQ(0, memcmp(readData, data, dataLen)); } /* * Write enough messages into the FMQ to fill half of it * and read back the same. * Write mNumMessagesMax messages into the queue. This will cause a * wrap around. Read and verify the data. */ TEST_F(UnsynchronizedWrite, ReadWriteWrapAround) { size_t numMessages = mNumMessagesMax - 1; std::vector<uint8_t> data(mNumMessagesMax); std::vector<uint8_t> readData(mNumMessagesMax); initData(&data[0], mNumMessagesMax); ASSERT_TRUE(mQueue->write(&data[0], numMessages)); ASSERT_TRUE(mQueue->read(&readData[0], numMessages)); ASSERT_TRUE(mQueue->write(&data[0], mNumMessagesMax)); ASSERT_TRUE(mQueue->read(&readData[0], mNumMessagesMax)); ASSERT_EQ(data, readData); }