3 * Copyright (c) 2020 Project CHIP Authors
4 * Copyright (c) 2016-2017 Nest Labs, Inc.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 * This is a unit test suite for <tt>chip::System::Object</tt>, *
22 * the part of the CHIP System Layer that implements objects and
23 * their static allocation pools.
27 #ifndef __STDC_LIMIT_MACROS
28 #define __STDC_LIMIT_MACROS
31 // Install a sleep in the high water mark function, to force
32 // collisions between the threads that call it.
35 #define SYSTEM_OBJECT_HWM_TEST_HOOK() do { usleep(1000); } while(0)
38 #include <system/SystemLayer.h>
40 #include <support/CodeUtils.h>
41 #include <support/ErrorStr.h>
42 #include <support/UnitTestRegistration.h>
44 #include <nlunit-test.h>
46 #if CHIP_SYSTEM_CONFIG_USE_LWIP
47 #include <lwip/init.h>
48 #include <lwip/tcpip.h>
50 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
52 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
54 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
62 using namespace chip::System;
67 static int Initialize(void * aContext);
68 static int Finalize(void * aContext);
70 class TestObject : public Object
75 static void CheckRetention(nlTestSuite * inSuite, void * aContext);
76 static void CheckConcurrency(nlTestSuite * inSuite, void * aContext);
77 static void CheckHighWatermark(nlTestSuite * inSuite, void * aContext);
78 static void CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext);
83 kPoolSize = 122 // a multiple of kNumThreads, less than CHIP_SYS_STATS_COUNT_MAX
85 static ObjectPool<TestObject, kPoolSize> sPool;
87 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
90 static constexpr int kNumThreads = 16;
91 static constexpr int kLoopIterations = 100000;
92 static constexpr int kMaxDelayIterations = 3;
94 void Delay(volatile unsigned int & aAccumulator);
95 static void * CheckConcurrencyThread(void * aContext);
96 static void * CheckHighWatermarkThread(void * aContext);
97 static void MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) );
98 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
101 TestObject(const TestObject &) = delete;
102 TestObject & operator=(const TestObject &) = delete;
105 ObjectPool<TestObject, TestObject::kPoolSize> TestObject::sPool;
107 Error TestObject::Init()
109 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
110 this->mDelay = kMaxDelayIterations > 0 ? 1 : 0;
111 if (kMaxDelayIterations > 1)
113 this->mDelay += static_cast<unsigned int>(rand() % kMaxDelayIterations);
115 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
117 return CHIP_SYSTEM_NO_ERROR;
123 nlTestSuite * mTestSuite;
124 void * mLayerContext;
125 volatile unsigned int mAccumulator;
128 struct TestContext sContext;
131 // Test Object retention
133 void TestObject::CheckRetention(nlTestSuite * inSuite, void * aContext)
135 TestContext & lContext = *static_cast<TestContext *>(aContext);
139 lLayer.Init(lContext.mLayerContext);
140 memset(&sPool, 0, sizeof(sPool));
142 for (i = 0; i < kPoolSize; ++i)
144 TestObject * lCreated = sPool.TryCreate(lLayer);
146 NL_TEST_ASSERT(lContext.mTestSuite, lCreated != nullptr);
147 if (lCreated == nullptr)
149 NL_TEST_ASSERT(lContext.mTestSuite, lCreated->IsRetained(lLayer));
150 NL_TEST_ASSERT(lContext.mTestSuite, &(lCreated->SystemLayer()) == &lLayer);
154 for (j = 0; j < kPoolSize; ++j)
156 TestObject * lGotten = sPool.Get(lLayer, j);
160 NL_TEST_ASSERT(lContext.mTestSuite, lGotten == nullptr);
164 NL_TEST_ASSERT(lContext.mTestSuite, lGotten != nullptr);
170 for (i = 0; i < kPoolSize; ++i)
172 TestObject * lGotten = sPool.Get(lLayer, i);
174 NL_TEST_ASSERT(lContext.mTestSuite, lGotten != nullptr);
176 for (j = kPoolSize; j > i; --j)
178 NL_TEST_ASSERT(lContext.mTestSuite, lGotten->IsRetained(lLayer));
182 NL_TEST_ASSERT(lContext.mTestSuite, lGotten->IsRetained(lLayer));
184 NL_TEST_ASSERT(lContext.mTestSuite, !lGotten->IsRetained(lLayer));
187 for (i = 0; i < kPoolSize; ++i)
189 TestObject * lGotten = sPool.Get(lLayer, i);
191 NL_TEST_ASSERT(lContext.mTestSuite, lGotten == nullptr);
197 // Test Object concurrency
199 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
200 void TestObject::Delay(volatile unsigned int & aAccumulator)
202 unsigned int lSum = 0;
204 if (kMaxDelayIterations > 0)
206 for (unsigned int z = 0; z < this->mDelay; ++z)
208 lSum += static_cast<unsigned int>(rand());
211 lSum = lSum / this->mDelay;
217 void * TestObject::CheckConcurrencyThread(void * aContext)
219 const unsigned int kNumObjects = kPoolSize / kNumThreads;
220 TestObject * lObject = nullptr;
221 TestContext & lContext = *static_cast<TestContext *>(aContext);
225 lLayer.Init(lContext.mLayerContext);
227 // Take this thread's share of objects
229 for (i = 0; i < kNumObjects; ++i)
231 while (lObject == nullptr)
233 lObject = sPool.TryCreate(lLayer);
236 NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
237 NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
240 lObject->Delay(lContext.mAccumulator);
243 // Free the last object of the pool, if it belongs to
246 lObject = sPool.Get(lLayer, kPoolSize - 1);
248 if (lObject != nullptr)
251 NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
254 // For each iteration, take one more object, and free one starting from the end
257 for (i = 0; i < kLoopIterations; ++i)
262 while (lObject == nullptr)
264 lObject = sPool.TryCreate(lLayer);
267 NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
268 NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
271 lObject->Delay(lContext.mAccumulator);
277 lObject = sPool.Get(lLayer, j);
279 if (lObject == nullptr)
283 NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
287 NL_TEST_ASSERT(lContext.mTestSuite, lObject != nullptr);
292 for (i = 0; i < kPoolSize; ++i)
294 lObject = sPool.Get(lLayer, i);
296 if (lObject == nullptr)
300 NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
308 void * TestObject::CheckHighWatermarkThread(void * aContext)
310 TestContext & lContext = *static_cast<TestContext *>(aContext);
312 chip::System::Stats::count_t lNumInUse;
313 chip::System::Stats::count_t lHighWatermark;
315 i = (rand() % CHIP_SYS_STATS_COUNT_MAX);
317 sPool.UpdateHighWatermark(static_cast<unsigned int>(i));
319 sPool.GetStatistics(lNumInUse, lHighWatermark);
321 NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark >= i);
322 if (lHighWatermark < i)
324 printf("hwm: %d, i: %d\n", lHighWatermark, i);
330 void TestObject::MultithreadedTest(nlTestSuite * inSuite, void * aContext, void * (*aStartRoutine)(void *) )
332 TestContext & lContext = *static_cast<TestContext *>(aContext);
333 pthread_t lThread[kNumThreads];
335 memset(&sPool, 0, sizeof(sPool));
337 for (unsigned int i = 0; i < kNumThreads; ++i)
339 int lError = pthread_create(&lThread[i], nullptr, aStartRoutine, &lContext);
341 NL_TEST_ASSERT(lContext.mTestSuite, lError == 0);
344 for (pthread_t thread : lThread)
346 int lError = pthread_join(thread, nullptr);
348 NL_TEST_ASSERT(lContext.mTestSuite, lError == 0);
351 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
353 void TestObject::CheckConcurrency(nlTestSuite * inSuite, void * aContext)
355 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
356 MultithreadedTest(inSuite, aContext, CheckConcurrencyThread);
357 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
360 void TestObject::CheckHighWatermarkConcurrency(nlTestSuite * inSuite, void * aContext)
362 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
363 for (unsigned int i = 0; i < 1000; i++)
365 MultithreadedTest(inSuite, aContext, CheckHighWatermarkThread);
367 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
370 void TestObject::CheckHighWatermark(nlTestSuite * inSuite, void * aContext)
372 memset(&sPool, 0, sizeof(sPool));
374 const int kNumObjects = kPoolSize;
375 TestObject * lObject = nullptr;
376 TestContext & lContext = *static_cast<TestContext *>(aContext);
378 chip::System::Stats::count_t lNumInUse;
379 chip::System::Stats::count_t lHighWatermark;
381 lLayer.Init(lContext.mLayerContext);
383 // Take all objects one at a time and check the watermark
384 // increases monotonically
386 for (int i = 0; i < kNumObjects; ++i)
388 lObject = sPool.TryCreate(lLayer);
390 NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
391 NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
393 sPool.GetStatistics(lNumInUse, lHighWatermark);
394 NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1));
395 NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == lNumInUse);
400 // Fail an allocation and check that both stats don't change
402 lObject = sPool.TryCreate(lLayer);
403 NL_TEST_ASSERT(lContext.mTestSuite, lObject == nullptr);
405 sPool.GetStatistics(lNumInUse, lHighWatermark);
406 NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == kNumObjects);
407 NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects);
409 // Free all objects one at a time and check that the watermark does not
412 for (int i = 0; i < kNumObjects; ++i)
414 lObject = sPool.Get(lLayer, static_cast<size_t>(i));
416 NL_TEST_ASSERT(lContext.mTestSuite, lObject != nullptr);
419 NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
421 sPool.GetStatistics(lNumInUse, lHighWatermark);
422 NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (kNumObjects - i - 1));
423 NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects);
426 // Take all objects one at a time again and check the watermark
429 for (int i = 0; i < kNumObjects; ++i)
431 lObject = sPool.TryCreate(lLayer);
433 NL_TEST_ASSERT(lContext.mTestSuite, lObject->IsRetained(lLayer));
434 NL_TEST_ASSERT(lContext.mTestSuite, &(lObject->SystemLayer()) == &lLayer);
436 sPool.GetStatistics(lNumInUse, lHighWatermark);
437 NL_TEST_ASSERT(lContext.mTestSuite, lNumInUse == (i + 1));
438 NL_TEST_ASSERT(lContext.mTestSuite, lHighWatermark == kNumObjects);
445 for (size_t i = 0; i < kPoolSize; ++i)
447 lObject = sPool.Get(lLayer, i);
449 if (lObject == nullptr)
453 NL_TEST_ASSERT(lContext.mTestSuite, !lObject->IsRetained(lLayer));
462 * Test Suite. It lists all the test functions.
465 static const nlTest sTests[] =
467 NL_TEST_DEF("Retention", TestObject::CheckRetention),
468 NL_TEST_DEF("Concurrency", TestObject::CheckConcurrency),
469 NL_TEST_DEF("HighWatermark", TestObject::CheckHighWatermark),
470 NL_TEST_DEF("HighWatermarkConcurrency", TestObject::CheckHighWatermarkConcurrency),
474 static nlTestSuite sTestSuite =
476 "chip-system-object",
484 * Initialize the test suite.
486 static int Initialize(void * aContext)
488 TestContext & lContext = *reinterpret_cast<TestContext *>(aContext);
489 void * lLayerContext = nullptr;
491 #if CHIP_SYSTEM_CONFIG_USE_LWIP && LWIP_VERSION_MAJOR <= 2 && LWIP_VERSION_MINOR < 1
492 static sys_mbox_t * sLwIPEventQueue = NULL;
494 if (sLwIPEventQueue == NULL)
496 sys_mbox_new(sLwIPEventQueue, 100);
499 lLayerContext = &sLwIPEventQueue;
500 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
502 lContext.mTestSuite = &sTestSuite;
503 lContext.mLayerContext = lLayerContext;
504 lContext.mAccumulator = 0;
510 * Finalize the test suite.
512 static int Finalize(void * aContext)
514 TestContext & lContext = *reinterpret_cast<TestContext *>(aContext);
516 lContext.mTestSuite = nullptr;
521 } // namespace System
524 int TestSystemObject(void)
526 // Initialize standard pseudo-random number generator
529 // Run test suit againt one lContext.
530 nlTestRunner(&sTestSuite, &chip::System::sContext);
532 return nlTestRunnerStats(&sTestSuite);
535 CHIP_REGISTER_TEST_SUITE(TestSystemObject)