• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include <pw_assert/check.h>
16 #include <pw_random/xor_shift.h>
17 #include <pw_string/format.h>
18 
19 #include <cstdlib>
20 
21 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
23 #include "pw_bluetooth_sapphire/internal/host/testing/parse_args.h"
24 #include "pw_unit_test/framework.h"
25 
26 #ifdef PW_LOG_DECLARE_FAKE_DRIVER
27 PW_LOG_DECLARE_FAKE_DRIVER();
28 #endif
29 
30 using bt::LogSeverity;
31 using bt::testing::GetArgValue;
32 
33 namespace {
34 
LogSeverityFromString(std::string_view str)35 constexpr LogSeverity LogSeverityFromString(std::string_view str) {
36   if (str == "DEBUG") {
37     return LogSeverity::DEBUG;
38   } else if (str == "INFO") {
39     return LogSeverity::INFO;
40   } else if (str == "WARN") {
41     return LogSeverity::WARN;
42   } else if (str == "ERROR") {
43     return LogSeverity::ERROR;
44   }
45   return LogSeverity::ERROR;
46 }
47 
48 // A valid random seed must be in [1, kMaxRandomSeed].
49 constexpr uint32_t kMaxRandomSeed = 99999;
50 
51 // Normalizes the seed to range [1, kMaxRandomSeed].
NormalizeRandomSeed(uint32_t seed)52 int32_t NormalizeRandomSeed(uint32_t seed) {
53   return static_cast<int32_t>((seed - 1U) % kMaxRandomSeed) + 1;
54 }
55 
GenerateRandomSeed()56 int32_t GenerateRandomSeed() {
57   // TODO(fxbug.dev/42069982): Get time using pw::chrono for portability.
58   const int64_t time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
59                               std::chrono::system_clock::now() -
60                               std::chrono::system_clock::from_time_t(0))
61                               .count();
62   return NormalizeRandomSeed(static_cast<uint32_t>(time_ms));
63 }
64 
65 }  // namespace
66 
main(int argc,char ** argv)67 int main(int argc, char** argv) {
68   LogSeverity log_severity = LogSeverity::ERROR;
69 
70   std::optional<std::string_view> severity_arg_value =
71       GetArgValue("severity", argc, argv);
72   if (severity_arg_value) {
73     log_severity = LogSeverityFromString(*severity_arg_value);
74   }
75 
76   // Set all library log messages to use printf.
77   bt::UsePrintf(log_severity);
78 
79   // If --gtest_random_seed is not specified, then GoogleTest calculates a seed
80   // based on time. To avoid using different seeds, we need to tell GoogleTest
81   // what seed we are using.
82   std::vector<char*> new_argv(argv, argv + argc);
83   char new_argv_seed_option[sizeof("--gtest_random_seed=-2147483648")] = {};
84 
85   // GoogleTest doesn't initialize the random seed (UnitTest::random_seed())
86   // until RUN_ALL_TESTS, so we need to parse it now to avoid configuring the
87   // random generator in every test suite.
88   int32_t random_seed = 0;
89   std::optional<std::string_view> seed_arg_value =
90       GetArgValue("gtest_random_seed", argc, argv);
91   if (seed_arg_value) {
92     std::from_chars_result result =
93         std::from_chars(seed_arg_value->data(),
94                         seed_arg_value->data() + seed_arg_value->size(),
95                         random_seed);
96     if (result.ec != std::errc()) {
97       fprintf(stderr, "\nERROR: Invalid gtest_random_seed value\n");
98       return 1;
99     }
100     random_seed = NormalizeRandomSeed(random_seed);
101   } else {
102     random_seed = GenerateRandomSeed();
103     bool format_ok =
104         pw::string::Format(
105             new_argv_seed_option, "--gtest_random_seed=%d", random_seed)
106             .ok();
107     PW_CHECK(format_ok);
108     new_argv.push_back(new_argv_seed_option);
109   }
110 
111   // Print the random seed so that it is easy to reproduce a test run.
112   printf("\nGTEST_RANDOM_SEED=%d\n", random_seed);
113 
114   pw::random::XorShiftStarRng64 rng(random_seed);
115   bt::set_random_generator(&rng);
116 
117   int new_argc = static_cast<int>(new_argv.size());
118   // argv[argc] must be nullptr according to the C++ standard.
119   new_argv.push_back(nullptr);
120 
121   testing::InitGoogleTest(&new_argc, new_argv.data());
122 
123   return RUN_ALL_TESTS();
124 }
125