// Copyright 2021 Google LLC // // 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. #ifndef HWY_TESTS_HWY_GTEST_H_ #define HWY_TESTS_HWY_GTEST_H_ // Adapters for GUnit to run tests for all targets. #include #include #include #include // std::tuple #include "gtest/gtest.h" #include "hwy/highway.h" namespace hwy { // googletest before 1.10 didn't define INSTANTIATE_TEST_SUITE_P() but instead // used INSTANTIATE_TEST_CASE_P which is now deprecated. #ifdef INSTANTIATE_TEST_SUITE_P #define HWY_GTEST_INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_SUITE_P #else #define HWY_GTEST_INSTANTIATE_TEST_SUITE_P INSTANTIATE_TEST_CASE_P #endif // Helper class to run parametric tests using the hwy target as parameter. To // use this define the following in your test: // class MyTestSuite : public TestWithParamTarget { // ... // }; // HWY_TARGET_INSTANTIATE_TEST_SUITE_P(MyTestSuite); // TEST_P(MyTestSuite, MyTest) { ... } class TestWithParamTarget : public testing::TestWithParam { protected: void SetUp() override { SetSupportedTargetsForTest(GetParam()); } void TearDown() override { // Check that the parametric test calls SupportedTargets() when the source // was compiled with more than one target. In the single-target case only // static dispatch will be used anyway. #if (HWY_TARGETS & (HWY_TARGETS - 1)) != 0 EXPECT_TRUE(SupportedTargetsCalledForTest()) << "This hwy target parametric test doesn't use dynamic-dispatch and " "doesn't need to be parametric."; #endif SetSupportedTargetsForTest(0); } }; // Function to convert the test parameter of a TestWithParamTarget for // displaying it in the gtest test name. static inline std::string TestParamTargetName( const testing::TestParamInfo& info) { return TargetName(info.param); } #define HWY_TARGET_INSTANTIATE_TEST_SUITE_P(suite) \ HWY_GTEST_INSTANTIATE_TEST_SUITE_P( \ suite##Group, suite, \ testing::ValuesIn(::hwy::SupportedAndGeneratedTargets()), \ ::hwy::TestParamTargetName) // Helper class similar to TestWithParamTarget to run parametric tests that // depend on the target and another parametric test. If you need to use multiple // extra parameters use a std::tuple<> of them and ::testing::Generate(...) as // the generator. To use this class define the following in your test: // class MyTestSuite : public TestWithParamTargetT { // ... // }; // HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(MyTestSuite, ::testing::Range(0, 9)); // TEST_P(MyTestSuite, MyTest) { ... GetParam() .... } template class TestWithParamTargetAndT : public ::testing::TestWithParam> { public: // Expose the parametric type here so it can be used by the // HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T macro. using HwyParamType = T; protected: void SetUp() override { SetSupportedTargetsForTest(std::get<0>( ::testing::TestWithParam>::GetParam())); } void TearDown() override { // Check that the parametric test calls SupportedTargets() when the source // was compiled with more than one target. In the single-target case only // static dispatch will be used anyway. #if (HWY_TARGETS & (HWY_TARGETS - 1)) != 0 EXPECT_TRUE(SupportedTargetsCalledForTest()) << "This hwy target parametric test doesn't use dynamic-dispatch and " "doesn't need to be parametric."; #endif SetSupportedTargetsForTest(0); } T GetParam() { return std::get<1>( ::testing::TestWithParam>::GetParam()); } }; template std::string TestParamTargetNameAndT( const testing::TestParamInfo>& info) { return std::string(TargetName(std::get<0>(info.param))) + "_" + ::testing::PrintToString(std::get<1>(info.param)); } #define HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T(suite, generator) \ HWY_GTEST_INSTANTIATE_TEST_SUITE_P( \ suite##Group, suite, \ ::testing::Combine( \ testing::ValuesIn(::hwy::SupportedAndGeneratedTargets()), \ generator), \ ::hwy::TestParamTargetNameAndT) // Helper macro to export a function and define a test that tests it. This is // equivalent to do a HWY_EXPORT of a void(void) function and run it in a test: // class MyTestSuite : public TestWithParamTarget { // ... // }; // HWY_TARGET_INSTANTIATE_TEST_SUITE_P(MyTestSuite); // HWY_EXPORT_AND_TEST_P(MyTestSuite, MyTest); #define HWY_EXPORT_AND_TEST_P(suite, func_name) \ HWY_EXPORT(func_name); \ TEST_P(suite, func_name) { HWY_DYNAMIC_DISPATCH(func_name)(); } \ static_assert(true, "For requiring trailing semicolon") #define HWY_EXPORT_AND_TEST_P_T(suite, func_name) \ HWY_EXPORT(func_name); \ TEST_P(suite, func_name) { HWY_DYNAMIC_DISPATCH(func_name)(GetParam()); } \ static_assert(true, "For requiring trailing semicolon") #define HWY_BEFORE_TEST(suite) \ class suite : public hwy::TestWithParamTarget {}; \ HWY_TARGET_INSTANTIATE_TEST_SUITE_P(suite); \ static_assert(true, "For requiring trailing semicolon") } // namespace hwy #endif // HWY_TESTS_HWY_GTEST_H_