//
// Copyright (C) 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 "update_engine/update_manager/variable.h"
#include <vector>
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <gtest/gtest.h>
using base::TimeDelta;
using brillo::MessageLoop;
using brillo::MessageLoopRunMaxIterations;
using std::string;
using std::vector;
namespace chromeos_update_manager {
// Variable class that returns a value constructed with the default value.
template <typename T>
class DefaultVariable : public Variable<T> {
public:
DefaultVariable(const string& name, VariableMode mode)
: Variable<T>(name, mode) {}
DefaultVariable(const string& name, const TimeDelta& poll_interval)
: Variable<T>(name, poll_interval) {}
~DefaultVariable() override {}
protected:
const T* GetValue(TimeDelta /* timeout */, string* /* errmsg */) override {
return new T();
}
private:
DISALLOW_COPY_AND_ASSIGN(DefaultVariable);
};
class UmBaseVariableTest : public ::testing::Test {
protected:
void SetUp() override { loop_.SetAsCurrent(); }
brillo::FakeMessageLoop loop_{nullptr};
};
TEST_F(UmBaseVariableTest, GetNameTest) {
DefaultVariable<int> var("var", kVariableModeConst);
EXPECT_EQ(var.GetName(), string("var"));
}
TEST_F(UmBaseVariableTest, GetModeTest) {
DefaultVariable<int> var("var", kVariableModeConst);
EXPECT_EQ(var.GetMode(), kVariableModeConst);
DefaultVariable<int> other_var("other_var", kVariableModePoll);
EXPECT_EQ(other_var.GetMode(), kVariableModePoll);
}
TEST_F(UmBaseVariableTest, DefaultPollIntervalTest) {
DefaultVariable<int> const_var("const_var", kVariableModeConst);
EXPECT_EQ(const_var.GetPollInterval(), TimeDelta());
DefaultVariable<int> poll_var("poll_var", kVariableModePoll);
EXPECT_EQ(poll_var.GetPollInterval(), TimeDelta::FromMinutes(5));
}
TEST_F(UmBaseVariableTest, GetPollIntervalTest) {
DefaultVariable<int> var("var", TimeDelta::FromMinutes(3));
EXPECT_EQ(var.GetMode(), kVariableModePoll);
EXPECT_EQ(var.GetPollInterval(), TimeDelta::FromMinutes(3));
}
class BaseVariableObserver : public BaseVariable::ObserverInterface {
public:
void ValueChanged(BaseVariable* variable) { calls_.push_back(variable); }
// List of called functions.
vector<BaseVariable*> calls_;
};
TEST_F(UmBaseVariableTest, RepeatedObserverTest) {
DefaultVariable<int> var("var", kVariableModeAsync);
BaseVariableObserver observer;
var.AddObserver(&observer);
EXPECT_EQ(1U, var.observer_list_.size());
var.AddObserver(&observer);
EXPECT_EQ(1U, var.observer_list_.size());
var.RemoveObserver(&observer);
EXPECT_EQ(0U, var.observer_list_.size());
var.RemoveObserver(&observer);
EXPECT_EQ(0U, var.observer_list_.size());
}
TEST_F(UmBaseVariableTest, NotifyValueChangedTest) {
DefaultVariable<int> var("var", kVariableModeAsync);
BaseVariableObserver observer1;
var.AddObserver(&observer1);
// Simulate a value change on the variable's implementation.
var.NotifyValueChanged();
ASSERT_EQ(0U, observer1.calls_.size());
MessageLoopRunMaxIterations(MessageLoop::current(), 100);
ASSERT_EQ(1U, observer1.calls_.size());
// Check that the observer is called with the right argument.
EXPECT_EQ(&var, observer1.calls_[0]);
BaseVariableObserver observer2;
var.AddObserver(&observer2);
var.NotifyValueChanged();
MessageLoopRunMaxIterations(MessageLoop::current(), 100);
// Check that all the observers are called.
EXPECT_EQ(2U, observer1.calls_.size());
EXPECT_EQ(1U, observer2.calls_.size());
var.RemoveObserver(&observer1);
var.RemoveObserver(&observer2);
}
class BaseVariableObserverRemover : public BaseVariable::ObserverInterface {
public:
BaseVariableObserverRemover() : calls_(0) {}
void ValueChanged(BaseVariable* variable) override {
for (auto& observer : remove_observers_) {
variable->RemoveObserver(observer);
}
calls_++;
}
void OnCallRemoveObserver(BaseVariable::ObserverInterface* observer) {
remove_observers_.push_back(observer);
}
int get_calls() { return calls_; }
private:
vector<BaseVariable::ObserverInterface*> remove_observers_;
int calls_;
};
// Tests that we can remove an observer from a Variable on the ValueChanged()
// call to that observer.
TEST_F(UmBaseVariableTest, NotifyValueRemovesObserversTest) {
DefaultVariable<int> var("var", kVariableModeAsync);
BaseVariableObserverRemover observer1;
BaseVariableObserverRemover observer2;
var.AddObserver(&observer1);
var.AddObserver(&observer2);
// Make each observer remove both observers on ValueChanged.
observer1.OnCallRemoveObserver(&observer1);
observer1.OnCallRemoveObserver(&observer2);
observer2.OnCallRemoveObserver(&observer1);
observer2.OnCallRemoveObserver(&observer2);
var.NotifyValueChanged();
MessageLoopRunMaxIterations(MessageLoop::current(), 100);
EXPECT_EQ(1, observer1.get_calls() + observer2.get_calls());
}
} // namespace chromeos_update_manager