Add unit tests for imported things from Android emulator

This commit is contained in:
Simon Fels 2016-12-21 13:10:24 +01:00
commit 3529b83753
16 changed files with 1142 additions and 58 deletions

View file

@ -1 +1,3 @@
add_subdirectory(support)
add_subdirectory(common)
add_subdirectory(graphics)

View file

@ -0,0 +1,4 @@
ANBOX_ADD_TEST(message_channel_tests message_channel_tests.cpp)
ANBOX_ADD_TEST(small_vector_tests small_vector_tests.cpp)
ANBOX_ADD_TEST(type_traits_tests type_traits_tests.cpp)
ANBOX_ADD_TEST(scope_ptr_tests scope_ptr_tests.cpp)

View file

@ -0,0 +1,94 @@
// Copyright 2014 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 "anbox/common/message_channel.h"
#include <gtest/gtest.h>
#include <string>
#include <thread>
namespace anbox {
namespace common {
TEST(MessageChannel, SingleThreadWithInt) {
MessageChannel<int, 3U> channel;
channel.send(1);
channel.send(2);
channel.send(3);
int ret;
channel.receive(&ret);
EXPECT_EQ(1, ret);
channel.receive(&ret);
EXPECT_EQ(2, ret);
channel.receive(&ret);
EXPECT_EQ(3, ret);
}
TEST(MessageChannel, SingleThreadWithStdString) {
MessageChannel<std::string, 5U> channel;
channel.send(std::string("foo"));
channel.send(std::string("bar"));
channel.send(std::string("zoo"));
std::string str;
channel.receive(&str);
EXPECT_STREQ("foo", str.c_str());
channel.receive(&str);
EXPECT_STREQ("bar", str.c_str());
channel.receive(&str);
EXPECT_STREQ("zoo", str.c_str());
}
TEST(MessageChannel, TwoThreadsPingPong) {
struct PingPongState {
MessageChannel<std::string, 3U> in;
MessageChannel<std::string, 3U> out;
};
PingPongState state;
std::thread thread([&](){
for (;;) {
std::string str;
state.in.receive(&str);
state.out.send(str);
if (str == "quit")
break;
}
return 0;
});
std::string str;
const size_t kCount = 100;
for (size_t n = 0; n < kCount; ++n) {
state.in.send(std::string("foo"));
state.in.send(std::string("bar"));
state.in.send(std::string("zoo"));
state.out.receive(&str);
EXPECT_STREQ("foo", str.c_str());
state.out.receive(&str);
EXPECT_STREQ("bar", str.c_str());
state.out.receive(&str);
EXPECT_STREQ("zoo", str.c_str());
}
state.in.send(std::string("quit"));
state.out.receive(&str);
EXPECT_STREQ("quit", str.c_str());
thread.join();
}
} // namespace common
} // namespace anbox

View file

@ -0,0 +1,65 @@
// 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 "anbox/common/scope_ptr.h"
#include <gtest/gtest.h>
#include <type_traits>
#include <utility>
namespace anbox {
namespace common {
TEST(ScopedPtr, FuncDelete_conversions) {
// This test makes sure the code compiles, it doesn't do any runtime checks.
auto lambda = [](int i) { return i; };
FuncDelete<decltype(lambda)> lambdaFd(lambda);
// unary + converts a captureless lambda into a raw function pointer
FuncDelete<decltype(+lambda)> funcFd1(+lambda);
// copy ctor
FuncDelete<decltype(+lambda)> funcFd2(funcFd1);
// assignment operator
funcFd2 = funcFd1;
// move operator
funcFd2 = std::move(funcFd1);
// conversion ctor
FuncDelete<decltype(+lambda)> funcFd3(lambdaFd);
// conversion move ctor
FuncDelete<decltype(+lambda)> funcFd4(std::move(lambdaFd));
// conversion assignment
funcFd3 = lambdaFd;
// conversion move
funcFd3 = std::move(lambdaFd);
}
TEST(ScopedPtr, makeCustomScopedPtr_fromLambda) {
auto freeAsLambda = [](void* ptr) { free(ptr); };
auto ptr1 = makeCustomScopedPtr(malloc(1), freeAsLambda);
ScopedPtr<void, FuncDelete<void(*)(void*)>> ptr2 =
makeCustomScopedPtr(malloc(1), freeAsLambda);
ScopedPtr<void, FuncDelete<void(*)(void*)>> ptr3 =
makeCustomScopedPtr(malloc(1), +freeAsLambda);
static_assert(!std::is_same<decltype(ptr1), decltype(ptr2)>::value,
"Custom ScopedPtr<> from a lambda expression type may not "
"be the same as with a function pointer");
}
} // namespace common
} // namespace anbox

View file

@ -0,0 +1,219 @@
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "anbox/common/small_vector.h"
#include "anbox/common/scope_ptr.h"
#include "anbox/testing/gtest_utils.h"
#include <gtest/gtest.h>
#include <string>
namespace anbox {
namespace common {
TEST(SmallVector, basic) {
SmallFixedVector<char, 10> sv;
EXPECT_EQ(sv.size(), 0);
EXPECT_TRUE(sv.empty());
EXPECT_EQ(10, sv.capacity());
EXPECT_FALSE(sv.isAllocated());
const int values[] = {1, 2, 3};
sv = SmallFixedVector<char, 10>(values, values + 3);
EXPECT_EQ(3, sv.size());
EXPECT_FALSE(sv.empty());
EXPECT_EQ(10, sv.capacity());
EXPECT_FALSE(sv.isAllocated());
EXPECT_TRUE(anbox::testing::RangesMatch(values, sv));
sv.clear();
EXPECT_EQ(0, sv.size());
EXPECT_TRUE(sv.empty());
const char str[] = "this is a long string for insertion";
sv = SmallFixedVector<char, 10>(str);
EXPECT_EQ(sizeof(str), sv.size());
EXPECT_GT(sv.capacity(), 10);
EXPECT_TRUE(sv.isAllocated());
EXPECT_TRUE(anbox::testing::RangesMatch(str, sv));
// make sure explicit loops over indices and iterators work
for (size_t i = 0; i != sv.size(); ++i) {
EXPECT_EQ(str[i], sv[i]);
}
const char* c = str;
for (auto i : sv) {
EXPECT_EQ(*c++, i);
}
c = str;
for (auto it = sv.begin(); it != sv.end(); ++it, ++c) {
EXPECT_EQ(*c, *it);
}
c = str;
for (auto it = sv.data(); it != sv.data() + sv.size(); ++it, ++c) {
EXPECT_EQ(*c, *it);
}
}
TEST(SmallVector, ctor) {
{
SmallFixedVector<int, 1> sv;
EXPECT_EQ(sv.size(), 0);
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv(values, values + 3);
EXPECT_TRUE(anbox::testing::RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv(values);
EXPECT_TRUE(anbox::testing::RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv(values);
EXPECT_TRUE(anbox::testing::RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv = {1, 2, 3};
EXPECT_TRUE(anbox::testing::RangesMatch(values, sv));
}
{
const int values[] = {1, 2, 3};
SmallFixedVector<int, 10> sv1(values);
SmallFixedVector<int, 10> sv2(sv1);
EXPECT_TRUE(anbox::testing::RangesMatch(values, sv1));
EXPECT_TRUE(anbox::testing::RangesMatch(sv2, sv1));
SmallFixedVector<int, 10> sv3(std::move(sv1));
EXPECT_TRUE(anbox::testing::RangesMatch(sv3, values));
// don't check |sv1| - it is not required to be empty.
}
}
TEST(SmallVector, dtor) {
// Count all destructor calls for the elements and make sure they're called
// enough times.
int destructedTimes = 0;
auto deleter = [](int* p) { ++(*p); };
auto item1 = makeCustomScopedPtr(&destructedTimes, deleter);
auto item2 = makeCustomScopedPtr(&destructedTimes, deleter);
auto item3 = makeCustomScopedPtr(&destructedTimes, deleter);
{
SmallFixedVector<decltype(item1), 1> sv;
sv.emplace_back(std::move(item1));
sv.emplace_back(std::move(item2));
// this one is already empty, so it won't add to |destructedTimes|.
sv.emplace_back(std::move(item2));
sv.emplace_back(std::move(item3));
}
EXPECT_EQ(3, destructedTimes);
}
TEST(SmallVector, modifiers) {
SmallFixedVector<unsigned, 5> sv;
EXPECT_EQ(0, sv.size());
EXPECT_EQ(5, sv.capacity());
sv.reserve(4);
EXPECT_EQ(0, sv.size());
EXPECT_EQ(5, sv.capacity());
EXPECT_FALSE(sv.isAllocated());
sv.reserve(6);
EXPECT_EQ(0, sv.size());
EXPECT_EQ(6, sv.capacity());
EXPECT_TRUE(sv.isAllocated());
sv.resize(3);
EXPECT_EQ(3, sv.size());
EXPECT_EQ(6, sv.capacity());
EXPECT_TRUE(sv.isAllocated());
sv.resize(10);
EXPECT_EQ(10, sv.size());
EXPECT_GE(sv.capacity(), 10);
EXPECT_TRUE(sv.isAllocated());
sv.push_back(1);
EXPECT_EQ(11, sv.size());
EXPECT_GE(sv.capacity(), 11);
sv.emplace_back(2);
EXPECT_EQ(12, sv.size());
EXPECT_GE(sv.capacity(), 12);
sv.clear();
EXPECT_EQ(0, sv.size());
EXPECT_GE(sv.capacity(), 12);
// resize_noinit() doesn't really have anything specific we can test
// compared to resize()
sv.resize_noinit(1);
EXPECT_EQ(1, sv.size());
EXPECT_GE(sv.capacity(), 12);
sv.resize_noinit(100);
EXPECT_EQ(100, sv.size());
EXPECT_GE(sv.capacity(), 100);
SmallFixedVector<std::string, 5> strings = {"a", "b", "c"};
strings.emplace_back("d");
strings.push_back("e");
EXPECT_EQ(5, strings.size());
EXPECT_EQ(5, strings.capacity());
EXPECT_FALSE(strings.isAllocated());
strings.push_back(std::string("e"));
EXPECT_EQ(6, strings.size());
EXPECT_GE(strings.capacity(), 6);
EXPECT_TRUE(strings.isAllocated());
}
TEST(SmallVector, useThroughInterface) {
SmallFixedVector<int, 10> sfv = {1, 2, 3};
SmallVector<int>& sv = sfv;
EXPECT_TRUE(anbox::testing::RangesMatch(sv, sfv));
EXPECT_EQ(sv.isAllocated(), sfv.isAllocated());
sv.reserve(20);
EXPECT_TRUE(sv.isAllocated());
EXPECT_EQ(sv.isAllocated(), sfv.isAllocated());
// now make sure that deleting through base class cleans up the memory
{
int destructedTimes = 0;
auto deleter = [](int* p) { ++(*p); };
auto item1 = makeCustomScopedPtr(&destructedTimes, deleter);
auto item2 = makeCustomScopedPtr(&destructedTimes, deleter);
SmallVector<decltype(item1)>* sv =
new SmallFixedVector<decltype(item1), 1>();
sv->push_back(std::move(item1));
sv->emplace_back(std::move(item2));
delete sv;
EXPECT_EQ(2, destructedTimes);
}
}
} // namespace common
} // namespace anbox

View file

@ -0,0 +1,84 @@
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "anbox/common/type_traits.h"
#include <gtest/gtest.h>
#include <array>
#include <functional>
#include <list>
#include <vector>
namespace anbox {
namespace common {
TEST(TypeTraits, IsCallable) {
class C;
C* c = nullptr;
auto lambda = [c](bool) -> C* { return nullptr; };
static_assert(is_callable_as<void(), void()>::value, "simple function");
static_assert(is_callable_as<void(&)(), void()>::value, "function reference");
static_assert(is_callable_as<void(*)(), void()>::value, "function pointer");
static_assert(is_callable_as<int(C&, C*), int(C&, C*)>::value,
"function with arguments and return type");
static_assert(is_callable_as<decltype(lambda), C*(bool)>::value, "lambda");
static_assert(is_callable_as<std::function<bool(int)>, bool(int)>::value,
"std::function");
static_assert(!is_callable_as<int, void()>::value, "int should not be callable");
static_assert(!is_callable_as<C, void()>::value, "incomplete type");
static_assert(!is_callable_as<void(), void(int)>::value, "different arguments");
static_assert(!is_callable_as<int(), void()>::value, "different return types");
static_assert(!is_callable_as<int(), short()>::value,
"slightly different return types");
static_assert(!is_callable_as<int(int), int(int, int)>::value,
"more arguments");
static_assert(!is_callable_as<int(int, int), int(int)>::value,
"less arguments");
static_assert(!is_callable_as<int(int), int>::value,
"bad required signature");
}
TEST(TypeTraits, IsTemplateInstantiation) {
static_assert(!is_template_instantiation_of<int, std::vector>::value,
"int is not an instance of vector");
static_assert(!is_template_instantiation_of<std::list<std::vector<int>>, std::vector>::value,
"list is not an instance of vector");
static_assert(is_template_instantiation_of<std::vector<int>, std::vector>::value,
"std::vector<int> is an instance of vector");
static_assert(is_template_instantiation_of<std::vector<std::vector<std::vector<int>>>, std::vector>::value,
"nested std::vector<> is an instance of vector");
}
TEST(TypeTraits, IsRange) {
static_assert(is_range<std::vector<int>>::value,
"vector<> should be detected as a range");
static_assert(is_range<const std::list<std::function<void()>>>::value,
"const list<> should be detected as a range");
static_assert(is_range<std::array<std::vector<int>, 10>>::value,
"array<> should be detected as a range");
char arr[100];
static_assert(is_range<decltype(arr)>::value,
"C array should be detected as a range");
static_assert(is_range<decltype("string")>::value,
"String literal should be detected as a range");
static_assert(!is_range<int>::value, "int shouldn't be a range");
static_assert(!is_range<int*>::value, "int* shouldn't be a range");
static_assert(!is_range<const int*>::value,
"even const int* shouldn't be a range");
}
} // namespace common
} // namespace anbox

View file

@ -0,0 +1 @@
ANBOX_ADD_TEST(buffer_queue_tests buffer_queue_tests.cpp)

View file

@ -0,0 +1,298 @@
// 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 "anbox/graphics/buffer_queue.h"
#include "anbox/common/message_channel.h"
#include "anbox/logger.h"
#include <gtest/gtest.h>
#include <thread>
namespace anbox {
namespace graphics {
TEST(BufferQueue, Constructor) {
BufferQueue queue(16);
}
TEST(BufferQueue, TryPushLocked) {
BufferQueue queue(2);
EXPECT_EQ(0, queue.try_push_locked(Buffer("Hello")));
EXPECT_EQ(0, queue.try_push_locked(Buffer("World")));
Buffer buff0("You Shall Not Move");
EXPECT_EQ(-EAGAIN, queue.try_push_locked(std::move(buff0)));
EXPECT_FALSE(buff0.empty()) << "Buffer should not be moved on failure!";
}
TEST(BufferQueue, TryPushLockedOnClosedQueue) {
BufferQueue queue(2);
EXPECT_EQ(0, queue.try_push_locked(Buffer("Hello")));
// Closing the queue prevents pushing new items to the queue.
queue.close_locked();
EXPECT_EQ(-EIO, queue.try_push_locked(Buffer("World")));
}
TEST(BufferQueue, TryPopLocked) {
BufferQueue queue(2);;
Buffer buffer;
EXPECT_EQ(-EAGAIN, queue.try_pop_locked(&buffer));
EXPECT_EQ(0, queue.try_push_locked(Buffer("Hello")));
EXPECT_EQ(0, queue.try_push_locked(Buffer("World")));
EXPECT_EQ(0, queue.try_pop_locked(&buffer));
EXPECT_STREQ("Hello", buffer.data());
EXPECT_EQ(0, queue.try_pop_locked(&buffer));
EXPECT_STREQ("World", buffer.data());
EXPECT_EQ(-EAGAIN, queue.try_pop_locked(&buffer));
EXPECT_STREQ("World", buffer.data());
}
TEST(BufferQueue, TryPopLockedOnClosedQueue) {
BufferQueue queue(2);
Buffer buffer;
EXPECT_EQ(-EAGAIN, queue.try_pop_locked(&buffer));
EXPECT_EQ(0, queue.try_push_locked(Buffer("Hello")));
EXPECT_EQ(0, queue.try_push_locked(Buffer("World")));
EXPECT_EQ(0, queue.try_pop_locked(&buffer));
EXPECT_STREQ("Hello", buffer.data());
// Closing the queue doesn't prevent popping existing items, but
// will generate -EIO once it is empty.
queue.close_locked();
EXPECT_EQ(0, queue.try_pop_locked(&buffer));
EXPECT_STREQ("World", buffer.data());
EXPECT_EQ(-EIO, queue.try_pop_locked(&buffer));
EXPECT_STREQ("World", buffer.data());
}
namespace {
// A TestThread instance that holds a reference to a queue and can either
// push or pull to it, on command from another thread. This uses a
// MessageChannel to implement the communication channel between the
// command thread and this one.
class TestThread final {
public:
TestThread(std::mutex &lock, BufferQueue &queue) :
lock_(lock),
queue_(queue) {
}
bool start() {
thread_ = std::thread(&TestThread::thread_main, this);
return true;
}
// Tell the test thread to push |buffer| to the queue.
// Call endPush() later to get the command's result.
bool start_push(Buffer&& buffer) {
input_.send(Request{Cmd::Push, std::move(buffer)});
return true;
}
int end_push() {
Reply reply = {};
output_.receive(&reply);
return reply.result;
}
// Tell the test thread to pop a buffer from the queue.
// Call end_pop() to get the command's result, as well as the popped
// buffer if it is 0.
bool start_pop() {
input_.send(Request{Cmd::Pop});
return true;
}
// Return the result of a previous start_pop() command. If result is
// 0, sets |*buffer| to the result buffer.
int end_pop(Buffer* buffer) {
Reply reply = {};
output_.receive(&reply);
if (reply.result == 0)
*buffer = std::move(reply.buffer);
return reply.result;
}
// Tell the test thread to close the queue from its side.
void do_close() {
input_.send(Request{Cmd::Close});
}
// Tell the test thread to stop after completing its current command.
void stop() {
input_.send(Request{Cmd::Stop});
thread_.join();
}
private:
enum class Cmd {
Push,
Pop,
Close,
Stop,
};
struct Request {
Cmd cmd;
Buffer buffer;
};
struct Reply {
int result;
Buffer buffer;
};
void thread_main() {
while (true) {
Request r;
input_.receive(&r);
if (r.cmd == Cmd::Stop)
break;
std::unique_lock<std::mutex> l(lock_);
Reply reply = {};
bool sendReply = false;
switch (r.cmd) {
case Cmd::Push:
reply.result = queue_.push_locked(std::move(r.buffer), l);
sendReply = true;
break;
case Cmd::Pop:
reply.result = queue_.pop_locked(&reply.buffer, l);
sendReply = true;
break;
case Cmd::Close:
queue_.close_locked();
break;
default:
;
}
if (sendReply)
output_.send(std::move(reply));
}
}
std::thread thread_;
std::mutex &lock_;
BufferQueue &queue_;
anbox::common::MessageChannel<Request, 4> input_;
anbox::common::MessageChannel<Reply, 4> output_;
};
} // namespace
TEST(BufferQueue, PushLocked) {
std::mutex lock;
BufferQueue queue(2);
TestThread thread(lock, queue);
ASSERT_TRUE(thread.start());
ASSERT_TRUE(thread.start_pop());
std::unique_lock<std::mutex> l(lock);
EXPECT_EQ(0, queue.push_locked(Buffer("Hello"), l));
EXPECT_EQ(0, queue.push_locked(Buffer("World"), l));
EXPECT_EQ(0, queue.push_locked(Buffer("Foo"), l));
thread.stop();
}
TEST(BufferQueue, PushLockedWithClosedQueue) {
std::mutex lock;
BufferQueue queue(2);
TestThread thread(lock, queue);
ASSERT_TRUE(thread.start());
{
std::unique_lock<std::mutex> l(lock);
EXPECT_EQ(0, queue.push_locked(Buffer("Hello"), l));
// Closing the queue prevents pushing new items, but not
// pulling from the queue.
queue.close_locked();
EXPECT_EQ(-EIO, queue.push_locked(Buffer("World"), l));
}
Buffer buffer;
ASSERT_TRUE(thread.start_pop());
EXPECT_EQ(0, thread.end_pop(&buffer));
EXPECT_STREQ("Hello", buffer.data());
thread.stop();
}
TEST(BufferQueue, PopLocked) {
std::mutex lock;
BufferQueue queue(2);
TestThread thread(lock, queue);
ASSERT_TRUE(thread.start());
ASSERT_TRUE(thread.start_push(Buffer("Hello World")));
EXPECT_EQ(0, thread.end_push());
{
std::unique_lock<std::mutex> l(lock);
Buffer buffer;
EXPECT_EQ(0, queue.pop_locked(&buffer, l));
EXPECT_STREQ("Hello World", buffer.data());
}
thread.stop();
}
TEST(BufferQueue, PopLockedWithClosedQueue) {
std::mutex lock;
BufferQueue queue(2);
TestThread thread(lock, queue);
ASSERT_TRUE(thread.start());
ASSERT_TRUE(thread.start_push(Buffer("Hello World")));
EXPECT_EQ(0, thread.end_push());
// Closing the queue shall not prevent pulling items from it.
// After that, -EIO shall be returned.
thread.do_close();
ASSERT_TRUE(thread.start_push(Buffer("Foo Bar")));
EXPECT_EQ(-EIO, thread.end_push());
{
std::unique_lock<std::mutex> l(lock);
Buffer buffer;
EXPECT_EQ(0, queue.pop_locked(&buffer, l));
EXPECT_STREQ("Hello World", buffer.data());
EXPECT_EQ(-EIO, queue.pop_locked(&buffer, l));
EXPECT_STREQ("Hello World", buffer.data());
}
thread.stop();
}
} // namespace graphics
} // namespace anbox