blob: 84a268230cbd71f11128d80cbd73f1a0a479f30e [file]
// Copyright 2023 Google LLC
//
// 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
//
// https://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 "mpact/sim/generic/component.h"
#include <cstdint>
#include <memory>
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/generic/config.h"
#include "mpact/sim/generic/counters.h"
#include "mpact/sim/proto/component_data.pb.h"
#include "src/google/protobuf/text_format.h"
namespace {
using ::mpact::sim::generic::Component;
using ::mpact::sim::generic::Config;
using ::mpact::sim::generic::SimpleCounter;
using ::mpact::sim::proto::ComponentData;
constexpr char kTopName[] = "top";
constexpr char kChildName[] = "child";
constexpr char kSecondChildName[] = "second_child";
constexpr char kInt64CounterName[] = "int64_counter";
constexpr int64_t kInt64CounterValue = -123;
constexpr char kUint64CounterName[] = "uint64_counter";
constexpr uint64_t kUint64CounterValue = 456;
constexpr char kInt64ConfigName[] = "int64_config";
constexpr int64_t kInt64ConfigValue = -456;
constexpr int64_t kInt64ConfigImportValue = -654;
constexpr char kUint64ConfigName[] = "uint64_config";
constexpr uint64_t kUint64ConfigValue = 123;
constexpr uint64_t kUint64ConfigImportValue = 321;
// Proto to import.
constexpr char kImportProto[] = R"pb(
name: "top"
configuration { name: "int64_config" sint64_value: -654 }
statistics { name: "int64_counter" sint64_value: -321 }
component_data {
name: "child"
configuration { name: "uint64_config" uint64_value: 321 }
statistics { name: "uint64_counter" uint64_value: 654 }
}
)pb";
// Test fixture. Allocates two components, two counters and two config items.
// The counters and config items are not added to the components in the fixture,
// but rather in each test as needed.
class ComponentTest : public testing::Test {
protected:
ComponentTest()
: top_(kTopName),
child_(kChildName, &top()),
int64_counter_(kInt64CounterName, kInt64CounterValue),
uint64_counter_(kUint64CounterName, kUint64CounterValue),
int64_config_(kInt64ConfigName, kInt64ConfigValue),
uint64_config_(kUint64ConfigName, kUint64ConfigValue) {}
Component &top() { return top_; }
Component &child() { return child_; }
SimpleCounter<int64_t> &int64_counter() { return int64_counter_; }
SimpleCounter<uint64_t> &uint64_counter() { return uint64_counter_; }
Config<int64_t> &int64_config() { return int64_config_; }
Config<uint64_t> &uint64_config() { return uint64_config_; }
private:
Component top_;
Component child_;
SimpleCounter<int64_t> int64_counter_;
SimpleCounter<uint64_t> uint64_counter_;
Config<int64_t> int64_config_;
Config<uint64_t> uint64_config_;
};
// Verify that the basic constructor works.
TEST_F(ComponentTest, Basic) { EXPECT_EQ(top().component_name(), kTopName); }
// Verify that the constructor that added the child component works. Also
// add another child component outside that constructor.
TEST_F(ComponentTest, ChildComponent) {
EXPECT_EQ(child().component_name(), kChildName);
EXPECT_EQ(child().parent(), &top());
EXPECT_EQ(top().GetChildComponent(kChildName), &child());
Component second_child(kSecondChildName);
EXPECT_TRUE(child().AddChildComponent(second_child).ok());
EXPECT_EQ(second_child.parent(), &child());
ASSERT_EQ(child().GetChildComponent(kSecondChildName), &second_child);
EXPECT_EQ(
top().GetChildComponent(kChildName)->GetChildComponent(kSecondChildName),
&second_child);
}
// Add counter objects to the two components. Ensure that the counters are
// found in the expected component.
TEST_F(ComponentTest, ComponentsWithCounters) {
auto *top_counter = &int64_counter();
auto *child_counter = &uint64_counter();
EXPECT_TRUE(top().AddCounter(top_counter).ok());
EXPECT_TRUE(child().AddCounter(child_counter).ok());
EXPECT_EQ(top().GetCounter(kInt64CounterName), top_counter);
EXPECT_EQ(top().GetCounter(kUint64CounterName), nullptr);
EXPECT_EQ(child().GetCounter(kInt64CounterName), nullptr);
EXPECT_EQ(child().GetCounter(kUint64CounterName), child_counter);
}
// Add config objects to the two components. Ensure that the config objects
// are found in the expected component.
TEST_F(ComponentTest, ComponentsWithConfigs) {
auto *top_config = &int64_config();
auto *child_config = &uint64_config();
EXPECT_TRUE(top().AddConfig(top_config).ok());
EXPECT_TRUE(child().AddConfig(child_config).ok());
EXPECT_EQ(top().GetConfig(kInt64ConfigName), top_config);
EXPECT_EQ(top().GetConfig(kUint64ConfigName), nullptr);
EXPECT_EQ(child().GetConfig(kInt64ConfigName), nullptr);
EXPECT_EQ(child().GetConfig(kUint64ConfigName), child_config);
}
// Verify the import done callback propagates.
TEST_F(ComponentTest, ImportDoneCallback) {
bool top_flag = false;
bool child_flag = false;
top().AddImportDoneCallback([&top_flag]() -> void { top_flag = true; });
child().AddImportDoneCallback([&child_flag]() -> void { child_flag = true; });
top().ImportDone();
EXPECT_TRUE(top_flag);
EXPECT_TRUE(child_flag);
}
// Add config entries and counters to the components with their default values,
// then export the components to a proto, and verify the proto against the
// expected value.
TEST_F(ComponentTest, ExportTest) {
auto *top_config = &int64_config();
auto *child_config = &uint64_config();
EXPECT_TRUE(top().AddConfig(top_config).ok());
EXPECT_TRUE(child().AddConfig(child_config).ok());
auto *top_counter = &int64_counter();
auto *child_counter = &uint64_counter();
EXPECT_TRUE(top().AddCounter(top_counter).ok());
EXPECT_TRUE(child().AddCounter(child_counter).ok());
auto exported_proto = std::make_unique<ComponentData>();
EXPECT_TRUE(top().Export(exported_proto.get()).ok());
}
// Import a proto into the components. Verify that the value of config entries
// are updated, but the counters are not.
TEST_F(ComponentTest, ImportTest) {
auto *top_config = &int64_config();
auto *child_config = &uint64_config();
EXPECT_TRUE(top().AddConfig(top_config).ok());
EXPECT_TRUE(child().AddConfig(child_config).ok());
auto *top_counter = &int64_counter();
auto *child_counter = &uint64_counter();
EXPECT_TRUE(top().AddCounter(top_counter).ok());
EXPECT_TRUE(child().AddCounter(child_counter).ok());
ComponentData from_text;
EXPECT_TRUE(
google::protobuf::TextFormat::ParseFromString(kImportProto, &from_text));
// Verify original values.
EXPECT_EQ(top_config->GetValue(), kInt64ConfigValue);
EXPECT_EQ(child_config->GetValue(), kUint64ConfigValue);
EXPECT_EQ(top_counter->GetValue(), kInt64CounterValue);
EXPECT_EQ(child_counter->GetValue(), kUint64CounterValue);
// Perform the import.
EXPECT_TRUE(top().Import(from_text).ok());
// Verify that the config values are changed to those specified in the proto.
EXPECT_EQ(top_config->GetValue(), kInt64ConfigImportValue);
EXPECT_EQ(child_config->GetValue(), kUint64ConfigImportValue);
// But the counter values shouldn't have changed on import.
EXPECT_EQ(top_counter->GetValue(), kInt64CounterValue);
EXPECT_EQ(child_counter->GetValue(), kUint64CounterValue);
}
} // namespace