Add unit tests for imported things from Android emulator
This commit is contained in:
parent
f7cabbf8ff
commit
3529b83753
16 changed files with 1142 additions and 58 deletions
|
|
@ -69,6 +69,10 @@ set(SOURCES
|
||||||
anbox/common/dispatcher.cpp
|
anbox/common/dispatcher.cpp
|
||||||
anbox/common/small_vector.h
|
anbox/common/small_vector.h
|
||||||
anbox/common/type_traits.h
|
anbox/common/type_traits.h
|
||||||
|
anbox/common/message_channel.cpp
|
||||||
|
anbox/common/scope_ptr.h
|
||||||
|
|
||||||
|
anbox/testing/gtest_utils.h
|
||||||
|
|
||||||
anbox/container/service.cpp
|
anbox/container/service.cpp
|
||||||
anbox/container/client.cpp
|
anbox/container/client.cpp
|
||||||
|
|
|
||||||
65
src/anbox/common/message_channel.cpp
Normal file
65
src/anbox/common/message_channel.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
namespace anbox {
|
||||||
|
namespace common {
|
||||||
|
MessageChannelBase::MessageChannelBase(size_t capacity) :
|
||||||
|
pos_(0U),
|
||||||
|
count_(0U),
|
||||||
|
capacity_(capacity),
|
||||||
|
lock_(),
|
||||||
|
can_read_(),
|
||||||
|
can_write_() {}
|
||||||
|
|
||||||
|
MessageChannelBase::~MessageChannelBase() {}
|
||||||
|
|
||||||
|
size_t MessageChannelBase::before_write() {
|
||||||
|
std::unique_lock<std::mutex> l(lock_, std::defer_lock);
|
||||||
|
lock_.lock();
|
||||||
|
|
||||||
|
while (count_ >= capacity_)
|
||||||
|
can_write_.wait(l);
|
||||||
|
|
||||||
|
size_t result = pos_ + count_;
|
||||||
|
if (result >= capacity_)
|
||||||
|
result -= capacity_;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageChannelBase::after_write() {
|
||||||
|
count_++;
|
||||||
|
can_read_.notify_one();
|
||||||
|
lock_.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MessageChannelBase::before_read() {
|
||||||
|
std::unique_lock<std::mutex> l(lock_, std::defer_lock);
|
||||||
|
lock_.lock();
|
||||||
|
while (count_ == 0)
|
||||||
|
can_read_.wait(l);
|
||||||
|
return pos_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageChannelBase::after_read() {
|
||||||
|
if (++pos_ == capacity_)
|
||||||
|
pos_ = 0U;
|
||||||
|
count_--;
|
||||||
|
can_write_.notify_one();
|
||||||
|
lock_.unlock();
|
||||||
|
}
|
||||||
|
} // namespace common
|
||||||
|
} // namespace anbox
|
||||||
99
src/anbox/common/message_channel.h
Normal file
99
src/anbox/common/message_channel.h
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef ANBOX_COMMON_MESSAGE_CHANNEL_H
|
||||||
|
#define ANBOX_COMMON_MESSAGE_CHANNEL_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
namespace anbox {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
// Base non-templated class used to reduce the amount of template
|
||||||
|
// specialization.
|
||||||
|
class MessageChannelBase {
|
||||||
|
public:
|
||||||
|
// Constructor. |capacity| is the buffer capacity in messages.
|
||||||
|
MessageChannelBase(size_t capacity);
|
||||||
|
|
||||||
|
// Destructor.
|
||||||
|
~MessageChannelBase();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Call this method in the sender thread before writing a new message.
|
||||||
|
// This returns the position of the available slot in the message array
|
||||||
|
// where to copy the new fixed-size message. After the copy, call
|
||||||
|
// afterWrite().
|
||||||
|
size_t before_write();
|
||||||
|
|
||||||
|
// To be called after beforeWrite() and copying a new fixed-size message
|
||||||
|
// into the array. This signal the receiver thread that there is a new
|
||||||
|
// incoming message.
|
||||||
|
void after_write();
|
||||||
|
|
||||||
|
// Call this method in the receiver thread before reading a new message.
|
||||||
|
// This returns the position in the message array where the new message
|
||||||
|
// can be read. Caller must process the message, then call afterRead().
|
||||||
|
size_t before_read();
|
||||||
|
|
||||||
|
// To be called in the receiver thread after beforeRead() and processing
|
||||||
|
// the corresponding message.
|
||||||
|
void after_read();
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t pos_;
|
||||||
|
size_t count_;
|
||||||
|
size_t capacity_;
|
||||||
|
std::mutex lock_;
|
||||||
|
std::condition_variable can_read_;
|
||||||
|
std::condition_variable can_write_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper class used to implement an uni-directional IPC channel between
|
||||||
|
// two threads. The channel can be used to send fixed-size messages of type
|
||||||
|
// |T|, with an internal buffer size of |CAPACITY| items. All calls are
|
||||||
|
// blocking.
|
||||||
|
//
|
||||||
|
// Usage is pretty straightforward:
|
||||||
|
//
|
||||||
|
// - From the sender thread, call send(msg);
|
||||||
|
// - From the receiver thread, call receive(&msg);
|
||||||
|
//
|
||||||
|
template <typename T, size_t CAPACITY>
|
||||||
|
class MessageChannel : public MessageChannelBase {
|
||||||
|
public:
|
||||||
|
MessageChannel() : MessageChannelBase(CAPACITY) {}
|
||||||
|
|
||||||
|
void send(const T& msg) {
|
||||||
|
size_t pos = before_write();
|
||||||
|
mItems[pos] = msg;
|
||||||
|
after_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive(T* msg) {
|
||||||
|
size_t pos = before_read();
|
||||||
|
*msg = mItems[pos];
|
||||||
|
after_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T mItems[CAPACITY];
|
||||||
|
};
|
||||||
|
} // namespace common
|
||||||
|
} // namespace anbox
|
||||||
|
|
||||||
|
#endif
|
||||||
98
src/anbox/common/scope_ptr.h
Normal file
98
src/anbox/common/scope_ptr.h
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "anbox/common/type_traits.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace anbox {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
struct FreeDelete {
|
||||||
|
template <class T>
|
||||||
|
void operator()(T ptr) const {
|
||||||
|
free((void*)ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Func>
|
||||||
|
struct FuncDelete {
|
||||||
|
explicit FuncDelete(Func f = {}) : mF(f) {}
|
||||||
|
|
||||||
|
FuncDelete(const FuncDelete& other) = default;
|
||||||
|
FuncDelete(FuncDelete&& other) = default;
|
||||||
|
FuncDelete& operator=(const FuncDelete& other) = default;
|
||||||
|
FuncDelete& operator=(FuncDelete&& other) = default;
|
||||||
|
|
||||||
|
// To be able to copy/move from all compatible template instantiations.
|
||||||
|
template <class U> friend struct FuncDelete;
|
||||||
|
|
||||||
|
// Template constructors and move assignment from compatible instantiations.
|
||||||
|
template <class U>
|
||||||
|
FuncDelete(const FuncDelete<U>& other) : mF(other.mF) {}
|
||||||
|
template <class U>
|
||||||
|
FuncDelete(FuncDelete<U>&& other) : mF(std::move(other.mF)) {}
|
||||||
|
template <class U>
|
||||||
|
FuncDelete& operator=(const FuncDelete<U>& other) {
|
||||||
|
mF = other.mF;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
template <class U>
|
||||||
|
FuncDelete& operator=(FuncDelete<U>&& other) {
|
||||||
|
mF = std::move(other.mF);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the actual deleter call.
|
||||||
|
template <class T>
|
||||||
|
void operator()(T t) const {
|
||||||
|
mF(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Func mF;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class Deleter = std::default_delete<T>>
|
||||||
|
using ScopedPtr = std::unique_ptr<T, Deleter>;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
using ScopedCPtr = std::unique_ptr<T, FreeDelete>;
|
||||||
|
|
||||||
|
template <class T, class Func>
|
||||||
|
using ScopedCustomPtr = std::unique_ptr<T, FuncDelete<Func>>;
|
||||||
|
|
||||||
|
// A factory function that creates a scoped pointer with |deleter|
|
||||||
|
// function used as a deleter - it is called at the scope exit.
|
||||||
|
// Note: enable_if<> limits the scope of allowed arguments to pointers and
|
||||||
|
// std::nullptr_t (to allow makeCustomScopedPtr(nullptr, ...) calls).
|
||||||
|
template <class T,
|
||||||
|
class Func,
|
||||||
|
class = enable_if_c<std::is_same<T, std::nullptr_t>::value ||
|
||||||
|
std::is_pointer<T>::value>>
|
||||||
|
ScopedCustomPtr<
|
||||||
|
typename std::decay<typename std::remove_pointer<T>::type>::type,
|
||||||
|
typename std::decay<Func>::type>
|
||||||
|
makeCustomScopedPtr(T data, Func deleter) {
|
||||||
|
return ScopedCustomPtr<
|
||||||
|
typename std::decay<typename std::remove_pointer<T>::type>::type,
|
||||||
|
typename std::decay<Func>::type>(
|
||||||
|
data, FuncDelete<typename std::decay<Func>::type>(deleter));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace anbox
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
/*
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
|
//
|
||||||
*
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
// you may not use this file except in compliance with the License.
|
||||||
* under the terms of the GNU General Public License version 3, as published
|
// You may obtain a copy of the License at
|
||||||
* by the Free Software Foundation.
|
//
|
||||||
*
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* This program is distributed in the hope that it will be useful, but
|
//
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranties of
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* PURPOSE. See the GNU General Public License for more details.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
*
|
// See the License for the specific language governing permissions and
|
||||||
* You should have received a copy of the GNU General Public License along
|
// limitations under the License.
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "anbox/graphics/buffer_queue.h"
|
#include "anbox/graphics/buffer_queue.h"
|
||||||
|
|
||||||
|
|
@ -22,58 +19,56 @@ namespace graphics {
|
||||||
BufferQueue::BufferQueue(size_t capacity)
|
BufferQueue::BufferQueue(size_t capacity)
|
||||||
: capacity_(capacity), buffers_(new Buffer[capacity]) {}
|
: capacity_(capacity), buffers_(new Buffer[capacity]) {}
|
||||||
|
|
||||||
BufferQueue::Result BufferQueue::try_push_locked(Buffer &&buffer) {
|
int BufferQueue::try_push_locked(Buffer &&buffer) {
|
||||||
if (closed_) {
|
if (closed_)
|
||||||
return Result::Error;
|
return -EIO;
|
||||||
}
|
|
||||||
if (count_ >= capacity_) {
|
if (count_ >= capacity_)
|
||||||
return Result::TryAgain;
|
return -EAGAIN;
|
||||||
}
|
|
||||||
size_t pos = pos_ + count_;
|
size_t pos = pos_ + count_;
|
||||||
if (pos >= capacity_) {
|
if (pos >= capacity_)
|
||||||
pos -= capacity_;
|
pos -= capacity_;
|
||||||
}
|
|
||||||
buffers_[pos] = std::move(buffer);
|
buffers_[pos] = std::move(buffer);
|
||||||
if (count_++ == 0) {
|
if (count_++ == 0)
|
||||||
can_pop_.notify_one();
|
can_pop_.notify_one();
|
||||||
}
|
|
||||||
return Result::Ok;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferQueue::Result BufferQueue::push_locked(
|
int BufferQueue::push_locked(
|
||||||
Buffer &&buffer, std::unique_lock<std::mutex> &lock) {
|
Buffer &&buffer, std::unique_lock<std::mutex> &lock) {
|
||||||
while (count_ == capacity_) {
|
while (count_ == capacity_) {
|
||||||
if (closed_) {
|
if (closed_)
|
||||||
return Result::Error;
|
return -EIO;
|
||||||
}
|
|
||||||
can_push_.wait(lock);
|
can_push_.wait(lock);
|
||||||
}
|
}
|
||||||
return try_push_locked(std::move(buffer));
|
return try_push_locked(std::move(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferQueue::Result BufferQueue::try_pop_locked(Buffer *buffer) {
|
int BufferQueue::try_pop_locked(Buffer *buffer) {
|
||||||
if (count_ == 0) {
|
if (count_ == 0)
|
||||||
return closed_ ? Result::Error : Result::TryAgain;
|
return closed_ ? -EIO : -EAGAIN;
|
||||||
}
|
|
||||||
*buffer = std::move(buffers_[pos_]);
|
*buffer = std::move(buffers_[pos_]);
|
||||||
size_t pos = pos_ + 1;
|
size_t pos = pos_ + 1;
|
||||||
if (pos >= capacity_) {
|
if (pos >= capacity_)
|
||||||
pos -= capacity_;
|
pos -= capacity_;
|
||||||
}
|
|
||||||
pos_ = pos;
|
pos_ = pos;
|
||||||
if (count_-- == capacity_) {
|
if (count_-- == capacity_)
|
||||||
can_push_.notify_one();
|
can_push_.notify_one();
|
||||||
}
|
|
||||||
return Result::Ok;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferQueue::Result BufferQueue::pop_locked(
|
int BufferQueue::pop_locked(
|
||||||
Buffer *buffer, std::unique_lock<std::mutex> &lock) {
|
Buffer *buffer, std::unique_lock<std::mutex> &lock) {
|
||||||
while (count_ == 0) {
|
while (count_ == 0) {
|
||||||
if (closed_) {
|
if (closed_)
|
||||||
// Closed queue is empty.
|
// Closed queue is empty.
|
||||||
return Result::Error;
|
return -EIO;
|
||||||
}
|
|
||||||
can_pop_.wait(lock);
|
can_pop_.wait(lock);
|
||||||
}
|
}
|
||||||
return try_pop_locked(buffer);
|
return try_pop_locked(buffer);
|
||||||
|
|
|
||||||
|
|
@ -30,18 +30,12 @@ using Buffer = anbox::common::SmallFixedVector<char, 512>;
|
||||||
|
|
||||||
class BufferQueue {
|
class BufferQueue {
|
||||||
public:
|
public:
|
||||||
enum class Result {
|
|
||||||
Ok = 0,
|
|
||||||
TryAgain = 1,
|
|
||||||
Error = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
BufferQueue(size_t capacity);
|
BufferQueue(size_t capacity);
|
||||||
|
|
||||||
Result try_push_locked(Buffer &&buffer);
|
int try_push_locked(Buffer &&buffer);
|
||||||
Result push_locked(Buffer &&buffer, std::unique_lock<std::mutex> &lock);
|
int push_locked(Buffer &&buffer, std::unique_lock<std::mutex> &lock);
|
||||||
Result try_pop_locked(Buffer *buffer);
|
int try_pop_locked(Buffer *buffer);
|
||||||
Result pop_locked(Buffer *buffer, std::unique_lock<std::mutex> &lock);
|
int pop_locked(Buffer *buffer, std::unique_lock<std::mutex> &lock);
|
||||||
void close_locked();
|
void close_locked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -72,13 +72,13 @@ const unsigned char *BufferedIOStream::read(void *buf, size_t *inout_len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blocking = (count == 0);
|
bool blocking = (count == 0);
|
||||||
auto result = BufferQueue::Result::Error;
|
auto result = -EIO;
|
||||||
if (blocking)
|
if (blocking)
|
||||||
result = in_queue_.pop_locked(&read_buffer_, l);
|
result = in_queue_.pop_locked(&read_buffer_, l);
|
||||||
else
|
else
|
||||||
result = in_queue_.try_pop_locked(&read_buffer_);
|
result = in_queue_.try_pop_locked(&read_buffer_);
|
||||||
|
|
||||||
if (result == BufferQueue::Result::Ok) {
|
if (result == 0) {
|
||||||
read_buffer_left_ = read_buffer_.size();
|
read_buffer_left_ = read_buffer_.size();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ void BufferedIOStream::thread_main() {
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
auto result = out_queue_.pop_locked(&buffer, l);
|
auto result = out_queue_.pop_locked(&buffer, l);
|
||||||
if (result == BufferQueue::Result::Error) break;
|
if (result != 0 && result != -EAGAIN) break;
|
||||||
|
|
||||||
auto bytes_left = buffer.size();
|
auto bytes_left = buffer.size();
|
||||||
while (bytes_left > 0) {
|
while (bytes_left > 0) {
|
||||||
|
|
|
||||||
62
src/anbox/testing/gtest_utils.h
Normal file
62
src/anbox/testing/gtest_utils.h
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "anbox/common/type_traits.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
// Miscellenaous helper declarations for unit-tests using the GoogleTest
|
||||||
|
// framework.
|
||||||
|
|
||||||
|
namespace anbox {
|
||||||
|
namespace testing {
|
||||||
|
// RangesMatch is a useful template used to compare the content of two
|
||||||
|
// ranges at runtime. Usage is simply:
|
||||||
|
//
|
||||||
|
// EXPECT_TRUE(RangesMatch(range1, range2);
|
||||||
|
//
|
||||||
|
// Where |range1| and |range2| must have the same item type, and size.
|
||||||
|
template <typename Range1,
|
||||||
|
typename Range2,
|
||||||
|
typename = common::enable_if_c<common::is_range<Range1>::value &&
|
||||||
|
common::is_range<Range2>::value>>
|
||||||
|
inline ::testing::AssertionResult RangesMatch(const Range1& expected,
|
||||||
|
const Range2& actual) {
|
||||||
|
const auto expectedSize =
|
||||||
|
std::distance(std::begin(expected), std::end(expected));
|
||||||
|
const auto actualSize = std::distance(std::begin(actual), std::end(actual));
|
||||||
|
if (actualSize != expectedSize) {
|
||||||
|
return ::testing::AssertionFailure()
|
||||||
|
<< "actual range size " << actualSize << " != expected size "
|
||||||
|
<< expectedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itExp = std::begin(expected);
|
||||||
|
for (const auto& act : actual) {
|
||||||
|
if (*itExp != act) {
|
||||||
|
const auto index = std::distance(std::begin(expected), itExp);
|
||||||
|
return ::testing::AssertionFailure()
|
||||||
|
<< "range[" << index << "] (" << act << ") != expected["
|
||||||
|
<< index << "] (" << *itExp << ")";
|
||||||
|
}
|
||||||
|
++itExp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::testing::AssertionSuccess();
|
||||||
|
}
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace anbox
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
add_subdirectory(support)
|
add_subdirectory(support)
|
||||||
|
add_subdirectory(common)
|
||||||
|
add_subdirectory(graphics)
|
||||||
|
|
|
||||||
4
tests/anbox/common/CMakeLists.txt
Normal file
4
tests/anbox/common/CMakeLists.txt
Normal 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)
|
||||||
94
tests/anbox/common/message_channel_tests.cpp
Normal file
94
tests/anbox/common/message_channel_tests.cpp
Normal 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
|
||||||
65
tests/anbox/common/scope_ptr_tests.cpp
Normal file
65
tests/anbox/common/scope_ptr_tests.cpp
Normal 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
|
||||||
219
tests/anbox/common/small_vector_tests.cpp
Normal file
219
tests/anbox/common/small_vector_tests.cpp
Normal 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
|
||||||
84
tests/anbox/common/type_traits_tests.cpp
Normal file
84
tests/anbox/common/type_traits_tests.cpp
Normal 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
|
||||||
1
tests/anbox/graphics/CMakeLists.txt
Normal file
1
tests/anbox/graphics/CMakeLists.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ANBOX_ADD_TEST(buffer_queue_tests buffer_queue_tests.cpp)
|
||||||
298
tests/anbox/graphics/buffer_queue_tests.cpp
Normal file
298
tests/anbox/graphics/buffer_queue_tests.cpp
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue