Skip to content

Commit

Permalink
Add TestObservable class;
Browse files Browse the repository at this point in the history
This class provide testing of invariants on observable's subscriptions
  • Loading branch information
muratovv committed Jul 23, 2017
1 parent b6a93fb commit 3991804
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 1 deletion.
190 changes: 190 additions & 0 deletions libs/common/test_observable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/**
* Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved.
* http://soramitsu.co.jp
*
* 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 IROHA_TEST_OBSERVABLE_HPP
#define IROHA_TEST_OBSERVABLE_HPP

#include <rxcpp/rx.hpp>
#include <functional>
#include <memory>
#include <utility>

namespace common {
namespace test_observable {

/**
* Interface of testing verification uses in test subscriber;
* @tparam T type of observed data
*
* For implement own strategies should:
* 1) inherit from this class
* 2) implement move constructor and move operator=
* 3) deny copy constructor and operator=
* 4) implement on_next_* handlers
* 5) implement validate() method
*/
template<typename T>
class VerificationStrategy {
public:

/**
* Handler that called before providing value to target subscriber
* @param next - new value
*/
virtual void on_next_before(T next) {
};

/**
* Handler that called after calling target subscriber
* @param next - new value
*/
virtual void on_next_after(T next) {
};

/**
* Implement destructor for verify invariant
*/
virtual ~VerificationStrategy() noexcept(false) {

};

/**
* validation function for invariant
* @return true if invariant safe, otherwice false
*/
virtual bool validate() = 0;
protected:

/**
* Exception reason
*/
std::string invalidate_reason_ = "empty_reason";
};

/**
* TestObservable class provide wrapper for observable
* @tparam T type of data in wrapped observable
*/
template<typename T>
class TestObservable {
public:

/**
* Constructor for wrapping observable for checking invariant
* @param unwrapped_observable - object for wrapping
*/
TestObservable(rxcpp::observable<T> unwrapped_observable)
: unwrapped_(unwrapped_observable) {
};

/**
* Method provide subscription
* for wrapped observable with checking invariant.
* @param strategy - invariant for validation
* @param subscriber - business logic subscriber
*/
void test_subscriber(std::unique_ptr<VerificationStrategy<T>> strategy,
std::function<void(T val)> subscriber) {
strategy_ = std::move(strategy);
unwrapped_.subscribe([this, subscriber](T val) {
// verify before invariant
this->strategy_->on_next_before(val);

// invoke subscriber
subscriber(val);

// verify after invariant
this->strategy_->on_next_after(val);
});
};

/**
* Validate invariant
* @return true if invariant correct
*/
bool validate() {
return strategy_->validate();
}

private:
rxcpp::observable<T> unwrapped_;
std::unique_ptr<VerificationStrategy<T>> strategy_;
};

/**
* CallExact check invariant that subscriber called exact number of timers
* @tparam T - observable parameter
*/
template<typename T>
class CallExact : public VerificationStrategy<T> {
public:

/**
* @param expected_number_of_calls - number of calls
* that required for call
*/
CallExact(uint64_t expected_number_of_calls) :
expected_number_of_calls_(expected_number_of_calls) {
};

CallExact(CallExact<T> &&rhs) {
number_of_calls_ = 0;
expected_number_of_calls_ = 0;
std::swap(expected_number_of_calls_, rhs.expected_number_of_calls_);
std::swap(number_of_calls_, rhs.number_of_calls_);
}

CallExact<T> &operator=(CallExact<T> &&rhs) {
number_of_calls_ = 0;
expected_number_of_calls_ = 0;
std::swap(expected_number_of_calls_, rhs.expected_number_of_calls_);
std::swap(number_of_calls_, rhs.number_of_calls_);
}

/**
* Remove copy operator=
*/
CallExact<T> &operator=(CallExact<T> &rhs) = delete;

/**
* Remove copy constructor
*/
CallExact(const CallExact<T> &rhs) = delete;

void on_next_after(T next) override {
++number_of_calls_;
};

virtual bool validate() {
auto val = number_of_calls_ == expected_number_of_calls_;
if (!val) {
this->invalidate_reason_ = "Expected calls: " +
std::to_string(expected_number_of_calls_) +
", but called " +
std::to_string(number_of_calls_);
}
return val;
};

private:
uint64_t expected_number_of_calls_;
uint64_t number_of_calls_ = 0;
};

} // namespace test_observable
} // namespace common
#endif //IROHA_TEST_OBSERVABLE_HPP
4 changes: 3 additions & 1 deletion test/libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ target_link_libraries(model_crypto_provider_test
model
hash
crypto
)
)

add_subdirectory(common)
5 changes: 5 additions & 0 deletions test/libs/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
addtest(test_observable_testing test_observable_testing.cpp)
target_link_libraries(test_observable_testing
rxcpp
)

63 changes: 63 additions & 0 deletions test/libs/common/test_observable_testing.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Copyright Soramitsu Co., Ltd. 2017 All Rights Reserved.
* http://soramitsu.co.jp
*
* 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 "common/test_observable.hpp"
#include <gtest/gtest.h>

#include <iostream>

using namespace common::test_observable;
using namespace std;

class TestObservableTesting : public ::testing::Test {

};

TEST_F(TestObservableTesting, ValidCallExactTest) {
auto ints = rxcpp::observable<>::create<int>(
[](rxcpp::subscriber<int> s) {
s.on_next(1);
s.on_next(2);
s.on_completed();
});

TestObservable<int> wrapper(ints);

auto p = CallExact<int>(2);
wrapper.test_subscriber(std::make_unique<CallExact<int>>(std::move(p)),
[](auto val) {});
ASSERT_EQ(true, wrapper.validate());
}

void f() {
}

TEST_F(TestObservableTesting, UnsatisfiedCallExactTest) {
auto ints = rxcpp::observable<>::create<std::string>(
[](rxcpp::subscriber<std::string> s) {
s.on_next("1");
s.on_next("2");
s.on_completed();
});

TestObservable<std::string> wrapper(ints);

auto p = CallExact<std::string>(100500);
wrapper.test_subscriber(std::make_unique<CallExact<std::string>>(std::move(p)),
[](auto val) { cout << val << "<="; });
ASSERT_EQ(false, wrapper.validate());
}

0 comments on commit 3991804

Please sign in to comment.