1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17#ifndef __TBB_test_container_move_support_H
18#define __TBB_test_container_move_support_H
19
20#include "harness.h"
21#include "harness_assert.h"
22#include "harness_allocator.h"
23#include "harness_state_trackable.h"
24
25#include "tbb/atomic.h"
26#include "tbb/aligned_space.h"
27#include "tbb/internal/_allocator_traits.h"
28
29#include <stdexcept>
30#include <string>
31#include <functional>
32
33tbb::atomic<size_t> FooCount;
34size_t MaxFooCount = 0;
35
36//! Exception for concurrent_container
37class Foo_exception : public std::bad_alloc {
38public:
39 virtual const char *what() const throw() __TBB_override { return "out of Foo limit"; }
40 virtual ~Foo_exception() throw() {}
41};
42
43struct FooLimit {
44 FooLimit(){
45 if(MaxFooCount && FooCount >= MaxFooCount)
46 __TBB_THROW( Foo_exception() );
47 }
48};
49
50static const intptr_t initial_value_of_bar = 42;
51
52
53struct Foo : FooLimit, Harness::StateTrackable<true>{
54 typedef Harness::StateTrackable<true> StateTrackable;
55 intptr_t my_bar;
56public:
57 bool is_valid_or_zero() const{
58 return is_valid()||(state==ZeroInitialized && !my_bar);
59 }
60 intptr_t& zero_bar(){
61 ASSERT( is_valid_or_zero(), NULL );
62 return my_bar;
63 }
64 intptr_t zero_bar() const{
65 ASSERT( is_valid_or_zero(), NULL );
66 return my_bar;
67 }
68 intptr_t& bar(){
69 ASSERT( is_valid(), NULL );
70 return my_bar;
71 }
72 intptr_t bar() const{
73 ASSERT( is_valid(), NULL );
74 return my_bar;
75 }
76 operator intptr_t() const{
77 return this->bar();
78 }
79 Foo( intptr_t barr ): StateTrackable(0){
80 my_bar = barr;
81 FooCount++;
82 }
83 Foo(){
84 my_bar = initial_value_of_bar;
85 FooCount++;
86 }
87 Foo( const Foo& foo ): FooLimit(), StateTrackable(foo){
88 my_bar = foo.my_bar;
89 FooCount++;
90 }
91#if __TBB_CPP11_RVALUE_REF_PRESENT
92 Foo( Foo&& foo ): FooLimit(), StateTrackable(std::move(foo)){
93 my_bar = foo.my_bar;
94 //TODO: consider not using constant here, instead something like ~my_bar
95 foo.my_bar = -1;
96 FooCount++;
97 }
98#endif
99 ~Foo(){
100 my_bar = ~initial_value_of_bar;
101 if(state != ZeroInitialized) --FooCount;
102 }
103 friend bool operator==(const int &lhs, const Foo &rhs) {
104 ASSERT( rhs.is_valid_or_zero(), "comparing invalid objects ?" );
105 return lhs == rhs.my_bar;
106 }
107 friend bool operator==(const Foo &lhs, const int &rhs) {
108 ASSERT( lhs.is_valid_or_zero(), "comparing invalid objects ?" );
109 return lhs.my_bar == rhs;
110 }
111 friend bool operator==(const Foo &lhs, const Foo &rhs) {
112 ASSERT( lhs.is_valid_or_zero(), "comparing invalid objects ?" );
113 ASSERT( rhs.is_valid_or_zero(), "comparing invalid objects ?" );
114 return lhs.my_bar == rhs.my_bar;
115 }
116 friend bool operator<(const Foo &lhs, const Foo &rhs) {
117 ASSERT( lhs.is_valid_or_zero(), "comparing invalid objects ?" );
118 ASSERT( rhs.is_valid_or_zero(), "comparing invalid objects ?" );
119 return lhs.my_bar < rhs.my_bar;
120 }
121 bool is_const() const {return true;}
122 bool is_const() {return false;}
123protected:
124 char reserve[1];
125 Foo& operator=( const Foo& x ) {
126 StateTrackable::operator=(x);
127 my_bar = x.my_bar;
128 return *this;
129 }
130#if __TBB_CPP11_RVALUE_REF_PRESENT
131 Foo& operator=( Foo&& x ) {
132 ASSERT( x.is_valid_or_zero(), "bad source for assignment" );
133 ASSERT( is_valid_or_zero(), NULL );
134 StateTrackable::operator=(std::move(x));
135 my_bar = x.my_bar;
136 x.my_bar = -1;
137 return *this;
138 }
139#endif
140};
141
142struct FooWithAssign: public Foo {
143 FooWithAssign() : Foo(){}
144 FooWithAssign(intptr_t barr) : Foo(barr){}
145 FooWithAssign(FooWithAssign const& f) : Foo(f) {}
146 FooWithAssign& operator=(FooWithAssign const& f) { return static_cast<FooWithAssign&>(Foo::operator=(f)); }
147
148
149#if __TBB_CPP11_RVALUE_REF_PRESENT
150 FooWithAssign(FooWithAssign && f) : Foo(std::move(f)) {}
151 FooWithAssign& operator=(FooWithAssign && f) { return static_cast<FooWithAssign&>(Foo::operator=(std::move(f))); }
152#endif
153};
154
155template<typename FooIteratorType>
156class FooIteratorBase {
157protected:
158 intptr_t x_bar;
159private:
160 FooIteratorType& as_derived(){ return *static_cast<FooIteratorType*>(this);}
161public:
162 FooIteratorBase(intptr_t x) {
163 x_bar = x;
164 }
165 FooIteratorType &operator++() {
166 x_bar++; return as_derived();
167 }
168 FooIteratorType operator++(int) {
169 FooIteratorType tmp(as_derived()); x_bar++; return tmp;
170 }
171 friend bool operator==(const FooIteratorType & lhs, const FooIteratorType & rhs){ return lhs.x_bar == rhs.x_bar; }
172 friend bool operator!=(const FooIteratorType & lhs, const FooIteratorType & rhs){ return !(lhs == rhs); }
173};
174
175class FooIterator: public std::iterator<std::input_iterator_tag,FooWithAssign>, public FooIteratorBase<FooIterator> {
176public:
177 FooIterator(intptr_t x): FooIteratorBase<FooIterator>(x) {}
178
179 FooWithAssign operator*() {
180 return FooWithAssign(x_bar);
181 }
182};
183
184class FooPairIterator: public std::iterator<std::input_iterator_tag, std::pair<FooWithAssign,FooWithAssign> >, public FooIteratorBase<FooPairIterator> {
185public:
186 FooPairIterator(intptr_t x): FooIteratorBase<FooPairIterator>(x) {}
187
188 std::pair<FooWithAssign,FooWithAssign> operator*() {
189 FooWithAssign foo; foo.bar() = x_bar;
190
191 return std::make_pair(foo, foo);
192 }
193};
194
195namespace FooTests{
196 template<typename Foo_type>
197 void TestDefaultConstructor(){
198 Foo_type src;
199 ASSERT(src.state == Foo::DefaultInitialized, "incorrect state for default constructed Foo (derived) ?");
200 }
201
202 template<typename Foo_type>
203 void TestDirectConstructor(){
204 Foo_type src(1);
205 ASSERT(src.state == Foo::DirectInitialized, "incorrect state for direct constructed Foo (derived) ?");
206 }
207
208 template<typename Foo_type>
209 void TestCopyConstructor(){
210 Foo_type src;
211 Foo_type dst(src);
212 ASSERT(dst.state == Foo::CopyInitialized, "incorrect state for Copy constructed Foo ?");
213 }
214
215 template<typename Foo_type>
216 void TestAssignOperator(){
217 Foo_type src;
218 Foo_type dst;
219 dst = (src);
220
221 ASSERT(dst.state == Foo::Assigned, "incorrect state for Assigned Foo ?");
222 }
223
224#if __TBB_CPP11_RVALUE_REF_PRESENT
225 template<typename Foo_type>
226 void TestMoveConstructor(){
227 Foo_type src;
228 Foo_type dst(std::move(src));
229 ASSERT(dst.state == Foo::MoveInitialized, "incorrect state for Move constructed Foo ?");
230 ASSERT(src.state == Foo::MovedFrom, "incorrect state for Move from Foo ?");
231 }
232
233 template<typename Foo_type>
234 void TestMoveAssignOperator(){
235 Foo_type src;
236 Foo_type dst;
237 dst = std::move(src);
238
239 ASSERT(dst.state == Foo::MoveAssigned, "incorrect state for Move Assigned Foo ?");
240 ASSERT(src.state == Foo::MovedFrom, "incorrect state for Moved from Foo ?");
241 }
242#if TBB_USE_EXCEPTIONS
243 void TestMoveConstructorException();
244#endif //TBB_USE_EXCEPTIONS
245#endif //__TBB_CPP11_RVALUE_REF_PRESENT
246}
247
248void TestFoo(){
249 using namespace FooTests;
250 TestDefaultConstructor<Foo>();
251 TestDefaultConstructor<FooWithAssign>();
252 TestDirectConstructor<Foo>();
253 TestDirectConstructor<FooWithAssign>();
254 TestCopyConstructor<Foo>();
255 TestCopyConstructor<FooWithAssign>();
256 TestAssignOperator<FooWithAssign>();
257#if __TBB_CPP11_RVALUE_REF_PRESENT
258 TestMoveConstructor<Foo>();
259 TestMoveConstructor<FooWithAssign>();
260 TestMoveAssignOperator<FooWithAssign>();
261#if TBB_USE_EXCEPTIONS && !__TBB_CPP11_EXCEPTION_IN_STATIC_TEST_BROKEN
262 TestMoveConstructorException();
263#endif //TBB_USE_EXCEPTIONS
264#endif //__TBB_CPP11_RVALUE_REF_PRESENT
265}
266
267//TODO: replace _IN_TEST with separately defined macro IN_TEST(msg,test_name)
268#define ASSERT_IN_TEST(p,message,test_name) ASSERT(p, (std::string(test_name) + ": " + message).c_str());
269//TODO: move to harness_assert
270#define ASSERT_THROWS_IN_TEST(expression, exception_type, message, test_name) \
271 try{ \
272 expression; \
273 ASSERT_IN_TEST(false, "should throw an exception", test_name); \
274 }catch(exception_type &){ \
275 }catch(...){ASSERT_IN_TEST(false, "unexpected exception", test_name);} \
276
277#define ASSERT_THROWS(expression, exception_type, message) ASSERT_THROWS_IN_TEST(expression, exception_type, message, "")
278
279template<Harness::StateTrackableBase::StateValue desired_state, bool allow_zero_initialized_state>
280bool is_state(Harness::StateTrackable<allow_zero_initialized_state> const& f){ return f.state == desired_state;}
281
282template<Harness::StateTrackableBase::StateValue desired_state>
283struct is_not_state_f {
284 template <bool allow_zero_initialized_state>
285 bool operator()(Harness::StateTrackable<allow_zero_initialized_state> const& f){ return !is_state<desired_state>(f);}
286};
287
288template<Harness::StateTrackableBase::StateValue desired_state>
289struct is_state_f {
290 template <bool allow_zero_initialized_state>
291 bool operator()(Harness::StateTrackable<allow_zero_initialized_state> const& f){ return is_state<desired_state>(f); }
292 //TODO: cu_map defines key as a const thus by default it is not moved, instead it is copied. Investigate how std::unordered_map behaves
293 template<typename T1, typename T2>
294 bool operator()(std::pair<T1, T2> const& p){ return /*is_state<desired_state>(p.first) && */is_state<desired_state>(p.second); }
295};
296
297template<typename iterator, typename unary_predicate>
298bool all_of(iterator begin, iterator const& end, unary_predicate p){
299 for (; begin != end; ++begin){
300 if ( !p(*begin)) return false;
301 }
302 return true;
303}
304
305template<typename container, typename unary_predicate>
306bool all_of(container const& c, unary_predicate p){
307 return ::all_of( c.begin(), c.end(), p );
308}
309
310void TestAllOf(){
311 Foo foos[] = {Foo(), Foo(), Foo()};
312 ASSERT(::all_of(foos, Harness::end(foos), is_state_f<Foo::DefaultInitialized>()), "all_of returned false while true expected");
313 ASSERT(! ::all_of(foos, Harness::end(foos), is_state_f<Foo::CopyInitialized>()), "all_of returned true while false expected ");
314}
315
316template<typename static_counter_allocator_type>
317struct track_allocator_memory: NoCopy{
318 typedef typename static_counter_allocator_type::counters_t counters_t;
319
320 counters_t previous_state;
321 const char* const test_name;
322 track_allocator_memory(const char* a_test_name): test_name(a_test_name) { static_counter_allocator_type::init_counters(); }
323 ~track_allocator_memory(){verify_no_allocator_memory_leaks();}
324
325 void verify_no_allocator_memory_leaks() const{
326 ASSERT_IN_TEST( static_counter_allocator_type::items_allocated == static_counter_allocator_type::items_freed, "memory leak?", test_name );
327 ASSERT_IN_TEST( static_counter_allocator_type::allocations == static_counter_allocator_type::frees, "memory leak?", test_name );
328 }
329 void save_allocator_counters(){ previous_state = static_counter_allocator_type::counters(); }
330 void verify_no_more_than_x_memory_items_allocated(size_t expected_number_of_items_to_allocate){
331 counters_t now = static_counter_allocator_type::counters();
332 ASSERT_IN_TEST( (now.items_allocated - previous_state.items_allocated) <= expected_number_of_items_to_allocate, "More then excepted memory allocated ?", test_name );
333 }
334};
335
336#include <vector>
337template<int line_n>
338struct track_foo_count: NoCopy{
339 bool active;
340 size_t previous_state;
341 const char* const test_name;
342 track_foo_count(const char* a_test_name): active(true), previous_state(FooCount), test_name(a_test_name) { }
343 ~track_foo_count(){
344 if (active){
345 this->verify_no_undestroyed_foo_left_and_dismiss();
346 }
347 }
348
349 //TODO: ideally in most places this check should be replaced with "no foo created or destroyed"
350 //TODO: deactivation of the check seems like a hack
351 void verify_no_undestroyed_foo_left_and_dismiss() {
352 ASSERT_IN_TEST( FooCount == previous_state, "Some instances of Foo were not destroyed ?", test_name );
353 active = false;
354 }
355};
356
357//TODO: inactive mode in these limiters is a temporary workaround for usage in exception type loop of TestException
358
359struct limit_foo_count_in_scope: NoCopy{
360 size_t previous_state;
361 bool active;
362 limit_foo_count_in_scope(size_t new_limit, bool an_active = true): previous_state(MaxFooCount), active(an_active) {
363 if (active){
364 MaxFooCount = new_limit;
365 }
366 }
367 ~limit_foo_count_in_scope(){
368 if (active) {
369 MaxFooCount = previous_state;
370 }
371 }
372};
373
374template<typename static_counter_allocator_type>
375struct limit_allocated_items_in_scope: NoCopy{
376 size_t previous_state;
377 bool active;
378 limit_allocated_items_in_scope(size_t new_limit, bool an_active = true) : previous_state(static_counter_allocator_type::max_items), active(an_active) {
379 if (active){
380 static_counter_allocator_type::set_limits(new_limit);
381 }
382 }
383 ~limit_allocated_items_in_scope(){
384 if (active) {
385 static_counter_allocator_type::set_limits(previous_state);
386 }
387 }
388};
389
390struct default_container_traits{
391 template <typename container_type, typename iterator_type>
392 static container_type& construct_container(tbb::aligned_space<container_type> & storage, iterator_type begin, iterator_type end){
393 new (storage.begin()) container_type(begin, end);
394 return *storage.begin();
395 }
396
397 template <typename container_type, typename iterator_type, typename allocator_type>
398 static container_type& construct_container(tbb::aligned_space<container_type> & storage, iterator_type begin, iterator_type end, allocator_type const& a){
399 new (storage.begin()) container_type(begin, end, a);
400 return *storage.begin();
401 }
402};
403
404struct memory_locations {
405 std::vector<const void*> locations;
406
407 template <typename container_type>
408 memory_locations(container_type const& source) : locations(source.size()){
409 for (typename container_type::const_iterator it = source.begin(); it != source.end(); ++it){locations[std::distance(source.begin(), it)] = & *it;}
410 }
411
412 template <typename container_t>
413 bool content_location_unchanged(container_t const& dst){
414 struct is_same_location{
415 static bool compare(typename container_t::value_type const& v, const void* location){ return &v == location;}
416 };
417
418 return std::equal(dst.begin(), dst.end(), locations.begin(), &is_same_location::compare);
419 }
420
421 template <typename container_t>
422 bool content_location_changed(container_t const& dst){
423 struct is_not_same_location{
424 static bool compare(typename container_t::value_type const& v, const void* location){ return &v != location;}
425 };
426
427 return std::equal(dst.begin(), dst.end(), locations.begin(), &is_not_same_location::compare);
428 }
429
430};
431
432#if __TBB_CPP11_RVALUE_REF_PRESENT
433#include <algorithm>
434void TestMemoryLocaionsHelper(){
435 const size_t test_sequence_len = 15;
436 std::vector<char> source(test_sequence_len, 0);
437 std::generate_n(source.begin(), source.size(), Harness::FastRandomBody<char>(1));
438
439 memory_locations source_memory_locations((source));
440
441 std::vector<char> copy((source));
442 ASSERT(source_memory_locations.content_location_changed(copy), "");
443
444 std::vector<char> alias(std::move(source));
445 ASSERT(source_memory_locations.content_location_unchanged(alias), "");
446}
447namespace FooTests{
448#if TBB_USE_EXCEPTIONS
449 void TestMoveConstructorException(){
450 Foo src;
451 const Foo::StateValue source_state_before = src.state;
452 ASSERT_THROWS_IN_TEST(
453 {
454 limit_foo_count_in_scope foo_limit(FooCount);
455 Foo f1(std::move(src));
456 },
457 std::bad_alloc, "", "TestLimitInstancesNumber"
458 );
459 ASSERT(source_state_before == src.state, "state of source changed while should not?");
460 }
461#endif //TBB_USE_EXCEPTIONS
462}
463
464template<typename container_traits, typename allocator_t>
465struct move_fixture : NoCopy{
466 typedef typename allocator_t::value_type element_type;
467 typedef typename container_traits:: template apply<element_type, allocator_t>::type container_t;
468 typedef typename container_traits::init_iterator_type init_iterator_type;
469 enum {default_container_size = 100};
470 const size_t container_size;
471 tbb::aligned_space<container_t> source_storage;
472 container_t & source;
473 //check that location of _all_ elements of container under test is changed/unchanged
474 memory_locations locations;
475
476 ~move_fixture(){
477 source_storage.begin()->~container_t();
478 }
479
480 const char* const test_name;
481 move_fixture(const char* a_test_name, size_t a_container_size = default_container_size )
482 : container_size(a_container_size)
483 , source(container_traits::construct_container(source_storage, init_iterator_type(0), init_iterator_type(container_size)))
484 , locations(source)
485 , test_name(a_test_name)
486 {
487 init("move_fixture::move_fixture()");
488 }
489
490 move_fixture(const char* a_test_name, allocator_t const& a, size_t a_container_size = default_container_size)
491 : container_size(a_container_size)
492 , source(container_traits::construct_container(source_storage, init_iterator_type(0), init_iterator_type(container_size), a))
493 , locations(source)
494 , test_name(a_test_name)
495 {
496 init("move_fixture::move_fixture(allocator_t const& a)");
497 }
498
499 void init(const std::string& ctor_name){
500 verify_size(source, ctor_name.c_str());
501 verify_content_equal_to_source(source, "did not properly initialized source? Or can not check container for equality with expected ?: " + ctor_name);
502 verify_size(locations.locations, "move_fixture:init ");
503 }
504
505 bool content_location_unchanged(container_t const& dst){
506 return locations.content_location_unchanged(dst);
507 }
508
509 bool content_location_changed(container_t const& dst){
510 return locations.content_location_changed(dst);
511 }
512
513 template<typename container_type>
514 void verify_size(container_type const& dst, const char* a_test_name){
515 ASSERT_IN_TEST(container_size == dst.size(), "Did not construct all the elements or allocate enough memory?, while should ?", a_test_name);
516 }
517
518 void verify_content_equal_to_source(container_t const& dst, const std::string& msg){
519 ASSERT_IN_TEST( container_traits::equal(dst, init_iterator_type(0), init_iterator_type(container_size)), msg.c_str(), test_name);
520 }
521
522 void verify_content_equal_to_source(container_t const& dst){
523 verify_content_equal_to_source(dst, "content changed during move/copy ?");
524 }
525
526 void verify_content_equal_to_source(container_t const& dst, size_t number_of_constructed_items){
527 ASSERT_IN_TEST(number_of_constructed_items <= dst.size(), "incorrect test expectation/input parameters?", test_name);
528 ASSERT_IN_TEST(std::equal(dst.begin(), dst.begin() + number_of_constructed_items, init_iterator_type(0)), "content changed during move/copy ?", test_name);
529 }
530
531 //TODO: better name ? e.g. "content_was_stolen"
532 void verify_content_shallow_moved(container_t const& dst){
533 verify_size(dst, test_name);
534 ASSERT_IN_TEST(content_location_unchanged(dst), "container move constructor actually changed element locations, while should not", test_name);
535 ASSERT_IN_TEST(source.empty(), "Moved from container instance should not contain any elements", test_name);
536 verify_content_equal_to_source(dst);
537 }
538
539 //TODO: better name ? e.g. "element move"
540 void verify_content_deep_moved(container_t const& dst){
541 verify_size(dst, test_name);
542 ASSERT_IN_TEST(content_location_changed(dst), "container actually did not changed element locations for unequal allocators, while should", test_name);
543 ASSERT_IN_TEST(all_of(dst, is_state_f<Foo::MoveInitialized>()), "container did not move construct some elements?", test_name);
544 ASSERT_IN_TEST(all_of(source, is_state_f<Foo::MovedFrom>()), "container did not move all the elements?", test_name);
545 verify_content_equal_to_source(dst);
546 }
547
548 void verify_part_of_content_deep_moved(container_t const& dst, size_t number_of_constructed_items){
549 ASSERT_IN_TEST(content_location_changed(dst), "Vector actually did not changed element locations for unequal allocators, while should", test_name);
550 ASSERT_IN_TEST(::all_of(dst.begin(), dst.begin() + number_of_constructed_items, is_state_f<Foo::MoveInitialized>()), "Vector did not move construct some elements?", test_name);
551 if (dst.size() != number_of_constructed_items) {
552 ASSERT_IN_TEST(::all_of(dst.begin() + number_of_constructed_items, dst.end(), is_state_f<Foo::ZeroInitialized>()), "Failed to zero-initialize items left not constructed after the exception?", test_name );
553 }
554 verify_content_equal_to_source(dst, number_of_constructed_items);
555
556 ASSERT_IN_TEST(::all_of(source.begin(), source.begin() + number_of_constructed_items, is_state_f<Foo::MovedFrom>()), "Vector did not move all the elements?", test_name);
557 ASSERT_IN_TEST(::all_of(source.begin() + number_of_constructed_items, source.end(), is_not_state_f<Foo::MovedFrom>()), "Vector changed elements in source after exception point?", test_name);
558 }
559};
560
561
562template <typename T, typename pocma = Harness::false_type>
563struct arena_allocator_fixture : NoCopy{
564 typedef arena<T, pocma> allocator_t;
565 typedef typename allocator_t::arena_data_t arena_data_t;
566
567 std::vector<tbb::aligned_space<T, 1> > storage;
568 arena_data_t arena_data;
569 allocator_t allocator;
570
571 arena_allocator_fixture(size_t size_to_allocate)
572 : storage(size_to_allocate)
573 , arena_data((*storage.begin()).begin(), storage.size())
574 , allocator(arena_data)
575 {}
576};
577
578//TODO: add ability to inject debug_allocator into stateful_allocator_fixture::allocator_t
579template <typename T, typename pocma = Harness::false_type>
580struct two_memory_arenas_fixture : NoCopy{
581 typedef arena_allocator_fixture<T, pocma> arena_fixture_t;
582 typedef typename arena_fixture_t::allocator_t allocator_t;
583
584 arena_fixture_t source_arena_fixture;
585 arena_fixture_t dst_arena_fixture;
586
587 allocator_t& source_allocator;
588 allocator_t& dst_allocator;
589
590 const char* test_name;
591
592 two_memory_arenas_fixture(size_t size_to_allocate, const char* a_test_name)
593 : source_arena_fixture(size_to_allocate)
594 , dst_arena_fixture(size_to_allocate)
595 , source_allocator(source_arena_fixture.allocator)
596 , dst_allocator(dst_arena_fixture.allocator)
597 , test_name(a_test_name)
598 {
599 ASSERT_IN_TEST(&*source_arena_fixture.storage.begin() != &*dst_arena_fixture.storage.begin(), "source and destination arena instances should use different memory regions", test_name);
600 ASSERT_IN_TEST(source_allocator != dst_allocator, "arenas using different memory regions should not compare equal", test_name);
601 ASSERT_IN_TEST(pocma::value == tbb::internal::allocator_traits<allocator_t>::propagate_on_container_move_assignment::value, "This test require proper allocator_traits support", test_name);
602
603 //Some ISO C++11 allocator requirements enforcement:
604 allocator_t source_allocator_copy(source_allocator), dst(dst_allocator);
605 allocator_t source_previous_state(source_allocator);
606 ASSERT_IN_TEST(source_previous_state == source_allocator, "Copy of allocator should compare equal to it's source", test_name);
607 dst = std::move(source_allocator_copy);
608 ASSERT_IN_TEST(dst == source_previous_state, "Move initialized instance of allocator should compare equal to it's source state before movement", test_name);
609 }
610
611 void verify_allocator_was_moved(const allocator_t& result_allocator){
612 //TODO: add assert that allocator move constructor/assignment operator was called
613 ASSERT_IN_TEST(result_allocator == source_allocator, "allocator was not moved ?", test_name);
614 ASSERT_IN_TEST(result_allocator != dst_allocator, "allocator was not moved ?", test_name);
615 }
616
617// template <typename any_allocator_t>
618// void verify_allocator_was_moved(const any_allocator_t& ){}
619};
620
621template <typename pocma = Harness::false_type>
622struct std_stateful_allocator : NoCopy {
623 typedef stateful_allocator<FooWithAssign, pocma> allocator_t;
624
625 allocator_t source_allocator;
626 allocator_t dst_allocator;
627
628 const char* test_name;
629
630 std_stateful_allocator(size_t , const char* a_test_name)
631 : test_name(a_test_name)
632 {}
633
634 template <typename any_allocator_t>
635 void verify_allocator_was_moved(const any_allocator_t& ){}
636
637};
638
639template<typename container_traits, typename pocma = Harness::false_type, typename T = FooWithAssign>
640struct default_stateful_fixture_make_helper{
641// typedef std_stateful_allocator<pocma> allocator_fixture_t;
642 typedef two_memory_arenas_fixture<T, pocma> allocator_fixture_t;
643 typedef static_shared_counting_allocator<Harness::int_to_type<__LINE__>, typename allocator_fixture_t::allocator_t, std::size_t> allocator_t;
644
645 typedef move_fixture<container_traits, allocator_t> move_fixture_t;
646 typedef track_allocator_memory<allocator_t> no_leaks_t;
647 typedef track_foo_count<__LINE__> no_foo_leaks_in_fixture_t;
648 typedef track_foo_count<__LINE__> no_foo_leaks_in_test_t;
649
650 struct default_stateful_fixture : no_leaks_t, private no_foo_leaks_in_fixture_t, allocator_fixture_t, move_fixture_t, no_foo_leaks_in_test_t {
651
652 default_stateful_fixture(const char* a_test_name)
653 : no_leaks_t(a_test_name)
654 , no_foo_leaks_in_fixture_t(a_test_name)
655 //TODO: calculate needed size more accurately
656 //allocate twice more storage to handle case when copy constructor called instead of move one
657 , allocator_fixture_t(2*4 * move_fixture_t::default_container_size, a_test_name)
658 , move_fixture_t(a_test_name, allocator_fixture_t::source_allocator)
659 , no_foo_leaks_in_test_t(a_test_name)
660 {
661 no_leaks_t::save_allocator_counters();
662 }
663
664 void verify_no_more_than_x_memory_items_allocated(){
665 no_leaks_t::verify_no_more_than_x_memory_items_allocated(container_traits::expected_number_of_items_to_allocate_for_steal_move);
666 }
667 using no_foo_leaks_in_test_t::verify_no_undestroyed_foo_left_and_dismiss;
668 typedef typename move_fixture_t::container_t::allocator_type allocator_t;
669 };
670
671 typedef default_stateful_fixture type;
672};
673
674template<typename container_traits>
675void TestMoveConstructorSingleArgument(){
676 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
677 typedef typename fixture_t::container_t container_t;
678
679 fixture_t fixture("TestMoveConstructorSingleArgument");
680
681 container_t dst(std::move(fixture.source));
682
683 fixture.verify_content_shallow_moved(dst);
684 fixture.verify_allocator_was_moved(dst.get_allocator());
685 fixture.verify_no_more_than_x_memory_items_allocated();
686 fixture.verify_no_undestroyed_foo_left_and_dismiss();
687}
688
689template<typename container_traits>
690void TestMoveConstructorWithEqualAllocator(){
691 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
692 typedef typename fixture_t::container_t container_t;
693
694 fixture_t fixture("TestMoveConstructorWithEqualAllocator");
695
696 container_t dst(std::move(fixture.source), fixture.source.get_allocator());
697
698 fixture.verify_content_shallow_moved(dst);
699 fixture.verify_no_more_than_x_memory_items_allocated();
700 fixture.verify_no_undestroyed_foo_left_and_dismiss();
701}
702
703template<typename container_traits>
704void TestMoveConstructorWithUnEqualAllocator(){
705 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
706 typedef typename fixture_t::container_t container_t;
707
708 fixture_t fixture("TestMoveConstructorWithUnEqualAllocator");
709
710 container_t dst(std::move(fixture.source), fixture.dst_allocator);
711
712 fixture.verify_content_deep_moved(dst);
713}
714
715template<typename container_traits>
716void TestMoveConstructor(){
717 TestMoveConstructorSingleArgument<container_traits>();
718 TestMoveConstructorWithEqualAllocator<container_traits>();
719 TestMoveConstructorWithUnEqualAllocator<container_traits>();
720}
721
722template<typename container_traits>
723void TestMoveAssignOperatorPOCMAStateful(){
724 typedef typename default_stateful_fixture_make_helper<container_traits, Harness::true_type>::type fixture_t;
725 typedef typename fixture_t::container_t container_t;
726
727 fixture_t fixture("TestMoveAssignOperatorPOCMAStateful");
728
729 container_t dst(fixture.dst_allocator);
730
731 fixture.save_allocator_counters();
732
733 dst = std::move(fixture.source);
734
735 fixture.verify_content_shallow_moved(dst);
736 fixture.verify_allocator_was_moved(dst.get_allocator());
737 fixture.verify_no_more_than_x_memory_items_allocated();
738 fixture.verify_no_undestroyed_foo_left_and_dismiss();
739}
740
741template<typename container_traits>
742void TestMoveAssignOperatorPOCMANonStateful(){
743 typedef std::allocator<FooWithAssign> allocator_t;
744
745 typedef move_fixture<container_traits, allocator_t> fixture_t;
746 typedef typename fixture_t::container_t container_t;
747
748 fixture_t fixture("TestMoveAssignOperatorPOCMANonStateful");
749
750 ASSERT(fixture.source.get_allocator() == allocator_t(), "Incorrect test setup: allocator is stateful while should not?");
751
752 container_t dst;
753 dst = std::move(fixture.source);
754
755 fixture.verify_content_shallow_moved(dst);
756 //TODO: add an assert that allocator was "moved" when POCMA is set
757}
758
759template<typename container_traits>
760void TestMoveAssignOperatorNotPOCMAWithUnEqualAllocator(){
761 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
762 typedef typename fixture_t::container_t container_t;
763
764 fixture_t fixture("TestMoveAssignOperatorNotPOCMAWithUnEqualAllocator");
765
766 container_t dst(fixture.dst_allocator);
767 dst = std::move(fixture.source);
768
769 fixture.verify_content_deep_moved(dst);
770}
771
772template<typename container_traits>
773void TestMoveAssignOperatorNotPOCMAWithEqualAllocator(){
774 typedef typename default_stateful_fixture_make_helper<container_traits, Harness::false_type>::type fixture_t;
775 typedef typename fixture_t::container_t container_t;
776 fixture_t fixture("TestMoveAssignOperatorNotPOCMAWithEqualAllocator");
777
778 container_t dst(fixture.source_allocator);
779 ASSERT(fixture.source.get_allocator() == dst.get_allocator(), "Incorrect test setup: allocators are not equal while should be?");
780
781 fixture.save_allocator_counters();
782
783 dst = std::move(fixture.source);
784
785 fixture.verify_content_shallow_moved(dst);
786 fixture.verify_no_more_than_x_memory_items_allocated();
787 fixture.verify_no_undestroyed_foo_left_and_dismiss();
788}
789
790template<typename container_traits>
791void TestMoveAssignOperator(){
792#if __TBB_ALLOCATOR_TRAITS_PRESENT
793 TestMoveAssignOperatorPOCMANonStateful<container_traits>();
794 TestMoveAssignOperatorPOCMAStateful<container_traits>();
795#endif
796 TestMoveAssignOperatorNotPOCMAWithUnEqualAllocator<container_traits>();
797 TestMoveAssignOperatorNotPOCMAWithEqualAllocator<container_traits>();
798}
799
800template<typename container_traits>
801void TestConstructorWithMoveIterators(){
802 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
803 typedef typename fixture_t::container_t container_t;
804
805 fixture_t fixture("TestConstructorWithMoveIterators");
806
807 container_t dst(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end()), fixture.dst_allocator);
808
809 fixture.verify_content_deep_moved(dst);
810}
811
812template<typename container_traits>
813void TestAssignWithMoveIterators(){
814 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
815 typedef typename fixture_t::container_t container_t;
816
817 fixture_t fixture("TestAssignWithMoveIterators");
818
819 container_t dst(fixture.dst_allocator);
820 dst.assign(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end()));
821
822 fixture.verify_content_deep_moved(dst);
823}
824
825#if TBB_USE_EXCEPTIONS
826template<typename container_traits>
827void TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorMemoryFailure(){
828 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
829 typedef typename fixture_t::container_t container_t;
830 typedef typename container_t::allocator_type allocator_t;
831 const char* test_name = "TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorMemoryFailure";
832 fixture_t fixture(test_name);
833
834 limit_allocated_items_in_scope<allocator_t> allocator_limit(allocator_t::items_allocated + fixture.container_size/4);
835 ASSERT_THROWS_IN_TEST(container_t dst(std::move(fixture.source), fixture.dst_allocator), std::bad_alloc, "", test_name);
836}
837
838//TODO: add tests that verify that stealing move constructors/assign operators does not throw exceptions
839template<typename container_traits>
840void TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorExceptionInElementCtor(){
841 typedef typename default_stateful_fixture_make_helper<container_traits>::type fixture_t;
842 typedef typename fixture_t::container_t container_t;
843
844 const char* test_name = "TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorExceptionInElementCtor";
845 fixture_t fixture(test_name);
846
847 limit_foo_count_in_scope foo_limit(FooCount + fixture.container_size/4);
848 ASSERT_THROWS_IN_TEST(container_t dst(std::move(fixture.source), fixture.dst_allocator), std::bad_alloc, "", test_name);
849}
850#endif /* TBB_USE_EXCEPTIONS */
851#endif//__TBB_CPP11_RVALUE_REF_PRESENT
852
853namespace helper_stuff_tests {
854 void inline TestArena(){
855 typedef int arena_element;
856
857 arena_element arena_storage[10] = {0};
858 typedef arena<arena_element> arena_t;
859
860 arena_t::arena_data_t arena_data(arena_storage,Harness::array_length(arena_storage));
861 arena_t a(arena_data);
862
863 ASSERT(a.allocate(1) == arena_storage, "");
864 ASSERT(a.allocate(2) == &arena_storage[1], "");
865 ASSERT(a.allocate(2) == &arena_storage[2+1], "");
866 }
867
868 template<typename static_counting_allocator_type>
869 void inline TestStaticCountingAllocatorRebound(){
870 static_counting_allocator_type::set_limits(1);
871 typedef typename static_counting_allocator_type:: template rebind<std::pair<int,int> >::other rebound_type;
872 ASSERT(rebound_type::max_items == static_counting_allocator_type::max_items, "rebound allocator should use the same limits");
873 static_counting_allocator_type::set_limits(0);
874 }
875
876 void inline TestStatefulAllocator(){
877 stateful_allocator<int> a1,a2;
878 stateful_allocator<int> copy_of_a1(a1);
879 ASSERT(a1 != a2,"non_equal_allocator are designed to simulate stateful allocators");
880 ASSERT(copy_of_a1 == a1,"");
881 }
882}
883struct TestHelperStuff{
884 TestHelperStuff(){
885 using namespace helper_stuff_tests;
886 TestFoo();
887 TestAllOf();
888 TestArena();
889 TestStaticCountingAllocatorRebound<static_shared_counting_allocator<int, arena<int> > >();
890 TestStatefulAllocator();
891#if __TBB_CPP11_RVALUE_REF_PRESENT
892 TestMemoryLocaionsHelper();
893#endif //__TBB_CPP11_RVALUE_REF_PRESENT
894 }
895};
896static TestHelperStuff TestHelperStuff_s;
897#endif /* __TBB_test_container_move_support_H */
898