From 8efb6aa317d7b42a2e550f4b2db4b602d63cad13 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Thu, 18 Jan 2024 15:18:39 -0500 Subject: [PATCH] Added a scoped value changer, mainly for unit test usage (#31513) * Added a scoped value changer, mainly for unit test usage * Restyle * Fix shadow variable naming * Fix name collision due to copy and paste * Update based on code review * Restyle --------- Co-authored-by: Andrei Litvin --- src/lib/support/BUILD.gn | 1 + src/lib/support/Scoped.h | 81 +++++++++++++++++++++ src/lib/support/tests/BUILD.gn | 1 + src/lib/support/tests/TestScoped.cpp | 102 +++++++++++++++++++++++++++ 4 files changed, 185 insertions(+) create mode 100644 src/lib/support/Scoped.h create mode 100644 src/lib/support/tests/TestScoped.cpp diff --git a/src/lib/support/BUILD.gn b/src/lib/support/BUILD.gn index 861cd280cec0d7..b02e4905ff5817 100644 --- a/src/lib/support/BUILD.gn +++ b/src/lib/support/BUILD.gn @@ -196,6 +196,7 @@ static_library("support") { "PrivateHeap.cpp", "PrivateHeap.h", "ReferenceCountedHandle.h", + "Scoped.h", "SerializableIntegerSet.cpp", "SerializableIntegerSet.h", "SetupDiscriminator.h", diff --git a/src/lib/support/Scoped.h b/src/lib/support/Scoped.h new file mode 100644 index 00000000000000..67cc2ea9e96271 --- /dev/null +++ b/src/lib/support/Scoped.h @@ -0,0 +1,81 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +namespace chip { + +template +class ScopedChange; + +/// Allows a value to only be changed within a scope. +/// +/// Generally used to force determinism for unit test execution. +/// +/// When a variable of this type is used, it should +/// only be changed via `ScopedChange`. +template +class ScopedChangeOnly +{ +public: + explicit ScopedChangeOnly(T initial) : mValue(initial) {} + operator T() const { return mValue; } + +private: + T mValue; + + // Expected to be used only by ScopedChange only + T & InternalMutableValue() { return mValue; } + friend class ScopedChange; +}; + +/// Allows a scoped mutation to occur on a variable. +/// +/// When an instance of this class goes out of scope, the variable +/// will be reset to the default. +/// +/// Example usage +/// +/// int a = 123; +/// ScopedChangeOnly b(234); +/// +/// /* a == 123, b == 234 */ +/// { +/// ScopedChange changeA(a, 321); +/// /* a == 321, b == 234 */ +/// { +/// ScopedChange changeB(b, 10); +/// /* a == 321, b == 10 */ +/// } +/// /* a == 321, b == 234 */ +/// } +/// /* a == 123, b == 234 */ +/// +template +class ScopedChange +{ +public: + ScopedChange(ScopedChangeOnly & what, T value) : mValue(what.InternalMutableValue()), mOriginal(what) { mValue = value; } + ScopedChange(T & what, T value) : mValue(what), mOriginal(what) { mValue = value; } + ~ScopedChange() { mValue = mOriginal; } + +private: + T & mValue; + T mOriginal; +}; + +} // namespace chip diff --git a/src/lib/support/tests/BUILD.gn b/src/lib/support/tests/BUILD.gn index ca647b3dad99cd..7a3b738ef4491a 100644 --- a/src/lib/support/tests/BUILD.gn +++ b/src/lib/support/tests/BUILD.gn @@ -44,6 +44,7 @@ chip_test_suite_using_nltest("tests") { "TestPrivateHeap.cpp", "TestSafeInt.cpp", "TestSafeString.cpp", + "TestScoped.cpp", "TestScopedBuffer.cpp", "TestSerializableIntegerSet.cpp", "TestSpan.cpp", diff --git a/src/lib/support/tests/TestScoped.cpp b/src/lib/support/tests/TestScoped.cpp new file mode 100644 index 00000000000000..90cdd62b0acf4a --- /dev/null +++ b/src/lib/support/tests/TestScoped.cpp @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +namespace { + +using namespace chip; + +void TestScopedVariableChange(nlTestSuite * inSuite, void * inContext) +{ + int x = 123; + + { + ScopedChange change1(x, 10); + NL_TEST_ASSERT(inSuite, x == 10); + + x = 15; + { + ScopedChange change2(x, 20); + NL_TEST_ASSERT(inSuite, x == 20); + } + NL_TEST_ASSERT(inSuite, x == 15); + } + NL_TEST_ASSERT(inSuite, x == 123); +} + +void TestScopedChangeOnly(nlTestSuite * inSuite, void * inContext) +{ + ScopedChangeOnly intValue(123); + ScopedChangeOnly strValue("abc"); + + NL_TEST_ASSERT(inSuite, intValue == 123); + NL_TEST_ASSERT(inSuite, strcmp(strValue, "abc") == 0); + + { + ScopedChange change1(intValue, 234); + + NL_TEST_ASSERT(inSuite, intValue == 234); + NL_TEST_ASSERT(inSuite, strcmp(strValue, "abc") == 0); + + ScopedChange change2(strValue, "xyz"); + NL_TEST_ASSERT(inSuite, intValue == 234); + NL_TEST_ASSERT(inSuite, strcmp(strValue, "xyz") == 0); + + { + ScopedChange change3(intValue, 10); + ScopedChange change4(strValue, "test"); + + NL_TEST_ASSERT(inSuite, intValue == 10); + NL_TEST_ASSERT(inSuite, strcmp(strValue, "test") == 0); + } + + NL_TEST_ASSERT(inSuite, intValue == 234); + NL_TEST_ASSERT(inSuite, strcmp(strValue, "xyz") == 0); + } + + NL_TEST_ASSERT(inSuite, intValue == 123); + NL_TEST_ASSERT(inSuite, strcmp(strValue, "abc") == 0); +} + +} // namespace + +#define NL_TEST_DEF_FN(fn) NL_TEST_DEF("Test " #fn, fn) +/** + * Test Suite. It lists all the test functions. + */ +static const nlTest sTests[] = { + NL_TEST_DEF_FN(TestScopedVariableChange), // + NL_TEST_DEF_FN(TestScopedChangeOnly), // + NL_TEST_SENTINEL() // +}; + +int TestScoped() +{ + nlTestSuite theSuite = { "CHIP Scoped tests", &sTests[0], nullptr, nullptr }; + + // Run test suite against one context. + nlTestRunner(&theSuite, nullptr); + return nlTestRunnerStats(&theSuite); +} + +CHIP_REGISTER_TEST_SUITE(TestScoped)