db_rate_limiter_test.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. // Copyright (c) 2022-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include <gtest/gtest.h>
  6. #include <cstdint>
  7. #include <string>
  8. #include "db/db_test_util.h"
  9. #include "port/stack_trace.h"
  10. #include "rocksdb/db.h"
  11. #include "rocksdb/env.h"
  12. #include "test_util/testharness.h"
  13. #include "util/file_checksum_helper.h"
  14. namespace ROCKSDB_NAMESPACE {
  15. class DBRateLimiterOnReadTest
  16. : public DBTestBase,
  17. public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
  18. public:
  19. explicit DBRateLimiterOnReadTest()
  20. : DBTestBase("db_rate_limiter_on_read_test", /*env_do_fsync=*/false),
  21. use_direct_io_(std::get<0>(GetParam())),
  22. use_block_cache_(std::get<1>(GetParam())),
  23. use_readahead_(std::get<2>(GetParam())) {}
  24. void Init() {
  25. options_ = GetOptions();
  26. Reopen(options_);
  27. for (int i = 0; i < kNumFiles; ++i) {
  28. for (int j = 0; j < kNumKeysPerFile; ++j) {
  29. ASSERT_OK(Put(Key(i * kNumKeysPerFile + j), "val"));
  30. }
  31. ASSERT_OK(Flush());
  32. }
  33. MoveFilesToLevel(1);
  34. }
  35. BlockBasedTableOptions GetTableOptions() {
  36. BlockBasedTableOptions table_options;
  37. table_options.no_block_cache = !use_block_cache_;
  38. return table_options;
  39. }
  40. ReadOptions GetReadOptions() {
  41. ReadOptions read_options;
  42. read_options.rate_limiter_priority = Env::IO_USER;
  43. read_options.readahead_size = use_readahead_ ? kReadaheadBytes : 0;
  44. return read_options;
  45. }
  46. Options GetOptions() {
  47. Options options = CurrentOptions();
  48. options.disable_auto_compactions = true;
  49. options.file_checksum_gen_factory.reset(new FileChecksumGenCrc32cFactory());
  50. options.rate_limiter.reset(NewGenericRateLimiter(
  51. 1 << 20 /* rate_bytes_per_sec */, 100 * 1000 /* refill_period_us */,
  52. 10 /* fairness */, RateLimiter::Mode::kAllIo));
  53. options.table_factory.reset(NewBlockBasedTableFactory(GetTableOptions()));
  54. options.use_direct_reads = use_direct_io_;
  55. return options;
  56. }
  57. protected:
  58. const static int kNumKeysPerFile = 1;
  59. const static int kNumFiles = 3;
  60. const static int kReadaheadBytes = 32 << 10; // 32KB
  61. Options options_;
  62. const bool use_direct_io_;
  63. const bool use_block_cache_;
  64. const bool use_readahead_;
  65. };
  66. std::string GetTestNameSuffix(
  67. ::testing::TestParamInfo<std::tuple<bool, bool, bool>> info) {
  68. std::ostringstream oss;
  69. if (std::get<0>(info.param)) {
  70. oss << "DirectIO";
  71. } else {
  72. oss << "BufferedIO";
  73. }
  74. if (std::get<1>(info.param)) {
  75. oss << "_BlockCache";
  76. } else {
  77. oss << "_NoBlockCache";
  78. }
  79. if (std::get<2>(info.param)) {
  80. oss << "_Readahead";
  81. } else {
  82. oss << "_NoReadahead";
  83. }
  84. return oss.str();
  85. }
  86. INSTANTIATE_TEST_CASE_P(DBRateLimiterOnReadTest, DBRateLimiterOnReadTest,
  87. ::testing::Combine(::testing::Bool(), ::testing::Bool(),
  88. ::testing::Bool()),
  89. GetTestNameSuffix);
  90. TEST_P(DBRateLimiterOnReadTest, Get) {
  91. if (use_direct_io_ && !IsDirectIOSupported()) {
  92. return;
  93. }
  94. Init();
  95. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  96. int expected = 0;
  97. for (int i = 0; i < kNumFiles; ++i) {
  98. {
  99. std::string value;
  100. ASSERT_OK(db_->Get(GetReadOptions(), Key(i * kNumKeysPerFile), &value));
  101. ++expected;
  102. }
  103. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  104. {
  105. std::string value;
  106. ASSERT_OK(db_->Get(GetReadOptions(), Key(i * kNumKeysPerFile), &value));
  107. if (!use_block_cache_) {
  108. ++expected;
  109. }
  110. }
  111. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  112. }
  113. }
  114. TEST_P(DBRateLimiterOnReadTest, NewMultiGet) {
  115. if (use_direct_io_ && !IsDirectIOSupported()) {
  116. return;
  117. }
  118. Init();
  119. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  120. const int kNumKeys = kNumFiles * kNumKeysPerFile;
  121. int64_t expected = 0;
  122. {
  123. std::vector<std::string> key_bufs;
  124. key_bufs.reserve(kNumKeys);
  125. std::vector<Slice> keys;
  126. keys.reserve(kNumKeys);
  127. for (int i = 0; i < kNumKeys; ++i) {
  128. key_bufs.emplace_back(Key(i));
  129. keys.emplace_back(key_bufs[i]);
  130. }
  131. std::vector<Status> statuses(kNumKeys);
  132. std::vector<PinnableSlice> values(kNumKeys);
  133. const int64_t prev_total_rl_req = options_.rate_limiter->GetTotalRequests();
  134. db_->MultiGet(GetReadOptions(), dbfull()->DefaultColumnFamily(), kNumKeys,
  135. keys.data(), values.data(), statuses.data());
  136. const int64_t cur_total_rl_req = options_.rate_limiter->GetTotalRequests();
  137. for (int i = 0; i < kNumKeys; ++i) {
  138. ASSERT_TRUE(statuses[i].ok());
  139. }
  140. ASSERT_GT(cur_total_rl_req, prev_total_rl_req);
  141. ASSERT_EQ(cur_total_rl_req - prev_total_rl_req,
  142. options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  143. }
  144. expected += kNumKeys;
  145. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  146. }
  147. TEST_P(DBRateLimiterOnReadTest, OldMultiGet) {
  148. // The old `vector<Status>`-returning `MultiGet()` APIs use `Read()`, which
  149. // supports rate limiting.
  150. if (use_direct_io_ && !IsDirectIOSupported()) {
  151. return;
  152. }
  153. Init();
  154. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  155. const int kNumKeys = kNumFiles * kNumKeysPerFile;
  156. int expected = 0;
  157. {
  158. std::vector<std::string> key_bufs;
  159. key_bufs.reserve(kNumKeys);
  160. std::vector<Slice> keys;
  161. keys.reserve(kNumKeys);
  162. for (int i = 0; i < kNumKeys; ++i) {
  163. key_bufs.emplace_back(Key(i));
  164. keys.emplace_back(key_bufs[i]);
  165. }
  166. std::vector<std::string> values;
  167. std::vector<Status> statuses =
  168. db_->MultiGet(GetReadOptions(), keys, &values);
  169. for (int i = 0; i < kNumKeys; ++i) {
  170. ASSERT_OK(statuses[i]);
  171. }
  172. }
  173. expected += kNumKeys;
  174. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  175. }
  176. TEST_P(DBRateLimiterOnReadTest, Iterator) {
  177. if (use_direct_io_ && !IsDirectIOSupported()) {
  178. return;
  179. }
  180. Init();
  181. std::unique_ptr<Iterator> iter(db_->NewIterator(GetReadOptions()));
  182. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  183. int expected = 0;
  184. for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
  185. ++expected;
  186. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  187. }
  188. for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
  189. // When `use_block_cache_ == true`, the reverse scan will access the blocks
  190. // loaded to cache during the above forward scan, in which case no further
  191. // file reads are expected.
  192. if (!use_block_cache_) {
  193. ++expected;
  194. }
  195. }
  196. ASSERT_OK(iter->status());
  197. // Reverse scan does not read evenly (one block per iteration) due to
  198. // descending seqno ordering, so wait until after the loop to check total.
  199. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  200. }
  201. TEST_P(DBRateLimiterOnReadTest, VerifyChecksum) {
  202. if (use_direct_io_ && !IsDirectIOSupported()) {
  203. return;
  204. }
  205. Init();
  206. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  207. ASSERT_OK(db_->VerifyChecksum(GetReadOptions()));
  208. // In BufferedIO,
  209. // there are 7 reads per file, each of which will be rate-limited.
  210. // During open: read footer, meta index block, properties block, index block.
  211. // During actual checksum verification: read meta index block, verify checksum
  212. // in meta blocks and verify checksum in file blocks.
  213. //
  214. // In DirectIO, where we support tail prefetching, during table open, we only
  215. // do 1 read instead of 4 as described above. Actual checksum verification
  216. // reads stay the same.
  217. #ifdef OS_WIN
  218. // No file system prefetch implemented for OS Win. During table open,
  219. // we only do 1 read for BufferedIO.
  220. int num_read_per_file = 4;
  221. #else
  222. int num_read_per_file = (!use_direct_io_) ? 7 : 4;
  223. #endif
  224. int expected = kNumFiles * num_read_per_file;
  225. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  226. }
  227. TEST_P(DBRateLimiterOnReadTest, VerifyFileChecksums) {
  228. if (use_direct_io_ && !IsDirectIOSupported()) {
  229. return;
  230. }
  231. Init();
  232. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  233. ASSERT_OK(db_->VerifyFileChecksums(GetReadOptions()));
  234. // The files are tiny so there should have just been one read per file.
  235. int expected = kNumFiles;
  236. ASSERT_EQ(expected, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  237. }
  238. class DBRateLimiterOnWriteTest : public DBTestBase {
  239. public:
  240. explicit DBRateLimiterOnWriteTest()
  241. : DBTestBase("db_rate_limiter_on_write_test", /*env_do_fsync=*/false) {}
  242. void Init() {
  243. options_ = GetOptions();
  244. ASSERT_OK(TryReopenWithColumnFamilies({"default"}, options_));
  245. Random rnd(301);
  246. for (int i = 0; i < kNumFiles; i++) {
  247. ASSERT_OK(Put(0, kStartKey, rnd.RandomString(2)));
  248. ASSERT_OK(Put(0, kEndKey, rnd.RandomString(2)));
  249. ASSERT_OK(Flush(0));
  250. }
  251. }
  252. Options GetOptions() {
  253. Options options = CurrentOptions();
  254. options.disable_auto_compactions = true;
  255. options.rate_limiter.reset(NewGenericRateLimiter(
  256. 1 << 20 /* rate_bytes_per_sec */, 100 * 1000 /* refill_period_us */,
  257. 10 /* fairness */, RateLimiter::Mode::kWritesOnly));
  258. options.table_factory.reset(
  259. NewBlockBasedTableFactory(BlockBasedTableOptions()));
  260. return options;
  261. }
  262. protected:
  263. inline const static int64_t kNumFiles = 3;
  264. inline const static std::string kStartKey = "a";
  265. inline const static std::string kEndKey = "b";
  266. Options options_;
  267. };
  268. TEST_F(DBRateLimiterOnWriteTest, Flush) {
  269. std::int64_t prev_total_request = 0;
  270. Init();
  271. std::int64_t actual_flush_request =
  272. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
  273. prev_total_request;
  274. std::int64_t exepcted_flush_request = kNumFiles;
  275. EXPECT_EQ(actual_flush_request, exepcted_flush_request);
  276. EXPECT_EQ(actual_flush_request,
  277. options_.rate_limiter->GetTotalRequests(Env::IO_HIGH));
  278. }
  279. TEST_F(DBRateLimiterOnWriteTest, Compact) {
  280. Init();
  281. // Pre-comaction:
  282. // level-0 : `kNumFiles` SST files overlapping on [kStartKey, kEndKey]
  283. std::string files_per_level_pre_compaction = std::to_string(kNumFiles);
  284. ASSERT_EQ(files_per_level_pre_compaction, FilesPerLevel(0 /* cf */));
  285. std::int64_t prev_total_request =
  286. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
  287. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_LOW));
  288. Compact(kStartKey, kEndKey);
  289. std::int64_t actual_compaction_request =
  290. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
  291. prev_total_request;
  292. // Post-comaction:
  293. // level-0 : 0 SST file
  294. // level-1 : 1 SST file
  295. std::string files_per_level_post_compaction = "0,1";
  296. ASSERT_EQ(files_per_level_post_compaction, FilesPerLevel(0 /* cf */));
  297. std::int64_t exepcted_compaction_request = 1;
  298. EXPECT_EQ(actual_compaction_request, exepcted_compaction_request);
  299. EXPECT_EQ(actual_compaction_request,
  300. options_.rate_limiter->GetTotalRequests(Env::IO_LOW));
  301. }
  302. class DBRateLimiterOnWriteWALTest
  303. : public DBRateLimiterOnWriteTest,
  304. public ::testing::WithParamInterface<std::tuple<
  305. bool /* WriteOptions::disableWal */,
  306. bool /* Options::manual_wal_flush */,
  307. Env::IOPriority /* WriteOptions::rate_limiter_priority */>> {
  308. public:
  309. static std::string GetTestNameSuffix(
  310. ::testing::TestParamInfo<std::tuple<bool, bool, Env::IOPriority>> info) {
  311. std::ostringstream oss;
  312. if (std::get<0>(info.param)) {
  313. oss << "DisableWAL";
  314. } else {
  315. oss << "EnableWAL";
  316. }
  317. if (std::get<1>(info.param)) {
  318. oss << "_ManualWALFlush";
  319. } else {
  320. oss << "_AutoWALFlush";
  321. }
  322. if (std::get<2>(info.param) == Env::IO_USER) {
  323. oss << "_RateLimitAutoWALFlush";
  324. } else if (std::get<2>(info.param) == Env::IO_TOTAL) {
  325. oss << "_NoRateLimitAutoWALFlush";
  326. } else {
  327. oss << "_RateLimitAutoWALFlushWithIncorrectPriority";
  328. }
  329. return oss.str();
  330. }
  331. explicit DBRateLimiterOnWriteWALTest()
  332. : disable_wal_(std::get<0>(GetParam())),
  333. manual_wal_flush_(std::get<1>(GetParam())),
  334. rate_limiter_priority_(std::get<2>(GetParam())) {}
  335. void Init() {
  336. options_ = GetOptions();
  337. options_.manual_wal_flush = manual_wal_flush_;
  338. Reopen(options_);
  339. }
  340. WriteOptions GetWriteOptions() {
  341. WriteOptions write_options;
  342. write_options.disableWAL = disable_wal_;
  343. write_options.rate_limiter_priority = rate_limiter_priority_;
  344. return write_options;
  345. }
  346. protected:
  347. bool disable_wal_;
  348. bool manual_wal_flush_;
  349. Env::IOPriority rate_limiter_priority_;
  350. };
  351. INSTANTIATE_TEST_CASE_P(
  352. DBRateLimiterOnWriteWALTest, DBRateLimiterOnWriteWALTest,
  353. ::testing::Values(std::make_tuple(false, false, Env::IO_TOTAL),
  354. std::make_tuple(false, false, Env::IO_USER),
  355. std::make_tuple(false, false, Env::IO_HIGH),
  356. std::make_tuple(false, true, Env::IO_USER),
  357. std::make_tuple(true, false, Env::IO_USER)),
  358. DBRateLimiterOnWriteWALTest::GetTestNameSuffix);
  359. TEST_P(DBRateLimiterOnWriteWALTest, AutoWalFlush) {
  360. Init();
  361. const bool no_rate_limit_auto_wal_flush =
  362. (rate_limiter_priority_ == Env::IO_TOTAL);
  363. const bool valid_arg = (rate_limiter_priority_ == Env::IO_USER &&
  364. !disable_wal_ && !manual_wal_flush_);
  365. std::int64_t prev_total_request =
  366. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
  367. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  368. Status s = Put("foo", "v1", GetWriteOptions());
  369. if (no_rate_limit_auto_wal_flush || valid_arg) {
  370. EXPECT_TRUE(s.ok());
  371. } else {
  372. EXPECT_TRUE(s.IsInvalidArgument());
  373. EXPECT_TRUE(s.ToString().find("WriteOptions::rate_limiter_priority") !=
  374. std::string::npos);
  375. }
  376. std::int64_t actual_auto_wal_flush_request =
  377. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
  378. prev_total_request;
  379. std::int64_t expected_auto_wal_flush_request = valid_arg ? 1 : 0;
  380. EXPECT_EQ(actual_auto_wal_flush_request, expected_auto_wal_flush_request);
  381. EXPECT_EQ(actual_auto_wal_flush_request,
  382. options_.rate_limiter->GetTotalRequests(Env::IO_USER));
  383. }
  384. class DBRateLimiterOnManualWALFlushTest
  385. : public DBRateLimiterOnWriteTest,
  386. public ::testing::WithParamInterface<Env::IOPriority> {
  387. public:
  388. static std::string GetTestNameSuffix(
  389. ::testing::TestParamInfo<Env::IOPriority> info) {
  390. std::ostringstream oss;
  391. if (info.param == Env::IO_USER) {
  392. oss << "RateLimitManualWALFlush";
  393. } else if (info.param == Env::IO_TOTAL) {
  394. oss << "NoRateLimitManualWALFlush";
  395. } else if (info.param == Env::IO_HIGH) {
  396. oss << "RateLimitManualWALFlushWithHighPriority";
  397. } else {
  398. oss << "RateLimitManualWALFlushWithLowPriority";
  399. }
  400. return oss.str();
  401. }
  402. explicit DBRateLimiterOnManualWALFlushTest()
  403. : rate_limiter_priority_(GetParam()) {}
  404. void Init() {
  405. options_ = GetOptions();
  406. // Enable manual WAL flush mode
  407. options_.manual_wal_flush = true;
  408. Reopen(options_);
  409. }
  410. WriteOptions GetWriteOptions() {
  411. WriteOptions write_options;
  412. // WAL must be enabled for manual WAL flush to work
  413. write_options.disableWAL = false;
  414. // In manual WAL flush mode, WAL write rate limiting should be done through
  415. // FlushWAL(), not WriteOptions::rate_limiter_priority
  416. write_options.rate_limiter_priority = Env::IO_TOTAL;
  417. return write_options;
  418. }
  419. protected:
  420. Env::IOPriority rate_limiter_priority_;
  421. };
  422. INSTANTIATE_TEST_CASE_P(DBRateLimiterOnManualWALFlushTest,
  423. DBRateLimiterOnManualWALFlushTest,
  424. ::testing::Values(Env::IO_TOTAL, Env::IO_USER,
  425. Env::IO_HIGH, Env::IO_LOW),
  426. DBRateLimiterOnManualWALFlushTest::GetTestNameSuffix);
  427. TEST_P(DBRateLimiterOnManualWALFlushTest, ManualWALFlush) {
  428. Init();
  429. const bool no_rate_limit = (rate_limiter_priority_ == Env::IO_TOTAL);
  430. ASSERT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL));
  431. for (bool sync : {false, true}) {
  432. std::int64_t prev_total_request =
  433. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
  434. Status put_status = Put("key_" + std::to_string(sync),
  435. "value_" + std::to_string(sync), GetWriteOptions());
  436. EXPECT_TRUE(put_status.ok());
  437. // Since manual_wal_flush is enabled and write_options.rate_limiter_priority
  438. // is IO_TOTAL, no rate limiting should have occurred for this user write
  439. EXPECT_EQ(0, options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
  440. prev_total_request);
  441. // Now explicitly flush the WAL with the test's rate_limiter_priority
  442. prev_total_request = options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL);
  443. std::int64_t prev_priority_request =
  444. options_.rate_limiter->GetTotalRequests(rate_limiter_priority_);
  445. FlushWALOptions flush_options;
  446. flush_options.sync = sync;
  447. flush_options.rate_limiter_priority = rate_limiter_priority_;
  448. Status flush_status = db_->FlushWAL(flush_options);
  449. EXPECT_TRUE(flush_status.ok());
  450. std::int64_t manual_wal_flush_requests_total =
  451. options_.rate_limiter->GetTotalRequests(Env::IO_TOTAL) -
  452. prev_total_request;
  453. std::int64_t manual_wal_flush_requests_for_priority =
  454. options_.rate_limiter->GetTotalRequests(rate_limiter_priority_) -
  455. prev_priority_request;
  456. if (no_rate_limit) {
  457. EXPECT_EQ(0, manual_wal_flush_requests_total);
  458. EXPECT_EQ(0, manual_wal_flush_requests_for_priority);
  459. } else {
  460. EXPECT_EQ(manual_wal_flush_requests_total,
  461. manual_wal_flush_requests_for_priority);
  462. EXPECT_GT(manual_wal_flush_requests_for_priority, 0);
  463. }
  464. }
  465. }
  466. } // namespace ROCKSDB_NAMESPACE
  467. int main(int argc, char** argv) {
  468. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  469. ::testing::InitGoogleTest(&argc, argv);
  470. return RUN_ALL_TESTS();
  471. }