You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
12 KiB
364 lines
12 KiB
4 months ago
|
//
|
||
|
// Copyright 2018 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 "fmq_driver/VtsFmqDriver.h"
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <limits.h>
|
||
|
#include <math.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <time.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include <fmq/MessageQueue.h>
|
||
|
#include <gtest/gtest.h>
|
||
|
|
||
|
using android::hardware::kSynchronizedReadWrite;
|
||
|
using android::hardware::kUnsynchronizedWrite;
|
||
|
using namespace std;
|
||
|
|
||
|
namespace android {
|
||
|
namespace vts {
|
||
|
|
||
|
// A test that initializes a single writer and a single reader.
|
||
|
class SyncReadWrites : public ::testing::Test {
|
||
|
protected:
|
||
|
virtual void SetUp() {
|
||
|
static constexpr size_t NUM_ELEMS = 2048;
|
||
|
|
||
|
// initialize a writer
|
||
|
writer_id_ = manager_.CreateFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", NUM_ELEMS, false);
|
||
|
ASSERT_NE(writer_id_, -1);
|
||
|
|
||
|
// initialize a reader
|
||
|
reader_id_ = manager_.CreateFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_);
|
||
|
ASSERT_NE(reader_id_, -1);
|
||
|
}
|
||
|
|
||
|
virtual void TearDown() {}
|
||
|
|
||
|
VtsFmqDriver manager_;
|
||
|
QueueId writer_id_;
|
||
|
QueueId reader_id_;
|
||
|
};
|
||
|
|
||
|
// A test that initializes a single writer and a single reader.
|
||
|
// TODO: add tests for blocking between multiple queues later when there is more
|
||
|
// use case.
|
||
|
class BlockingReadWrites : public ::testing::Test {
|
||
|
protected:
|
||
|
virtual void SetUp() {
|
||
|
static constexpr size_t NUM_ELEMS = 2048;
|
||
|
|
||
|
// initialize a writer
|
||
|
writer_id_ = manager_.CreateFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", NUM_ELEMS, true);
|
||
|
ASSERT_NE(writer_id_, -1);
|
||
|
|
||
|
// initialize a reader
|
||
|
reader_id_ = manager_.CreateFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_);
|
||
|
ASSERT_NE(reader_id_, -1);
|
||
|
}
|
||
|
|
||
|
virtual void TearDown() {}
|
||
|
|
||
|
VtsFmqDriver manager_;
|
||
|
QueueId writer_id_;
|
||
|
QueueId reader_id_;
|
||
|
};
|
||
|
|
||
|
// A test that initializes a single writer and two readers.
|
||
|
class UnsynchronizedWrites : public ::testing::Test {
|
||
|
protected:
|
||
|
virtual void SetUp() {
|
||
|
static constexpr size_t NUM_ELEMS = 2048;
|
||
|
|
||
|
// initialize a writer
|
||
|
writer_id_ = manager_.CreateFmq<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", NUM_ELEMS, false);
|
||
|
ASSERT_NE(writer_id_, -1);
|
||
|
|
||
|
// initialize two readers
|
||
|
reader_id1_ = manager_.CreateFmq<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", writer_id_);
|
||
|
reader_id2_ = manager_.CreateFmq<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", writer_id_);
|
||
|
ASSERT_NE(reader_id1_, -1);
|
||
|
ASSERT_NE(reader_id2_, -1);
|
||
|
}
|
||
|
|
||
|
virtual void TearDown() {}
|
||
|
|
||
|
VtsFmqDriver manager_;
|
||
|
QueueId writer_id_;
|
||
|
QueueId reader_id1_;
|
||
|
QueueId reader_id2_;
|
||
|
};
|
||
|
|
||
|
// Tests the reader and writer are set up correctly.
|
||
|
TEST_F(SyncReadWrites, SetupBasicTest) {
|
||
|
static constexpr size_t NUM_ELEMS = 2048;
|
||
|
|
||
|
// check if the writer has a valid queue
|
||
|
ASSERT_TRUE((manager_.IsValid<uint16_t, kSynchronizedReadWrite>("uint16_t",
|
||
|
writer_id_)));
|
||
|
|
||
|
// check queue size on writer side
|
||
|
size_t writer_queue_size;
|
||
|
ASSERT_TRUE((manager_.GetQuantumCount<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, &writer_queue_size)));
|
||
|
ASSERT_EQ(NUM_ELEMS, writer_queue_size);
|
||
|
|
||
|
// check queue element size on writer side
|
||
|
size_t writer_elem_size;
|
||
|
ASSERT_TRUE((manager_.GetQuantumSize<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, &writer_elem_size)));
|
||
|
ASSERT_EQ(sizeof(uint16_t), writer_elem_size);
|
||
|
|
||
|
// check space available for writer
|
||
|
size_t writer_available_writes;
|
||
|
ASSERT_TRUE((manager_.AvailableToWrite<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, &writer_available_writes)));
|
||
|
ASSERT_EQ(NUM_ELEMS, writer_available_writes);
|
||
|
|
||
|
// check if the reader has a valid queue
|
||
|
ASSERT_TRUE((manager_.IsValid<uint16_t, kSynchronizedReadWrite>("uint16_t",
|
||
|
reader_id_)));
|
||
|
|
||
|
// check queue size on reader side
|
||
|
size_t reader_queue_size;
|
||
|
ASSERT_TRUE((manager_.GetQuantumCount<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, &reader_queue_size)));
|
||
|
ASSERT_EQ(NUM_ELEMS, reader_queue_size);
|
||
|
|
||
|
// check queue element size on reader side
|
||
|
size_t reader_elem_size;
|
||
|
ASSERT_TRUE((manager_.GetQuantumSize<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, &reader_elem_size)));
|
||
|
ASSERT_EQ(sizeof(uint16_t), reader_elem_size);
|
||
|
|
||
|
// check items available for reader
|
||
|
size_t reader_available_reads;
|
||
|
ASSERT_TRUE((manager_.AvailableToRead<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, &reader_available_reads)));
|
||
|
ASSERT_EQ(0, reader_available_reads);
|
||
|
}
|
||
|
|
||
|
// util method to initialize data
|
||
|
void InitData(uint16_t* data, size_t len) {
|
||
|
for (size_t i = 0; i < len; i++) {
|
||
|
data[i] = rand() % 100 + 1; // a random value between 1 and 100
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tests a basic writer and reader interaction.
|
||
|
// Reader reads back data written by the writer correctly.
|
||
|
TEST_F(SyncReadWrites, ReadWriteSuccessTest) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
uint16_t read_data[DATA_SIZE];
|
||
|
|
||
|
// initialize the data to transfer
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
|
||
|
// writer should succeed
|
||
|
ASSERT_TRUE((manager_.WriteFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE)));
|
||
|
|
||
|
// reader should succeed
|
||
|
ASSERT_TRUE((manager_.ReadFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, read_data, DATA_SIZE)));
|
||
|
|
||
|
// check if the data is read back correctly
|
||
|
ASSERT_EQ(0, memcmp(write_data, read_data, DATA_SIZE * sizeof(uint16_t)));
|
||
|
}
|
||
|
|
||
|
// Tests reading from an empty queue.
|
||
|
TEST_F(SyncReadWrites, ReadEmpty) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
uint16_t read_data[DATA_SIZE];
|
||
|
|
||
|
// attempt to read from an empty queue
|
||
|
ASSERT_FALSE((manager_.ReadFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, read_data, DATA_SIZE)));
|
||
|
}
|
||
|
|
||
|
// Tests writing to a full queue.
|
||
|
TEST_F(SyncReadWrites, WriteFull) {
|
||
|
static constexpr size_t DATA_SIZE = 2048;
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
uint16_t read_data[DATA_SIZE];
|
||
|
|
||
|
// initialize the data to transfer
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
|
||
|
// This write succeeds, filling up the queue
|
||
|
ASSERT_TRUE((manager_.WriteFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE)));
|
||
|
|
||
|
// This write fails, queue is full
|
||
|
ASSERT_FALSE((manager_.WriteFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE)));
|
||
|
|
||
|
// checks space available is 0
|
||
|
size_t writer_available_writes;
|
||
|
ASSERT_TRUE((manager_.AvailableToWrite<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, &writer_available_writes)));
|
||
|
ASSERT_EQ(0, writer_available_writes);
|
||
|
|
||
|
// reader succeeds, reads the entire queue back correctly
|
||
|
ASSERT_TRUE((manager_.ReadFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, read_data, DATA_SIZE)));
|
||
|
|
||
|
ASSERT_EQ(0, memcmp(write_data, read_data, DATA_SIZE * sizeof(uint16_t)));
|
||
|
}
|
||
|
|
||
|
// Attempt to write more than the size of the queue.
|
||
|
TEST_F(SyncReadWrites, WriteTooLarge) {
|
||
|
static constexpr size_t LARGE_DATA_SIZE = 2049;
|
||
|
uint16_t write_data[LARGE_DATA_SIZE];
|
||
|
|
||
|
// initialize data to transfer
|
||
|
InitData(write_data, LARGE_DATA_SIZE);
|
||
|
|
||
|
// write more than the queue size
|
||
|
ASSERT_FALSE((manager_.WriteFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, write_data, LARGE_DATA_SIZE)));
|
||
|
}
|
||
|
|
||
|
// Pass the wrong type.
|
||
|
TEST_F(SyncReadWrites, WrongType) {
|
||
|
static constexpr size_t DATA_SIZE = 2;
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
|
||
|
// initialize data to transfer
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
|
||
|
// attempt to write uint32_t type
|
||
|
ASSERT_FALSE((manager_.WriteFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint32_t", writer_id_, write_data, DATA_SIZE)));
|
||
|
}
|
||
|
|
||
|
// Tests consecutive interaction between writer and reader.
|
||
|
// Reader immediately reads back what writer writes.
|
||
|
TEST_F(SyncReadWrites, ConsecutiveReadWrite) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
static constexpr size_t BATCH_SIZE = 10;
|
||
|
uint16_t read_data[DATA_SIZE];
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
|
||
|
// 10 consecutive writes and reads
|
||
|
for (size_t i = 0; i < BATCH_SIZE; i++) {
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
ASSERT_TRUE((manager_.WriteFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE)));
|
||
|
|
||
|
ASSERT_TRUE((manager_.ReadFmq<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, read_data, DATA_SIZE)));
|
||
|
ASSERT_EQ(0, memcmp(write_data, read_data, DATA_SIZE * sizeof(uint16_t)));
|
||
|
}
|
||
|
|
||
|
// no more available to read
|
||
|
size_t reader_available_reads;
|
||
|
ASSERT_TRUE((manager_.AvailableToRead<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, &reader_available_reads)));
|
||
|
ASSERT_EQ(0, reader_available_reads);
|
||
|
}
|
||
|
|
||
|
// Tests reader waiting for data to be available.
|
||
|
// Writer waits for 0.05s and writes the data.
|
||
|
// Reader blocks for at most 0.1s and reads the data if it is available.
|
||
|
TEST_F(BlockingReadWrites, ReadWriteSuccess) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
|
||
|
uint16_t read_data[DATA_SIZE];
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
|
||
|
// initialize data to transfer
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
|
||
|
pid_t pid = fork();
|
||
|
if (pid == 0) { // child process is a reader, blocking for at most 0.1s.
|
||
|
ASSERT_TRUE((manager_.ReadFmqBlocking<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, read_data, DATA_SIZE, 100 * 1000000)));
|
||
|
ASSERT_EQ(0, memcmp(write_data, read_data, DATA_SIZE * sizeof(uint16_t)));
|
||
|
exit(0);
|
||
|
} else if (pid > 0) {
|
||
|
// parent process is a writer, waits for 0.05s and writes.
|
||
|
struct timespec writer_wait_time = {0, 50 * 1000000};
|
||
|
nanosleep(&writer_wait_time, NULL);
|
||
|
ASSERT_TRUE((manager_.WriteFmqBlocking<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE, 1000000)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tests reader blocking times out.
|
||
|
TEST_F(BlockingReadWrites, BlockingTimeOut) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
uint16_t read_data[DATA_SIZE];
|
||
|
|
||
|
// block for 0.05s, timeout
|
||
|
ASSERT_FALSE((manager_.ReadFmqBlocking<uint16_t, kSynchronizedReadWrite>(
|
||
|
"uint16_t", reader_id_, read_data, DATA_SIZE, 50 * 1000000)));
|
||
|
}
|
||
|
|
||
|
// Tests two readers can both read back what writer writes correctly.
|
||
|
TEST_F(UnsynchronizedWrites, ReadWriteSuccess) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
uint16_t read_data1[DATA_SIZE];
|
||
|
uint16_t read_data2[DATA_SIZE];
|
||
|
|
||
|
// initialize data to transfer
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
|
||
|
// writer writes 64 items
|
||
|
ASSERT_TRUE((manager_.WriteFmq<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE)));
|
||
|
|
||
|
// reader 1 reads back data correctly
|
||
|
ASSERT_TRUE((manager_.ReadFmq<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", reader_id1_, read_data1, DATA_SIZE)));
|
||
|
ASSERT_EQ(0, memcmp(write_data, read_data1, DATA_SIZE * sizeof(uint16_t)));
|
||
|
|
||
|
// reader 2 reads back data correctly
|
||
|
ASSERT_TRUE((manager_.ReadFmq<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", reader_id2_, read_data2, DATA_SIZE)));
|
||
|
ASSERT_EQ(0, memcmp(write_data, read_data2, DATA_SIZE * sizeof(uint16_t)));
|
||
|
}
|
||
|
|
||
|
// Tests that blocking is not allowed on unsynchronized queue.
|
||
|
TEST_F(UnsynchronizedWrites, IllegalBlocking) {
|
||
|
static constexpr size_t DATA_SIZE = 64;
|
||
|
uint16_t write_data[DATA_SIZE];
|
||
|
|
||
|
// initialize data to transfer
|
||
|
InitData(write_data, DATA_SIZE);
|
||
|
|
||
|
// should fail immediately, instead of blocking
|
||
|
ASSERT_FALSE((manager_.WriteFmqBlocking<uint16_t, kUnsynchronizedWrite>(
|
||
|
"uint16_t", writer_id_, write_data, DATA_SIZE, 1000000)));
|
||
|
}
|
||
|
|
||
|
} // namespace vts
|
||
|
} // namespace android
|