| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-present eyeo GmbH | 
|  | 4  * | 
|  | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 6  * it under the terms of the GNU General Public License version 3 as | 
|  | 7  * published by the Free Software Foundation. | 
|  | 8  * | 
|  | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
|  | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12  * GNU General Public License for more details. | 
|  | 13  * | 
|  | 14  * You should have received a copy of the GNU General Public License | 
|  | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 16  */ | 
|  | 17 | 
|  | 18 #include <future> | 
|  | 19 #include <gtest/gtest.h> | 
|  | 20 #include <AdblockPlus/AsyncExecutor.h> | 
|  | 21 | 
|  | 22 using namespace AdblockPlus; | 
|  | 23 | 
|  | 24 | 
|  | 25 // For present gtest does not provide public API to have value- and type- | 
|  | 26 // parameterized tests, so the approach is to accumalte the common code in | 
|  | 27 // BaseAsyncExecutorTest, then define it with different types and then for each | 
|  | 28 // type instantiate value-parameterized tests and call the common test body. | 
|  | 29 | 
|  | 30 template<typename Executor> | 
|  | 31 struct BaseAsyncExecutorTestTraits; | 
|  | 32 | 
|  | 33 typedef ::testing::tuple<uint16_t, // number of procucers | 
|  | 34                          uint16_t  // number of tasks issued by each producer | 
|  | 35                         > BaseAsyncExecutorTestParams; | 
|  | 36 | 
|  | 37 template<typename TExecutor> | 
|  | 38 class BaseAsyncExecutorTest : public ::testing::TestWithParam<BaseAsyncExecutorT
     estParams> | 
|  | 39 { | 
|  | 40 public: | 
|  | 41   typedef typename ::testing::tuple_element<0, ParamType>::type ProducersNumber; | 
|  | 42   typedef typename ::testing::tuple_element<1, ParamType>::type ProducerTasksNum
     ber; | 
|  | 43 | 
|  | 44   typedef BaseAsyncExecutorTestTraits<TExecutor> Traits; | 
|  | 45   typedef typename Traits::Executor Executor; | 
|  | 46 | 
|  | 47 protected: | 
|  | 48   void SetUp() override | 
|  | 49   { | 
|  | 50     testThreadID = std::this_thread::get_id(); | 
|  | 51   } | 
|  | 52 | 
|  | 53   void MultithreadedCallsTest(); | 
|  | 54 | 
|  | 55   std::thread::id testThreadID; | 
|  | 56 }; | 
|  | 57 | 
|  | 58 template<> | 
|  | 59 struct BaseAsyncExecutorTestTraits<ActiveObject> | 
|  | 60 { | 
|  | 61   typedef ActiveObject Executor; | 
|  | 62   typedef std::vector<uint32_t> PayloadResults; | 
|  | 63 | 
|  | 64   static void Dispatch(typename BaseAsyncExecutorTest<Executor>::Executor& execu
     tor, std::function<void()> call) | 
|  | 65   { | 
|  | 66     executor.Post(std::move(call)); | 
|  | 67   } | 
|  | 68 }; | 
|  | 69 | 
|  | 70 template<> | 
|  | 71 struct BaseAsyncExecutorTestTraits<AsyncExecutor> | 
|  | 72 { | 
|  | 73   typedef AsyncExecutor Executor; | 
|  | 74 | 
|  | 75   // The API of this class is not thread-safe! It's merely a helper to | 
|  | 76   // generalize particular tests, don't use it anywhere else. | 
|  | 77   class PayloadResults : public SynchronizedCollection<std::vector<uint32_t>> | 
|  | 78   { | 
|  | 79   public: | 
|  | 80     typename Container::size_type size() const | 
|  | 81     { | 
|  | 82       return collection.size(); | 
|  | 83     } | 
|  | 84     typename Container::iterator begin() | 
|  | 85     { | 
|  | 86       return collection.begin(); | 
|  | 87     } | 
|  | 88     typename Container::iterator end() | 
|  | 89     { | 
|  | 90       return collection.end(); | 
|  | 91     } | 
|  | 92     typename Container::const_reference operator[](typename Container::size_type
      pos) const | 
|  | 93     { | 
|  | 94       return collection[pos]; | 
|  | 95     } | 
|  | 96   }; | 
|  | 97 | 
|  | 98   static void Dispatch(typename BaseAsyncExecutorTest<Executor>::Executor& execu
     tor, std::function<void()> call) | 
|  | 99   { | 
|  | 100     executor.Dispatch(std::move(call)); | 
|  | 101   } | 
|  | 102 }; | 
|  | 103 | 
|  | 104 template<typename Executor> | 
|  | 105 void BaseAsyncExecutorTest<Executor>::MultithreadedCallsTest() | 
|  | 106 { | 
|  | 107   typename Traits::PayloadResults results; | 
|  | 108 | 
|  | 109   std::promise<void> prepare; | 
|  | 110   std::shared_future<void> asyncPrepared = prepare.get_future(); | 
|  | 111   const ProducersNumber producersNumber = ::testing::get<0>(GetParam()); | 
|  | 112   const ProducerTasksNumber producerTasksNumber = ::testing::get<1>(GetParam()); | 
|  | 113   const uint32_t totalTasksNumber = producersNumber * producerTasksNumber; | 
|  | 114   { | 
|  | 115     Executor executor; | 
|  | 116     { | 
|  | 117       // Defining AsyncExecutor (async) after tested Executor ensures that all | 
|  | 118       // producers finish before destroying the Executor. Otherwise there could | 
|  | 119       // be cases when the Executor is already destroyed but payload task is not | 
|  | 120       // dispatched yet, what would result in a data race. | 
|  | 121       // It seems at least strange to use AsyncExecutor in the test of | 
|  | 122       // AsyncExecutor, but perhaps it can be considered as addition testing. | 
|  | 123       AsyncExecutor async; | 
|  | 124       for (ProducersNumber producer = 0; producer < producersNumber; ++producer) | 
|  | 125       { | 
|  | 126         auto producerTask = [producerTasksNumber, this, &executor, producer, &re
     sults](std::thread::id producerThreadID) | 
|  | 127         { | 
|  | 128           for (ProducerTasksNumber task = 0; task < producerTasksNumber; ++task) | 
|  | 129           { | 
|  | 130             auto id = producer * producerTasksNumber + task; | 
|  | 131             Traits::Dispatch(executor, /*payload task*/[this, id, &results, prod
     ucerThreadID] | 
|  | 132             { | 
|  | 133               results.push_back(id); | 
|  | 134               EXPECT_NE(producerThreadID, std::this_thread::get_id()); | 
|  | 135               EXPECT_NE(testThreadID, std::this_thread::get_id()); | 
|  | 136             }); | 
|  | 137           } | 
|  | 138         }; | 
|  | 139         async.Dispatch([this, producerTask, asyncPrepared] | 
|  | 140         { | 
|  | 141           const auto producerThreadID = std::this_thread::get_id(); | 
|  | 142           EXPECT_NE(testThreadID, producerThreadID); | 
|  | 143           asyncPrepared.wait(); | 
|  | 144           producerTask(producerThreadID); | 
|  | 145         }); | 
|  | 146         std::this_thread::yield(); | 
|  | 147       } | 
|  | 148       // it's not ideal because some threads can still have not reached | 
|  | 149       // asyncPrepared.wait() but it's good enough, in particular yield should | 
|  | 150       // be helpful there. | 
|  | 151       prepare.set_value(); | 
|  | 152     } | 
|  | 153   } | 
|  | 154 | 
|  | 155   std::sort(results.begin(), results.end()); | 
|  | 156   // ensure that all tasks are executed by finding their IDs | 
|  | 157   ASSERT_EQ(totalTasksNumber, results.size()); | 
|  | 158   for (uint16_t id = 0; id < totalTasksNumber; ++id) { | 
|  | 159     EXPECT_EQ(id, results[id]); | 
|  | 160   } | 
|  | 161 } | 
|  | 162 | 
|  | 163 namespace | 
|  | 164 { | 
|  | 165 | 
|  | 166   std::string humanReadbleParams(const ::testing::TestParamInfo<BaseAsyncExecuto
     rTestParams> paramInfo) | 
|  | 167   { | 
|  | 168     std::stringstream ss; | 
|  | 169     ss << "producers_" << ::testing::get<0>(paramInfo.param) << "_tasks_" << ::t
     esting::get<1>(paramInfo.param); | 
|  | 170     return ss.str(); | 
|  | 171   } | 
|  | 172 | 
|  | 173   // The reason for such splitting for different implementations and different | 
|  | 174   // platforms is the limited number of threads and time to run tests on | 
|  | 175   // travis-ci. | 
|  | 176   // E.g. 100 x 1000 is already too much, so the generators are split. | 
|  | 177   // 0 - 10      1 - 100, 1000 | 
|  | 178   const auto MultithreadedCallsGenerator1 = ::testing::Combine(::testing::Values
     (0, 1, 2, 3, 5, 10), | 
|  | 179                                                                ::testing::Values
     (1, 2, 3, 5, 10, 100, 1000)); | 
|  | 180   //        100, 1 - 100 | 
|  | 181   const auto MultithreadedCallsGenerator2 = ::testing::Combine(::testing::Values
     (100), | 
|  | 182                                                                ::testing::Values
     (1, 2, 3, 5, 10, 100)); | 
|  | 183   // 0 - 10                    2000|4000 | 
|  | 184   const auto MultithreadedCallsGenerator3 = ::testing::Combine(::testing::Values
     (0, 1, 2, 3, 5, 10), | 
|  | 185 #ifdef WIN32 | 
|  | 186     ::testing::Values(4000) | 
|  | 187 #else | 
|  | 188     ::testing::Values(2000) | 
|  | 189 #endif | 
|  | 190   ); | 
|  | 191 | 
|  | 192   typedef BaseAsyncExecutorTest<ActiveObject> ActiveObjectTest; | 
|  | 193 | 
|  | 194   INSTANTIATE_TEST_CASE_P(DifferentProducersNumber1, | 
|  | 195     ActiveObjectTest, | 
|  | 196     MultithreadedCallsGenerator1, | 
|  | 197     humanReadbleParams); | 
|  | 198 | 
|  | 199   INSTANTIATE_TEST_CASE_P(DifferentProducersNumber2, | 
|  | 200     ActiveObjectTest, | 
|  | 201     MultithreadedCallsGenerator2, | 
|  | 202     humanReadbleParams); | 
|  | 203 | 
|  | 204   INSTANTIATE_TEST_CASE_P(DifferentProducersNumber3, | 
|  | 205     ActiveObjectTest, | 
|  | 206     MultithreadedCallsGenerator3, | 
|  | 207     humanReadbleParams); | 
|  | 208 | 
|  | 209   TEST_P(ActiveObjectTest, MultithreadedCalls) | 
|  | 210   { | 
|  | 211     MultithreadedCallsTest(); | 
|  | 212   } | 
|  | 213 | 
|  | 214 | 
|  | 215   typedef BaseAsyncExecutorTest<AsyncExecutor> AsyncExecutorTest; | 
|  | 216 | 
|  | 217   INSTANTIATE_TEST_CASE_P(DifferentProducersNumber1, | 
|  | 218     AsyncExecutorTest, | 
|  | 219     MultithreadedCallsGenerator1, | 
|  | 220     humanReadbleParams); | 
|  | 221 | 
|  | 222   INSTANTIATE_TEST_CASE_P(DifferentProducersNumber2, | 
|  | 223     AsyncExecutorTest, | 
|  | 224     MultithreadedCallsGenerator2, | 
|  | 225     humanReadbleParams); | 
|  | 226 | 
|  | 227   INSTANTIATE_TEST_CASE_P(DifferentProducersNumber3, | 
|  | 228     AsyncExecutorTest, | 
|  | 229     MultithreadedCallsGenerator3, | 
|  | 230     humanReadbleParams); | 
|  | 231 | 
|  | 232   TEST_P(AsyncExecutorTest, MultithreadedCalls) | 
|  | 233   { | 
|  | 234     MultithreadedCallsTest(); | 
|  | 235   } | 
|  | 236 } | 
| OLD | NEW | 
|---|