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/small_vector.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/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 Simon Fels <morphis@gravedo.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 3, as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
// 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"
|
||||
|
||||
|
|
@ -22,58 +19,56 @@ namespace graphics {
|
|||
BufferQueue::BufferQueue(size_t capacity)
|
||||
: capacity_(capacity), buffers_(new Buffer[capacity]) {}
|
||||
|
||||
BufferQueue::Result BufferQueue::try_push_locked(Buffer &&buffer) {
|
||||
if (closed_) {
|
||||
return Result::Error;
|
||||
}
|
||||
if (count_ >= capacity_) {
|
||||
return Result::TryAgain;
|
||||
}
|
||||
int BufferQueue::try_push_locked(Buffer &&buffer) {
|
||||
if (closed_)
|
||||
return -EIO;
|
||||
|
||||
if (count_ >= capacity_)
|
||||
return -EAGAIN;
|
||||
|
||||
size_t pos = pos_ + count_;
|
||||
if (pos >= capacity_) {
|
||||
if (pos >= capacity_)
|
||||
pos -= capacity_;
|
||||
}
|
||||
|
||||
buffers_[pos] = std::move(buffer);
|
||||
if (count_++ == 0) {
|
||||
if (count_++ == 0)
|
||||
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) {
|
||||
while (count_ == capacity_) {
|
||||
if (closed_) {
|
||||
return Result::Error;
|
||||
}
|
||||
if (closed_)
|
||||
return -EIO;
|
||||
can_push_.wait(lock);
|
||||
}
|
||||
return try_push_locked(std::move(buffer));
|
||||
}
|
||||
|
||||
BufferQueue::Result BufferQueue::try_pop_locked(Buffer *buffer) {
|
||||
if (count_ == 0) {
|
||||
return closed_ ? Result::Error : Result::TryAgain;
|
||||
}
|
||||
int BufferQueue::try_pop_locked(Buffer *buffer) {
|
||||
if (count_ == 0)
|
||||
return closed_ ? -EIO : -EAGAIN;
|
||||
|
||||
*buffer = std::move(buffers_[pos_]);
|
||||
size_t pos = pos_ + 1;
|
||||
if (pos >= capacity_) {
|
||||
if (pos >= capacity_)
|
||||
pos -= capacity_;
|
||||
}
|
||||
|
||||
pos_ = pos;
|
||||
if (count_-- == capacity_) {
|
||||
if (count_-- == capacity_)
|
||||
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) {
|
||||
while (count_ == 0) {
|
||||
if (closed_) {
|
||||
if (closed_)
|
||||
// Closed queue is empty.
|
||||
return Result::Error;
|
||||
}
|
||||
return -EIO;
|
||||
can_pop_.wait(lock);
|
||||
}
|
||||
return try_pop_locked(buffer);
|
||||
|
|
|
|||
|
|
@ -30,18 +30,12 @@ using Buffer = anbox::common::SmallFixedVector<char, 512>;
|
|||
|
||||
class BufferQueue {
|
||||
public:
|
||||
enum class Result {
|
||||
Ok = 0,
|
||||
TryAgain = 1,
|
||||
Error = 2,
|
||||
};
|
||||
|
||||
BufferQueue(size_t capacity);
|
||||
|
||||
Result try_push_locked(Buffer &&buffer);
|
||||
Result push_locked(Buffer &&buffer, std::unique_lock<std::mutex> &lock);
|
||||
Result try_pop_locked(Buffer *buffer);
|
||||
Result pop_locked(Buffer *buffer, std::unique_lock<std::mutex> &lock);
|
||||
int try_push_locked(Buffer &&buffer);
|
||||
int push_locked(Buffer &&buffer, std::unique_lock<std::mutex> &lock);
|
||||
int try_pop_locked(Buffer *buffer);
|
||||
int pop_locked(Buffer *buffer, std::unique_lock<std::mutex> &lock);
|
||||
void close_locked();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -72,13 +72,13 @@ const unsigned char *BufferedIOStream::read(void *buf, size_t *inout_len) {
|
|||
}
|
||||
|
||||
bool blocking = (count == 0);
|
||||
auto result = BufferQueue::Result::Error;
|
||||
auto result = -EIO;
|
||||
if (blocking)
|
||||
result = in_queue_.pop_locked(&read_buffer_, l);
|
||||
else
|
||||
result = in_queue_.try_pop_locked(&read_buffer_);
|
||||
|
||||
if (result == BufferQueue::Result::Ok) {
|
||||
if (result == 0) {
|
||||
read_buffer_left_ = read_buffer_.size();
|
||||
continue;
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ void BufferedIOStream::thread_main() {
|
|||
|
||||
Buffer buffer;
|
||||
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();
|
||||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue