Commit e9f50dd9 authored by Leonid Baraz's avatar Leonid Baraz Committed by Commit Bot

Copy interval_map and its unittest into chrome/browser/chromeos/policy/status_collector/

Changed namespace from media:: to policy:: for IntervalMap<...> and
Interval<..>

No functionality changes

Bug: b/144279234

Change-Id: I1ebf9f005e779d813c0a25b777f9d02fa995d70a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2080790Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Commit-Queue: Leonid Baraz <lbaraz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746866}
parent 896d6de4
...@@ -1956,6 +1956,7 @@ source_set("chromeos") { ...@@ -1956,6 +1956,7 @@ source_set("chromeos") {
"policy/status_collector/device_status_collector.h", "policy/status_collector/device_status_collector.h",
"policy/status_collector/enterprise_activity_storage.cc", "policy/status_collector/enterprise_activity_storage.cc",
"policy/status_collector/enterprise_activity_storage.h", "policy/status_collector/enterprise_activity_storage.h",
"policy/status_collector/interval_map.h",
"policy/status_collector/status_collector.cc", "policy/status_collector/status_collector.cc",
"policy/status_collector/status_collector.h", "policy/status_collector/status_collector.h",
"policy/status_collector/status_collector_state.cc", "policy/status_collector/status_collector_state.cc",
...@@ -2996,6 +2997,7 @@ source_set("unit_tests") { ...@@ -2996,6 +2997,7 @@ source_set("unit_tests") {
"policy/secondary_google_account_signin_policy_handler_unittest.cc", "policy/secondary_google_account_signin_policy_handler_unittest.cc",
"policy/server_backed_state_keys_broker_unittest.cc", "policy/server_backed_state_keys_broker_unittest.cc",
"policy/single_app_install_event_log_unittest.cc", "policy/single_app_install_event_log_unittest.cc",
"policy/status_collector/interval_map_unittest.cc",
"policy/status_uploader_unittest.cc", "policy/status_uploader_unittest.cc",
"policy/system_log_uploader_unittest.cc", "policy/system_log_uploader_unittest.cc",
"policy/system_proxy_settings_policy_handler_unittest.cc", "policy/system_proxy_settings_policy_handler_unittest.cc",
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_INTERVAL_MAP_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_INTERVAL_MAP_H_
#include <algorithm>
#include <functional>
#include <limits>
#include <map>
#include <type_traits>
#include <utility>
#include "base/logging.h"
namespace policy {
// An IntervalMap<KeyType, ValueType> maps every value of KeyType to
// a ValueType, and incrementing, decrementing and setting ranges of values
// has been optimized. The default state is to map all values in
// KeyType to ValueType(). (Which is usually zero.)
//
// Set/Increment operations should generally take
// O(log(N)) + O(M) time where N is the number of intervals in the map and
// M is the number of modified intervals.
//
// Internally, IntervalMap<> uses an std::map, where the beginning of each
// interval is stored along with the value for that interval. Adjacent intervals
// which have the same value are automatically merged. For instance, if you did:
//
// IntervalMap<int, int> tmp;
// tmp.IncrementInterval(2, 5, 2);
// tmp.IncrementInterval(4, 6, 1);
//
// Then:
// tmp[0] = 0
// tmp[1] = 0
// tmp[2] = 2
// tmp[3] = 2
// tmp[4] = 3
// tmp[5] = 1
// tmp[6] = 0
//
// If you iterate over tmp, you get the following intervals:
// -maxint .. 2 => 0
// 2 .. 4 => 2
// 4 .. 5 => 3
// 5 .. 6 => 1
// 6 .. maxint => 0
//
// Internally, this would be stored in a map as:
// -maxint:0, 2:2, 4:3, 5:1, 6:0
//
// TODO(hubbe): Consider consolidating with media::Ranges.
// Simple interval class.
// Interval ends are always non-inclusive.
// Please note that end <= begin is a valid (but empty) interval.
template <typename T>
struct Interval {
public:
Interval(const T& begin, const T& end) : begin(begin), end(end) {
static_assert(std::is_integral<T>::value, "Integral types only.");
}
// May return empty intervals (begin >= end).
Interval Intersect(const Interval& other) const {
return Interval(std::max(begin, other.begin), std::min(end, other.end));
}
bool Empty() const { return begin >= end; }
T begin;
T end;
};
// The IntervalMapConstIterator points to an interval in an
// IntervalMap where all values are the same. Calling ++/--
// goes to the next/previous interval, which is guaranteed to
// have values different from the current interval.
template <typename KeyType,
typename ValueType,
class Compare = std::less<KeyType>,
class NumericLimits = std::numeric_limits<KeyType>>
class IntervalMapConstIterator {
public:
typedef std::map<KeyType, ValueType, Compare> MapType;
IntervalMapConstIterator() {
static_assert(std::is_integral<KeyType>::value, "Integral types only.");
}
IntervalMapConstIterator(const MapType* map,
typename MapType::const_iterator iter)
: map_(map), iter_(iter) {
static_assert(std::is_integral<KeyType>::value, "Integral types only.");
}
bool operator==(const IntervalMapConstIterator& other) const {
return iter_ == other.iter_;
}
bool operator!=(const IntervalMapConstIterator& other) const {
return iter_ != other.iter_;
}
// Returns the beginning of the current interval.
KeyType interval_begin() const {
DCHECK(iter_ != map_->end());
return iter_->first;
}
// Returns the end of the current interval, non-inclusive.
KeyType interval_end() const {
DCHECK(iter_ != map_->end());
typename MapType::const_iterator next = iter_;
++next;
if (next == map_->end()) {
return NumericLimits::max();
} else {
return next->first;
}
}
// Returns the current interval.
Interval<KeyType> interval() const {
return Interval<KeyType>(interval_begin(), interval_end());
}
// Returns the value associated with the current interval.
ValueType value() const {
DCHECK(iter_ != map_->end());
return iter_->second;
}
// Needed to make the following construct work:
// for (const auto& interval_value_pair : interval_map)
std::pair<Interval<KeyType>, ValueType> operator*() const {
return std::make_pair(interval(), value());
}
// Go to the next interval.
// The beginning of the next interval always matches the end of the current
// interval. (But should always have a different value.)
// Not allowed if we're already at map_->end().
void operator++() {
DCHECK(iter_ != map_->end());
++iter_;
}
// Go to the previous interval.
// The end of the previous interval always matches the beginning of the
// current interval. (But should always have a different value.)
// Not allowed if we're already at map_->begin().
void operator--() {
DCHECK(iter_ != map_->begin());
--iter_;
}
private:
const MapType* map_;
// Pointer to the entry in the IntervalMap that specifies the
// beginning of the current interval.
typename MapType::const_iterator iter_;
};
template <typename KeyType,
typename ValueType,
class Compare = std::less<KeyType>,
class NumericLimits = std::numeric_limits<KeyType>>
class IntervalMap {
public:
typedef std::map<KeyType, ValueType, Compare> MapType;
typedef IntervalMapConstIterator<KeyType, ValueType, Compare, NumericLimits>
const_iterator;
IntervalMap() {
static_assert(std::is_integral<KeyType>::value, "Integral types only.");
// Adding an explicit entry for the default interval is not strictly needed,
// but simplifies the code a lot.
map_[NumericLimits::min()] = ValueType();
}
// Returns the value at a particular point.
// Defaults to ValueType().
ValueType operator[](const KeyType& k) const {
typename MapType::const_iterator i = map_.upper_bound(k);
DCHECK(i != map_.begin());
--i;
return i->second;
}
// Increase [from..to) by |how_much|.
void IncrementInterval(KeyType from, KeyType to, ValueType how_much) {
if (to <= from || how_much == 0)
return;
typename MapType::iterator a = MakeEntry(from);
typename MapType::iterator b = MakeEntry(to);
for (typename MapType::iterator i = a; i != b; ++i) {
i->second += how_much;
}
RemoveDuplicates(a);
// b may be invalid
RemoveDuplicates(map_.lower_bound(to));
}
// Set [from..to) to |how_much|.
void SetInterval(KeyType from, KeyType to, ValueType how_much) {
if (to <= from)
return;
typename MapType::iterator a = MakeEntry(from);
typename MapType::iterator b = MakeEntry(to);
a->second = how_much;
while (true) {
typename MapType::iterator c = a;
++c;
if (c == b) {
break;
} else {
map_.erase(c);
}
}
RemoveDuplicates(a);
// b may be invalid
RemoveDuplicates(map_.lower_bound(to));
}
// Returns an iterator to the first interval.
// Note, there is always at least one interval.
const_iterator begin() const { return const_iterator(&map(), map_.begin()); }
// Returns an end marker iterator.
const_iterator end() const { return const_iterator(&map(), map_.end()); }
// Returns an iterator to the interval containing |k|.
// Always returns a valid iterator.
const_iterator find(KeyType k) const {
typename MapType::const_iterator iter = map_.upper_bound(k);
DCHECK(iter != map_.begin());
--iter;
return const_iterator(&map(), iter);
}
bool empty() const { return map().size() == 1; }
void clear() {
map_.clear();
map_[NumericLimits::min()] = ValueType();
}
private:
const MapType& map() const { return map_; }
// Make an entry in map_ with the key |k| and return it's iterator.
// If such an entry already exists, just re-use it.
// If a new entry is created, it's value will be set to the same
// as the preceding entry, or ValueType() if no preceding entry exists.
// After calling this function, we'll need to call RemoveDuplicates()
// to clean up any duplicates that we made.
typename MapType::iterator MakeEntry(KeyType k) {
auto insert_result = map_.emplace(k, ValueType());
if (insert_result.second) {
if (insert_result.first != map_.begin()) {
typename MapType::iterator i = insert_result.first;
--i;
insert_result.first->second = i->second;
}
}
return insert_result.first;
}
// Remove duplicates before and after |i|.
void RemoveDuplicates(typename MapType::iterator i) {
if (i == map_.end())
return;
typename MapType::iterator first = i;
typename MapType::iterator second = i;
if (i != map_.begin()) {
--first;
if (first->second == second->second) {
map_.erase(second);
second = first;
} else {
first = second;
}
}
++second;
if (second != map_.end() && first->second == second->second) {
map_.erase(second);
}
}
MapType map_;
};
} // namespace policy
#endif // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_INTERVAL_MAP_H_
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <string>
#include <vector>
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/chromeos/policy/status_collector/interval_map.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Our tests only modify the interval map entries in [0..kTestSize).
// We need this to be big enough to hit tricky corner cases, but small
// enough that we get lots of entry duplication to clean up.
// Also, SimpleIntervalMap uses a vector of size kTestSize to emulate
// a intervalmap, so making this too big will the test down a lot.
const int kTestSize = 16;
class SimpleIntervalMap {
public:
SimpleIntervalMap() : data_(kTestSize) {}
void IncrementInterval(int32_t from, int32_t to, int32_t how_much) {
for (int32_t i = from; i < to; i++) {
data_[i] += how_much;
}
}
void SetInterval(int32_t from, int32_t to, int32_t how_much) {
for (int32_t i = from; i < to; i++) {
data_[i] = how_much;
}
}
int32_t operator[](int32_t index) const { return data_[index]; }
private:
std::vector<int32_t> data_;
};
class IntervalMapTest : public testing::Test {
public:
IntervalMapTest() = default;
void IncrementInterval(int32_t from, int32_t to, int32_t how_much) {
truth_.IncrementInterval(from, to, how_much);
testee_.IncrementInterval(from, to, how_much);
std::string message =
base::StringPrintf("After [%d - %d) += %d", from, to, how_much);
Compare(message);
}
void SetInterval(int32_t from, int32_t to, int32_t how_much) {
truth_.SetInterval(from, to, how_much);
testee_.SetInterval(from, to, how_much);
std::string message =
base::StringPrintf("After [%d - %d) += %d", from, to, how_much);
Compare(message);
}
// Will exercise operator[] and IntervalMap::const_iterator.
void Compare(const std::string& message) {
bool had_fail = HasFailure();
for (int i = 0; i < kTestSize; i++) {
EXPECT_EQ(truth_[i], testee_[i]) << " i = " << i << " " << message;
}
EXPECT_EQ(testee_[-1], 0) << message;
EXPECT_EQ(testee_[kTestSize], 0) << message;
int32_t prev_ = 0;
int32_t end_of_last_interval = 0;
int32_t num_intervals = 0;
for (const auto& r : testee_) {
num_intervals++;
EXPECT_LT(r.first.begin, r.first.end);
if (r.first.begin == std::numeric_limits<int32_t>::min()) {
EXPECT_EQ(0, r.second);
} else {
EXPECT_EQ(end_of_last_interval, r.first.begin);
EXPECT_GE(r.first.begin, 0) << message;
EXPECT_LE(r.first.begin, kTestSize) << message;
EXPECT_NE(r.second, prev_) << message;
}
end_of_last_interval = r.first.end;
prev_ = r.second;
}
EXPECT_EQ(prev_, 0) << message;
if (HasFailure() && !had_fail) {
for (int i = 0; i < kTestSize; i++) {
LOG(ERROR) << i << ": Truth =" << truth_[i]
<< " Testee = " << testee_[i];
}
for (const auto& r : testee_) {
LOG(ERROR) << "Interval: " << r.first.begin << " - " << r.first.end
<< " = " << r.second;
}
}
}
void Clear() {
for (int j = 0; j < kTestSize; j++) {
IncrementInterval(j, j + 1, -truth_[j]);
}
}
protected:
SimpleIntervalMap truth_;
policy::IntervalMap<int32_t, int32_t> testee_;
};
} // namespace
TEST_F(IntervalMapTest, SimpleTest) {
IncrementInterval(3, 7, 4);
EXPECT_EQ(0, testee_[0]);
EXPECT_EQ(0, testee_[2]);
EXPECT_EQ(4, testee_[3]);
EXPECT_EQ(4, testee_[5]);
EXPECT_EQ(4, testee_[6]);
EXPECT_EQ(0, testee_[7]);
IncrementInterval(3, 7, -4);
EXPECT_TRUE(testee_.empty());
}
TEST_F(IntervalMapTest, SimpleIncrementTest) {
IncrementInterval(3, 7, 1);
IncrementInterval(6, 10, 2);
EXPECT_EQ(0, testee_[2]);
EXPECT_EQ(1, testee_[3]);
EXPECT_EQ(1, testee_[5]);
EXPECT_EQ(3, testee_[6]);
EXPECT_EQ(2, testee_[7]);
EXPECT_EQ(2, testee_[9]);
EXPECT_EQ(0, testee_[10]);
SetInterval(3, 12, 0);
EXPECT_TRUE(testee_.empty());
}
TEST_F(IntervalMapTest, IncrementJoinIntervalsTest) {
IncrementInterval(3, 5, 1);
IncrementInterval(7, 8, 1);
IncrementInterval(9, 11, 1);
IncrementInterval(5, 7, 1);
IncrementInterval(8, 9, 1);
auto i = testee_.find(5);
EXPECT_EQ(3, i.interval_begin());
EXPECT_EQ(11, i.interval_end());
EXPECT_EQ(1, i.value());
}
TEST_F(IntervalMapTest, SetJoinIntervalsTest) {
SetInterval(3, 5, 1);
SetInterval(7, 8, 1);
SetInterval(9, 11, 1);
SetInterval(5, 9, 1); // overwrites one interval
auto i = testee_.find(5);
EXPECT_EQ(3, i.interval_begin());
EXPECT_EQ(11, i.interval_end());
EXPECT_EQ(1, i.value());
}
TEST_F(IntervalMapTest, FindTest) {
IncrementInterval(5, 6, 1);
IncrementInterval(1, 10, 2);
int32_t min_value = std::numeric_limits<int32_t>::min();
int32_t max_value = std::numeric_limits<int32_t>::max();
auto i = testee_.find(0);
EXPECT_EQ(min_value, i.interval_begin());
EXPECT_EQ(1, i.interval_end());
EXPECT_EQ(0, i.value());
i = testee_.find(4);
EXPECT_EQ(1, i.interval_begin());
EXPECT_EQ(5, i.interval_end());
EXPECT_EQ(2, i.value());
i = testee_.find(5);
EXPECT_EQ(5, i.interval_begin());
EXPECT_EQ(6, i.interval_end());
EXPECT_EQ(3, i.value());
i = testee_.find(6);
EXPECT_EQ(6, i.interval_begin());
EXPECT_EQ(10, i.interval_end());
EXPECT_EQ(2, i.value());
i = testee_.find(9);
EXPECT_EQ(6, i.interval_begin());
EXPECT_EQ(10, i.interval_end());
EXPECT_EQ(2, i.value());
i = testee_.find(10);
EXPECT_EQ(10, i.interval_begin());
EXPECT_EQ(max_value, i.interval_end());
EXPECT_EQ(0, i.value());
}
TEST_F(IntervalMapTest, MinMaxInt) {
int32_t min_value = std::numeric_limits<int32_t>::min();
int32_t max_value = std::numeric_limits<int32_t>::max();
// Change a single value at minint
testee_.IncrementInterval(min_value, min_value + 1, 7);
EXPECT_EQ(7, testee_[min_value]);
EXPECT_EQ(0, testee_[min_value + 1]);
auto i = testee_.find(0);
EXPECT_EQ(min_value + 1, i.interval_begin());
EXPECT_EQ(max_value, i.interval_end());
EXPECT_EQ(0, i.value());
--i;
EXPECT_TRUE(i == testee_.find(min_value));
EXPECT_EQ(min_value, i.interval_begin());
EXPECT_EQ(min_value + 1, i.interval_end());
EXPECT_EQ(7, i.value());
testee_.clear();
// Change a single value at maxint
// Note that we don't actually have a way to represent a range
// that includes maxint as the end of the interval is non-inclusive.
testee_.IncrementInterval(max_value - 1, max_value, 7);
EXPECT_EQ(7, testee_[max_value - 1]);
EXPECT_EQ(0, testee_[max_value - 2]);
i = testee_.find(0);
EXPECT_EQ(min_value, i.interval_begin());
EXPECT_EQ(max_value - 1, i.interval_end());
EXPECT_EQ(0, i.value());
++i;
EXPECT_TRUE(i == testee_.find(max_value - 1));
EXPECT_EQ(max_value - 1, i.interval_begin());
EXPECT_EQ(max_value, i.interval_end());
EXPECT_EQ(7, i.value());
testee_.clear();
// Change entire range (almost)
testee_.IncrementInterval(min_value, max_value, 17);
EXPECT_EQ(17, testee_[min_value]);
EXPECT_EQ(17, testee_[0]);
EXPECT_EQ(17, testee_[max_value - 1]);
i = testee_.find(0);
EXPECT_EQ(min_value, i.interval_begin());
EXPECT_EQ(max_value, i.interval_end());
EXPECT_EQ(17, i.value());
EXPECT_TRUE(i == testee_.find(max_value - 1));
EXPECT_TRUE(i == testee_.find(min_value));
}
TEST_F(IntervalMapTest, RandomIncrementTest) {
for (int j = 0; j < 200; j++) {
Clear();
for (int i = 0; i < 200; i++) {
int32_t begin = base::RandInt(0, kTestSize - 1 - 1);
int32_t end = begin + 1 + base::RandInt(0, kTestSize - begin - 1 - 1);
IncrementInterval(begin, end, base::RandInt(0, 1) ? 1 : -1);
if (HasFailure()) {
return;
}
}
}
}
TEST_F(IntervalMapTest, RandomSetTest) {
for (int j = 0; j < 200; j++) {
Clear();
for (int i = 0; i < 200; i++) {
int32_t begin = base::RandInt(0, kTestSize - 1 - 1);
int32_t end = begin + 1 + base::RandInt(0, kTestSize - begin - 1 - 1);
SetInterval(begin, end, base::RandInt(0, 3));
if (HasFailure()) {
return;
}
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment