/* * Copyright (c) 2016, Google Inc. * All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef PERFTOOLS_INTERVALMAP_TEST_H_ #define PERFTOOLS_INTERVALMAP_TEST_H_ #include <utility> #include <vector> #include "int_compat.h" #include "intervalmap.h" #include "string_compat.h" #include "test_compat.h" namespace perftools { namespace { class Command { public: virtual ~Command() {} virtual void ExecuteOn(IntervalMap<string>* map) const = 0; }; class SetCommand : public Command { public: SetCommand(uint64 start, uint64 limit, const char* value) : start_(start), limit_(limit), value_(value) {} void ExecuteOn(IntervalMap<string>* map) const override { map->Set(start_, limit_, value_); } private: const uint64 start_; const uint64 limit_; const char* value_; }; class NumIntervalsCommand : public Command { public: explicit NumIntervalsCommand(uint64 expected) : expected_(expected) {} void ExecuteOn(IntervalMap<string>* map) const override { ASSERT_EQ(expected_, map->Size()); } private: const uint64 expected_; }; class LookupCommand : public Command { public: LookupCommand(uint64 from, uint64 to, const char* expected) : from_(from), to_(to), expected_(expected) {} void ExecuteOn(IntervalMap<string>* map) const override { for (uint64 key = from_; key <= to_; ++key) { string result; ASSERT_TRUE(map->Lookup(key, &result)) << "Did not find value for key: " << key; ASSERT_EQ(expected_, result) << "For key: " << key << " Found value: " << result << ". Expected: " << expected_; } } private: const uint64 from_; const uint64 to_; const char* expected_; }; class FailLookupCommand : public Command { public: explicit FailLookupCommand(std::vector<uint64> keys) : keys_(std::move(keys)) {} void ExecuteOn(IntervalMap<string>* map) const override { string result; for (auto key : keys_) { ASSERT_FALSE(map->Lookup(key, &result)) << "Found value for key: " << key; } } private: std::vector<uint64> keys_; }; class FindNextCommand : public Command { public: FindNextCommand(uint64 key, uint64 expected_start, uint64 expected_limit, const char* expected_value) : key_(key), expected_start_(expected_start), expected_limit_(expected_limit), expected_value_(expected_value) {} void ExecuteOn(IntervalMap<string>* map) const override { uint64 start; uint64 limit; string value; ASSERT_TRUE(map->FindNext(key_, &start, &limit, &value)) << "Did not find a next interval for key: " << key_; bool matches_expected = expected_start_ == start && expected_limit_ == limit && expected_value_ == value; ASSERT_TRUE(matches_expected) << "Found incorrect interval for key: " << key_ << ". " << "Expected start: " << expected_start_ << ". " << "Expected limit: " << expected_start_ << ". " << "Expected value: " << expected_value_ << ". " << "Found start: " << start << ". " << "Found limit: " << limit << ". " << "Found value: " << value << ". "; } private: uint64 key_; uint64 expected_start_; uint64 expected_limit_; const char* expected_value_; }; class FailFindNextCommand : public Command { public: explicit FailFindNextCommand(uint64 key) : key_(key) {} void ExecuteOn(IntervalMap<string>* map) const override { uint64 start; uint64 limit; string value; ASSERT_FALSE(map->FindNext(key_, &start, &limit, &value)) << "Found interval for: " << key_ << ". " << "start: " << start << ". " << "limit: " << limit << ". " << "value: " << value << ". " << "Did not find a next interval for key: " << key_; } private: uint64 key_; }; std::shared_ptr<Command> Set(uint64 start, uint64 limit, const char* value) { return std::make_shared<SetCommand>(start, limit, value); } std::shared_ptr<Command> NumIntervals(uint64 size) { return std::make_shared<NumIntervalsCommand>(size); } // Looks up every key in the interval [from, to] and expects them all to be // equal to expected. std::shared_ptr<Command> Lookup(uint64 from, uint64 to, const char* expected) { return std::make_shared<LookupCommand>(from, to, expected); } std::shared_ptr<Command> FailLookup(std::vector<uint64> keys) { return std::make_shared<FailLookupCommand>(keys); } std::shared_ptr<Command> FindNext(uint64 key, uint64 start, uint64 limit, const char* expected) { return std::make_shared<FindNextCommand>(key, start, limit, expected); } std::shared_ptr<Command> FailFindNext(uint64 key) { return std::make_shared<FailFindNextCommand>(key); } class IntervalMapTest : public ::testing::TestWithParam<std::vector<std::shared_ptr<Command>>> {}; const std::vector<std::shared_ptr<Command>> tests[] = { { // Simple set/lookup Set(0, 10, "Added"), NumIntervals(1), Lookup(0, 9, "Added"), FailLookup({10, 11}), }, { // Total overwrite same start Set(5, 10, "Added"), Set(5, 20, "Overwrite"), NumIntervals(1), Lookup(5, 19, "Overwrite"), FailLookup({3, 4, 20, 21}), }, { // No overwrite, start of one equals limit of other Set(5, 10, "Segment 1"), Set(10, 20, "Segment 2"), NumIntervals(2), Lookup(5, 9, "Segment 1"), Lookup(10, 19, "Segment 2"), FailLookup({3, 4, 20, 21}), }, { // Right side overwrite Set(5, 10, "Added"), Set(8, 12, "Overwrite"), NumIntervals(2), Lookup(5, 7, "Added"), Lookup(8, 11, "Overwrite"), FailLookup({3, 4, 12, 13}), }, { // Left side overwrite Set(5, 10, "Added"), Set(3, 8, "Overwrite"), NumIntervals(2), Lookup(8, 9, "Added"), Lookup(3, 7, "Overwrite"), FailLookup({1, 2, 12, 13}), }, { // Total overwrite Set(5, 10, "Added"), Set(3, 12, "Overwrite"), NumIntervals(1), Lookup(3, 11, "Overwrite"), FailLookup({1, 2, 12, 13}), }, { // Internal overwrite Set(4, 11, "Added"), Set(6, 9, "Overwrite"), NumIntervals(3), Lookup(4, 5, "Added"), Lookup(6, 8, "Overwrite"), Lookup(9, 10, "Added"), FailLookup({2, 3, 11, 12}), }, { // Exact overwrite Set(5, 10, "Added"), Set(5, 10, "Overwrite"), NumIntervals(1), Lookup(5, 9, "Overwrite"), FailLookup({3, 4, 10, 11}), }, { // Same left side overwrite Set(5, 10, "Added"), Set(5, 8, "Overwrite"), NumIntervals(2), Lookup(5, 7, "Overwrite"), Lookup(8, 9, "Added"), FailLookup({3, 4, 10, 11}), }, { // Multiple total overwrite Set(5, 10, "SEG 1"), Set(8, 12, "SEG 2"), Set(16, 22, "SEG 3"), Set(25, 26, "SEG 4"), Set(3, 30, "Overwrite"), NumIntervals(1), Lookup(3, 29, "Overwrite"), FailLookup({1, 2, 30, 31}), }, { // Multiple total overwrite, left side free Set(5, 10, "SEG 1"), Set(8, 12, "SEG 2"), Set(16, 22, "SEG 3"), Set(25, 26, "SEG 4"), Set(7, 30, "Overwrite"), NumIntervals(2), Lookup(5, 6, "SEG 1"), Lookup(7, 29, "Overwrite"), FailLookup({3, 4, 30, 31}), }, { // Multiple total overwrite, right side free Set(5, 10, "SEG 1"), Set(8, 12, "SEG 2"), Set(16, 22, "SEG 3"), Set(25, 32, "SEG 4"), Set(3, 30, "Overwrite"), NumIntervals(2), Lookup(3, 29, "Overwrite"), Lookup(30, 31, "SEG 4"), FailLookup({1, 2, 32, 33}), }, { // Multiple total overwrite, both sides free Set(5, 10, "SEG 1"), Set(8, 12, "SEG 2"), Set(16, 22, "SEG 3"), Set(25, 32, "SEG 4"), Set(7, 30, "Overwrite"), NumIntervals(3), Lookup(5, 6, "SEG 1"), Lookup(7, 29, "Overwrite"), Lookup(30, 31, "SEG 4"), FailLookup({3, 4, 32, 33}), }, { // Two segments partly overwritten Set(5, 10, "SEG 1"), Set(17, 25, "SEG 2"), Set(8, 20, "Overwrite"), NumIntervals(3), Lookup(5, 7, "SEG 1"), Lookup(8, 19, "Overwrite"), Lookup(20, 24, "SEG 2"), FailLookup({3, 4, 25, 26}), }, { // Loop through interval map using FindNext Set(5, 10, "SEG 1"), Set(15, 20, "SEG 2"), FindNext(0, 5, 10, "SEG 1"), FindNext(10, 15, 20, "SEG 2"), FailFindNext(20), }, }; TEST_P(IntervalMapTest, GenericTest) { IntervalMap<string> map; const auto& commands = GetParam(); for (const auto& command : commands) { command->ExecuteOn(&map); // Failed asserts in subroutines aren't actually fatal so we have to return // manually. if (HasFatalFailure()) return; } } INSTANTIATE_TEST_CASE_P(AllIntervalMapTests, IntervalMapTest, ::testing::ValuesIn(tests)); } // namespace } // namespace perftools int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } #endif // PERFTOOLS_INTERVALMAP_TEST_H_