From 0daa8be9d6addfbafa09f54aeef62b1c5a360ac6 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Fri, 18 Apr 2014 21:20:54 +0000 Subject: [PATCH] Add Chromium's ScopedVector. Trivial changes from the original excepting scoped_vector_unittest.cc, diff here: https://paste.googleplex.com/6664017300946944 This is a prerequisite for: http://review.webrtc.org/9919004/ TBR=henrike@webrtc.org BUG=2894 Review URL: https://webrtc-codereview.appspot.com/12179004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5938 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../system_wrappers/interface/scoped_vector.h | 149 ++++++++ webrtc/system_wrappers/interface/stl_util.h | 265 ++++++++++++++ webrtc/system_wrappers/source/move.h | 11 + .../source/scoped_vector_unittest.cc | 328 ++++++++++++++++++ .../source/stl_util_unittest.cc | 250 +++++++++++++ .../source/system_wrappers.gyp | 2 + .../source/system_wrappers_tests.gyp | 2 + 7 files changed, 1007 insertions(+) create mode 100644 webrtc/system_wrappers/interface/scoped_vector.h create mode 100644 webrtc/system_wrappers/interface/stl_util.h create mode 100644 webrtc/system_wrappers/source/scoped_vector_unittest.cc create mode 100644 webrtc/system_wrappers/source/stl_util_unittest.cc diff --git a/webrtc/system_wrappers/interface/scoped_vector.h b/webrtc/system_wrappers/interface/scoped_vector.h new file mode 100644 index 0000000000..68db3a1210 --- /dev/null +++ b/webrtc/system_wrappers/interface/scoped_vector.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/memory/scoped_vector.h. + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_VECTOR_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_VECTOR_H_ + +#include +#include +#include + +#include "webrtc/system_wrappers/interface/stl_util.h" +#include "webrtc/system_wrappers/source/move.h" + +namespace webrtc { + +// ScopedVector wraps a vector deleting the elements from its +// destructor. +template +class ScopedVector { + WEBRTC_MOVE_ONLY_TYPE_FOR_CPP_03(ScopedVector, RValue) + + public: + typedef typename std::vector::allocator_type allocator_type; + typedef typename std::vector::size_type size_type; + typedef typename std::vector::difference_type difference_type; + typedef typename std::vector::pointer pointer; + typedef typename std::vector::const_pointer const_pointer; + typedef typename std::vector::reference reference; + typedef typename std::vector::const_reference const_reference; + typedef typename std::vector::value_type value_type; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::reverse_iterator reverse_iterator; + typedef typename std::vector::const_reverse_iterator + const_reverse_iterator; + + ScopedVector() {} + ~ScopedVector() { clear(); } + ScopedVector(RValue other) { swap(*other.object); } + + ScopedVector& operator=(RValue rhs) { + swap(*rhs.object); + return *this; + } + + reference operator[](size_t index) { return v_[index]; } + const_reference operator[](size_t index) const { return v_[index]; } + + bool empty() const { return v_.empty(); } + size_t size() const { return v_.size(); } + + reverse_iterator rbegin() { return v_.rbegin(); } + const_reverse_iterator rbegin() const { return v_.rbegin(); } + reverse_iterator rend() { return v_.rend(); } + const_reverse_iterator rend() const { return v_.rend(); } + + iterator begin() { return v_.begin(); } + const_iterator begin() const { return v_.begin(); } + iterator end() { return v_.end(); } + const_iterator end() const { return v_.end(); } + + const_reference front() const { return v_.front(); } + reference front() { return v_.front(); } + const_reference back() const { return v_.back(); } + reference back() { return v_.back(); } + + void push_back(T* elem) { v_.push_back(elem); } + + void pop_back() { + assert(!empty()); + delete v_.back(); + v_.pop_back(); + } + + std::vector& get() { return v_; } + const std::vector& get() const { return v_; } + void swap(std::vector& other) { v_.swap(other); } + void swap(ScopedVector& other) { v_.swap(other.v_); } + void release(std::vector* out) { + out->swap(v_); + v_.clear(); + } + + void reserve(size_t capacity) { v_.reserve(capacity); } + + // Resize, deleting elements in the disappearing range if we are shrinking. + void resize(size_t new_size) { + if (v_.size() > new_size) + STLDeleteContainerPointers(v_.begin() + new_size, v_.end()); + v_.resize(new_size); + } + + template + void assign(InputIterator begin, InputIterator end) { + v_.assign(begin, end); + } + + void clear() { STLDeleteElements(&v_); } + + // Like |clear()|, but doesn't delete any elements. + void weak_clear() { v_.clear(); } + + // Lets the ScopedVector take ownership of |x|. + iterator insert(iterator position, T* x) { + return v_.insert(position, x); + } + + // Lets the ScopedVector take ownership of elements in [first,last). + template + void insert(iterator position, InputIterator first, InputIterator last) { + v_.insert(position, first, last); + } + + iterator erase(iterator position) { + delete *position; + return v_.erase(position); + } + + iterator erase(iterator first, iterator last) { + STLDeleteContainerPointers(first, last); + return v_.erase(first, last); + } + + // Like |erase()|, but doesn't delete the element at |position|. + iterator weak_erase(iterator position) { + return v_.erase(position); + } + + // Like |erase()|, but doesn't delete the elements in [first, last). + iterator weak_erase(iterator first, iterator last) { + return v_.erase(first, last); + } + + private: + std::vector v_; +}; + +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_VECTOR_H_ diff --git a/webrtc/system_wrappers/interface/stl_util.h b/webrtc/system_wrappers/interface/stl_util.h new file mode 100644 index 0000000000..ebe855fb10 --- /dev/null +++ b/webrtc/system_wrappers/interface/stl_util.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/stl_util.h. + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STL_UTIL_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STL_UTIL_H_ + +#include +#include +#include +#include +#include +#include + +namespace webrtc { + +// Clears internal memory of an STL object. +// STL clear()/reserve(0) does not always free internal memory allocated +// This function uses swap/destructor to ensure the internal memory is freed. +template +void STLClearObject(T* obj) { + T tmp; + tmp.swap(*obj); + // Sometimes "T tmp" allocates objects with memory (arena implementation?). + // Hence using additional reserve(0) even if it doesn't always work. + obj->reserve(0); +} + +// For a range within a container of pointers, calls delete (non-array version) +// on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template +void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// For a range within a container of pairs, calls delete (non-array version) on +// BOTH items in the pairs. +// NOTE: Like STLDeleteContainerPointers, it is important that this deletes +// behind the iterator because if both the key and value are deleted, the +// container may call the hash function on the iterator when it is advanced, +// which could result in the hash function trying to dereference a stale +// pointer. +template +void STLDeleteContainerPairPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + delete temp->second; + } +} + +// For a range within a container of pairs, calls delete (non-array version) on +// the FIRST item in the pairs. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +template +void STLDeleteContainerPairFirstPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + } +} + +// For a range within a container of pairs, calls delete. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +// Deleting the value does not always invalidate the iterator, but it may +// do so if the key is a pointer into the value object. +template +void STLDeleteContainerPairSecondPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->second; + } +} + +// To treat a possibly-empty vector as an array, use these functions. +// If you know the array will never be empty, you can use &*v.begin() +// directly, but that is undefined behaviour if |v| is empty. +template +inline T* vector_as_array(std::vector* v) { + return v->empty() ? NULL : &*v->begin(); +} + +template +inline const T* vector_as_array(const std::vector* v) { + return v->empty() ? NULL : &*v->begin(); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(std::string* str) { + // DO NOT USE const_cast(str->data()) + return str->empty() ? NULL : &*str->begin(); +} + +// The following functions are useful for cleaning up STL containers whose +// elements point to allocated memory. + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// STLElementDeleter (defined below), which ensures that your container's +// elements are deleted when the STLElementDeleter goes out of scope. +template +void STLDeleteElements(T* container) { + if (!container) + return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. +template +void STLDeleteValues(T* container) { + if (!container) + return; + for (typename T::iterator i(container->begin()); i != container->end(); ++i) + delete i->second; + container->clear(); +} + + +// The following classes provide a convenient way to delete all elements or +// values from STL containers when they goes out of scope. This greatly +// simplifies code that creates temporary objects and has multiple return +// statements. Example: +// +// vector tmp_proto; +// STLElementDeleter > d(&tmp_proto); +// if (...) return false; +// ... +// return success; + +// Given a pointer to an STL container this class will delete all the element +// pointers when it goes out of scope. +template +class STLElementDeleter { + public: + STLElementDeleter(T* container) : container_(container) {} + ~STLElementDeleter() { STLDeleteElements(container_); } + + private: + T* container_; +}; + +// Given a pointer to an STL container this class will delete all the value +// pointers when it goes out of scope. +template +class STLValueDeleter { + public: + STLValueDeleter(T* container) : container_(container) {} + ~STLValueDeleter() { STLDeleteValues(container_); } + + private: + T* container_; +}; + +// Test to see if a set, map, hash_set or hash_map contains a particular key. +// Returns true if the key is in the collection. +template +bool ContainsKey(const Collection& collection, const Key& key) { + return collection.find(key) != collection.end(); +} + +// Returns true if the container is sorted. +template +bool STLIsSorted(const Container& cont) { + // Note: Use reverse iterator on container to ensure we only require + // value_type to implement operator<. + return std::adjacent_find(cont.rbegin(), cont.rend(), + std::less()) + == cont.rend(); +} + +// Returns a new ResultType containing the difference of two sorted containers. +template +ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + ResultType difference; + std::set_difference(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(difference, difference.end())); + return difference; +} + +// Returns a new ResultType containing the union of two sorted containers. +template +ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + ResultType result; + std::set_union(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(result, result.end())); + return result; +} + +// Returns a new ResultType containing the intersection of two sorted +// containers. +template +ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + ResultType result; + std::set_intersection(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(result, result.end())); + return result; +} + +// Returns true if the sorted container |a1| contains all elements of the sorted +// container |a2|. +template +bool STLIncludes(const Arg1& a1, const Arg2& a2) { + assert(STLIsSorted(a1)); + assert(STLIsSorted(a2)); + return std::includes(a1.begin(), a1.end(), + a2.begin(), a2.end()); +} + +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_STL_UTIL_H_ diff --git a/webrtc/system_wrappers/source/move.h b/webrtc/system_wrappers/source/move.h index 53109c73ad..2e93641f45 100644 --- a/webrtc/system_wrappers/source/move.h +++ b/webrtc/system_wrappers/source/move.h @@ -144,6 +144,16 @@ // choose the one that adheres to the standard. // // +// WHY HAVE typedef void MoveOnlyTypeForCPP03 +// +// Callback<>/Bind() needs to understand movable-but-not-copyable semantics +// to call .Pass() appropriately when it is expected to transfer the value. +// The cryptic typedef MoveOnlyTypeForCPP03 is added to make this check +// easy and automatic in helper templates for Callback<>/Bind(). +// See IsMoveOnlyType template and its usage in base/callback_internal.h +// for more details. +// +// // COMPARED TO C++11 // // In C++11, you would implement this functionality using an r-value reference @@ -210,6 +220,7 @@ public: \ operator rvalue_type() { return rvalue_type(this); } \ type Pass() { return type(rvalue_type(this)); } \ + typedef void MoveOnlyTypeForCPP03; \ private: #endif // WEBRTC_SYSTEM_WRAPPERS_INTEFACE_MOVE_H_ diff --git a/webrtc/system_wrappers/source/scoped_vector_unittest.cc b/webrtc/system_wrappers/source/scoped_vector_unittest.cc new file mode 100644 index 0000000000..c1b9d01cc8 --- /dev/null +++ b/webrtc/system_wrappers/source/scoped_vector_unittest.cc @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/memory/scoped_vector_unittest.cc + +#include "webrtc/system_wrappers/interface/scoped_vector.h" + +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { +namespace { + +// The LifeCycleObject notifies its Observer upon construction & destruction. +class LifeCycleObject { + public: + class Observer { + public: + virtual void OnLifeCycleConstruct(LifeCycleObject* o) = 0; + virtual void OnLifeCycleDestroy(LifeCycleObject* o) = 0; + + protected: + virtual ~Observer() {} + }; + + ~LifeCycleObject() { + observer_->OnLifeCycleDestroy(this); + } + + private: + friend class LifeCycleWatcher; + + explicit LifeCycleObject(Observer* observer) + : observer_(observer) { + observer_->OnLifeCycleConstruct(this); + } + + Observer* observer_; + + DISALLOW_COPY_AND_ASSIGN(LifeCycleObject); +}; + +// The life cycle states we care about for the purposes of testing ScopedVector +// against objects. +enum LifeCycleState { + LC_INITIAL, + LC_CONSTRUCTED, + LC_DESTROYED, +}; + +// Because we wish to watch the life cycle of an object being constructed and +// destroyed, and further wish to test expectations against the state of that +// object, we cannot save state in that object itself. Instead, we use this +// pairing of the watcher, which observes the object and notifies of +// construction & destruction. Since we also may be testing assumptions about +// things not getting freed, this class also acts like a scoping object and +// deletes the |constructed_life_cycle_object_|, if any when the +// LifeCycleWatcher is destroyed. To keep this simple, the only expected state +// changes are: +// INITIAL -> CONSTRUCTED -> DESTROYED. +// Anything more complicated than that should start another test. +class LifeCycleWatcher : public LifeCycleObject::Observer { + public: + LifeCycleWatcher() : life_cycle_state_(LC_INITIAL) {} + virtual ~LifeCycleWatcher() {} + + // Assert INITIAL -> CONSTRUCTED and no LifeCycleObject associated with this + // LifeCycleWatcher. + virtual void OnLifeCycleConstruct(LifeCycleObject* object) OVERRIDE { + ASSERT_EQ(LC_INITIAL, life_cycle_state_); + ASSERT_EQ(NULL, constructed_life_cycle_object_.get()); + life_cycle_state_ = LC_CONSTRUCTED; + constructed_life_cycle_object_.reset(object); + } + + // Assert CONSTRUCTED -> DESTROYED and the |object| being destroyed is the + // same one we saw constructed. + virtual void OnLifeCycleDestroy(LifeCycleObject* object) OVERRIDE { + ASSERT_EQ(LC_CONSTRUCTED, life_cycle_state_); + LifeCycleObject* constructed_life_cycle_object = + constructed_life_cycle_object_.release(); + ASSERT_EQ(constructed_life_cycle_object, object); + life_cycle_state_ = LC_DESTROYED; + } + + LifeCycleState life_cycle_state() const { return life_cycle_state_; } + + // Factory method for creating a new LifeCycleObject tied to this + // LifeCycleWatcher. + LifeCycleObject* NewLifeCycleObject() { + return new LifeCycleObject(this); + } + + // Returns true iff |object| is the same object that this watcher is tracking. + bool IsWatching(LifeCycleObject* object) const { + return object == constructed_life_cycle_object_.get(); + } + + private: + LifeCycleState life_cycle_state_; + scoped_ptr constructed_life_cycle_object_; + + DISALLOW_COPY_AND_ASSIGN(LifeCycleWatcher); +}; + +TEST(ScopedVectorTest, LifeCycleWatcher) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + LifeCycleObject* object = watcher.NewLifeCycleObject(); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + delete object; + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +TEST(ScopedVectorTest, PopBack) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + scoped_vector.pop_back(); + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); + EXPECT_TRUE(scoped_vector.empty()); +} + +TEST(ScopedVectorTest, Clear) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + scoped_vector.clear(); + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); + EXPECT_TRUE(scoped_vector.empty()); +} + +TEST(ScopedVectorTest, WeakClear) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + scoped_vector.weak_clear(); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(scoped_vector.empty()); +} + +TEST(ScopedVectorTest, ResizeShrink) { + LifeCycleWatcher first_watcher; + EXPECT_EQ(LC_INITIAL, first_watcher.life_cycle_state()); + LifeCycleWatcher second_watcher; + EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state()); + ScopedVector scoped_vector; + + scoped_vector.push_back(first_watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); + EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state()); + EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0])); + EXPECT_FALSE(second_watcher.IsWatching(scoped_vector[0])); + + scoped_vector.push_back(second_watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); + EXPECT_EQ(LC_CONSTRUCTED, second_watcher.life_cycle_state()); + EXPECT_FALSE(first_watcher.IsWatching(scoped_vector[1])); + EXPECT_TRUE(second_watcher.IsWatching(scoped_vector[1])); + + // Test that shrinking a vector deletes elements in the disappearing range. + scoped_vector.resize(1); + EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); + EXPECT_EQ(LC_DESTROYED, second_watcher.life_cycle_state()); + EXPECT_EQ(1u, scoped_vector.size()); + EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0])); +} + +TEST(ScopedVectorTest, ResizeGrow) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + + scoped_vector.resize(5); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + ASSERT_EQ(5u, scoped_vector.size()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector[0])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[1])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[2])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[3])); + EXPECT_FALSE(watcher.IsWatching(scoped_vector[4])); +} + +TEST(ScopedVectorTest, Scope) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + { + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + } + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +TEST(ScopedVectorTest, MoveConstruct) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + { + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + EXPECT_FALSE(scoped_vector.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + + ScopedVector scoped_vector_copy(scoped_vector.Pass()); + EXPECT_TRUE(scoped_vector.empty()); + EXPECT_FALSE(scoped_vector_copy.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector_copy.back())); + + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + } + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +TEST(ScopedVectorTest, MoveAssign) { + LifeCycleWatcher watcher; + EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); + { + ScopedVector scoped_vector; + scoped_vector.push_back(watcher.NewLifeCycleObject()); + ScopedVector scoped_vector_assign; + EXPECT_FALSE(scoped_vector.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); + + scoped_vector_assign = scoped_vector.Pass(); + EXPECT_TRUE(scoped_vector.empty()); + EXPECT_FALSE(scoped_vector_assign.empty()); + EXPECT_TRUE(watcher.IsWatching(scoped_vector_assign.back())); + + EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); + } + EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); +} + +class DeleteCounter { + public: + explicit DeleteCounter(int* deletes) + : deletes_(deletes) { + } + + ~DeleteCounter() { + (*deletes_)++; + } + + void VoidMethod0() {} + + private: + int* const deletes_; + + DISALLOW_COPY_AND_ASSIGN(DeleteCounter); +}; + +// This class is used in place of Chromium's base::Callback. +template +class PassThru { + public: + explicit PassThru(ScopedVector scoper) : scoper_(scoper.Pass()) {} + + ScopedVector Run() { + return scoper_.Pass(); + } + + private: + ScopedVector scoper_; +}; + +TEST(ScopedVectorTest, Passed) { + int deletes = 0; + ScopedVector deleter_vector; + deleter_vector.push_back(new DeleteCounter(&deletes)); + EXPECT_EQ(0, deletes); + PassThru pass_thru(deleter_vector.Pass()); + EXPECT_EQ(0, deletes); + ScopedVector result = pass_thru.Run(); + EXPECT_EQ(0, deletes); + result.clear(); + EXPECT_EQ(1, deletes); +}; + +TEST(ScopedVectorTest, InsertRange) { + LifeCycleWatcher watchers[5]; + size_t watchers_size = sizeof(watchers) / sizeof(*watchers); + + std::vector vec; + for (LifeCycleWatcher* it = watchers; it != watchers + watchers_size; + ++it) { + EXPECT_EQ(LC_INITIAL, it->life_cycle_state()); + vec.push_back(it->NewLifeCycleObject()); + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); + } + // Start scope for ScopedVector. + { + ScopedVector scoped_vector; + scoped_vector.insert(scoped_vector.end(), vec.begin() + 1, vec.begin() + 3); + for (LifeCycleWatcher* it = watchers; it != watchers + watchers_size; + ++it) + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); + } + for (LifeCycleWatcher* it = watchers; it != watchers + 1; ++it) + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); + for (LifeCycleWatcher* it = watchers + 1; it != watchers + 3; ++it) + EXPECT_EQ(LC_DESTROYED, it->life_cycle_state()); + for (LifeCycleWatcher* it = watchers + 3; it != watchers + watchers_size; + ++it) + EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/system_wrappers/source/stl_util_unittest.cc b/webrtc/system_wrappers/source/stl_util_unittest.cc new file mode 100644 index 0000000000..e60a913cfc --- /dev/null +++ b/webrtc/system_wrappers/source/stl_util_unittest.cc @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Borrowed from Chromium's src/base/stl_util_unittest.cc +#include "webrtc/system_wrappers/interface/stl_util.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Used as test case to ensure the various base::STLXxx functions don't require +// more than operators "<" and "==" on values stored in containers. +class ComparableValue { + public: + explicit ComparableValue(int value) : value_(value) {} + + bool operator==(const ComparableValue& rhs) const { + return value_ == rhs.value_; + } + + bool operator<(const ComparableValue& rhs) const { + return value_ < rhs.value_; + } + + private: + int value_; +}; + +} // namespace + +namespace webrtc { +namespace { + +TEST(STLUtilTest, STLIsSorted) { + { + std::set set; + set.insert(24); + set.insert(1); + set.insert(12); + EXPECT_TRUE(STLIsSorted(set)); + } + + { + std::set set; + set.insert(ComparableValue(24)); + set.insert(ComparableValue(1)); + set.insert(ComparableValue(12)); + EXPECT_TRUE(STLIsSorted(set)); + } + + { + std::vector vector; + vector.push_back(1); + vector.push_back(1); + vector.push_back(4); + vector.push_back(64); + vector.push_back(12432); + EXPECT_TRUE(STLIsSorted(vector)); + vector.back() = 1; + EXPECT_FALSE(STLIsSorted(vector)); + } +} + +TEST(STLUtilTest, STLSetDifference) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set difference; + difference.insert(1); + difference.insert(2); + EXPECT_EQ(difference, STLSetDifference >(a1, a2)); + } + + { + std::set difference; + difference.insert(5); + difference.insert(6); + difference.insert(7); + EXPECT_EQ(difference, STLSetDifference >(a2, a1)); + } + + { + std::vector difference; + difference.push_back(1); + difference.push_back(2); + EXPECT_EQ(difference, STLSetDifference >(a1, a2)); + } + + { + std::vector difference; + difference.push_back(5); + difference.push_back(6); + difference.push_back(7); + EXPECT_EQ(difference, STLSetDifference >(a2, a1)); + } +} + +TEST(STLUtilTest, STLSetUnion) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set result; + result.insert(1); + result.insert(2); + result.insert(3); + result.insert(4); + result.insert(5); + result.insert(6); + result.insert(7); + EXPECT_EQ(result, STLSetUnion >(a1, a2)); + } + + { + std::set result; + result.insert(1); + result.insert(2); + result.insert(3); + result.insert(4); + result.insert(5); + result.insert(6); + result.insert(7); + EXPECT_EQ(result, STLSetUnion >(a2, a1)); + } + + { + std::vector result; + result.push_back(1); + result.push_back(2); + result.push_back(3); + result.push_back(4); + result.push_back(5); + result.push_back(6); + result.push_back(7); + EXPECT_EQ(result, STLSetUnion >(a1, a2)); + } + + { + std::vector result; + result.push_back(1); + result.push_back(2); + result.push_back(3); + result.push_back(4); + result.push_back(5); + result.push_back(6); + result.push_back(7); + EXPECT_EQ(result, STLSetUnion >(a2, a1)); + } +} + +TEST(STLUtilTest, STLSetIntersection) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set result; + result.insert(3); + result.insert(4); + EXPECT_EQ(result, STLSetIntersection >(a1, a2)); + } + + { + std::set result; + result.insert(3); + result.insert(4); + EXPECT_EQ(result, STLSetIntersection >(a2, a1)); + } + + { + std::vector result; + result.push_back(3); + result.push_back(4); + EXPECT_EQ(result, STLSetIntersection >(a1, a2)); + } + + { + std::vector result; + result.push_back(3); + result.push_back(4); + EXPECT_EQ(result, STLSetIntersection >(a2, a1)); + } +} + +TEST(STLUtilTest, STLIncludes) { + std::set a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set a2; + a2.insert(3); + a2.insert(4); + + std::set a3; + a3.insert(3); + a3.insert(4); + a3.insert(5); + + EXPECT_TRUE(STLIncludes >(a1, a2)); + EXPECT_FALSE(STLIncludes >(a1, a3)); + EXPECT_FALSE(STLIncludes >(a2, a1)); + EXPECT_FALSE(STLIncludes >(a2, a3)); + EXPECT_FALSE(STLIncludes >(a3, a1)); + EXPECT_TRUE(STLIncludes >(a3, a2)); +} + +} // namespace +} // namespace webrtc + diff --git a/webrtc/system_wrappers/source/system_wrappers.gyp b/webrtc/system_wrappers/source/system_wrappers.gyp index cc79f0bcc7..2deefaa9d9 100644 --- a/webrtc/system_wrappers/source/system_wrappers.gyp +++ b/webrtc/system_wrappers/source/system_wrappers.gyp @@ -43,9 +43,11 @@ '../interface/rw_lock_wrapper.h', '../interface/scoped_ptr.h', '../interface/scoped_refptr.h', + '../interface/scoped_vector.h', '../interface/sleep.h', '../interface/sort.h', '../interface/static_instance.h', + '../interface/stl_util.h', '../interface/stringize_macros.h', '../interface/thread_annotations.h', '../interface/thread_wrapper.h', diff --git a/webrtc/system_wrappers/source/system_wrappers_tests.gyp b/webrtc/system_wrappers/source/system_wrappers_tests.gyp index 06737d23c2..91ff0283b6 100644 --- a/webrtc/system_wrappers/source/system_wrappers_tests.gyp +++ b/webrtc/system_wrappers/source/system_wrappers_tests.gyp @@ -29,7 +29,9 @@ 'data_log_helpers_unittest.cc', 'data_log_c_helpers_unittest.c', 'data_log_c_helpers_unittest.h', + 'scoped_vector_unittest.cc', 'stringize_macros_unittest.cc', + 'stl_util_unittest.cc', 'thread_unittest.cc', 'thread_posix_unittest.cc', 'unittest_utilities_unittest.cc',