icu_mergeable_data_file_unittest.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/i18n/icu_mergeable_data_file.h"
  5. #include "base/debug/proc_maps_linux.h"
  6. #include "base/files/scoped_temp_dir.h"
  7. #include "base/i18n/icu_util.h"
  8. #include "build/build_config.h"
  9. #include "testing/gtest/include/gtest/gtest.h"
  10. #if !BUILDFLAG(IS_NACL)
  11. #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
  12. namespace base::i18n {
  13. class IcuMergeableDataFileTest : public testing::Test {
  14. protected:
  15. void SetUp() override { ResetGlobalsForTesting(); }
  16. };
  17. TEST_F(IcuMergeableDataFileTest, IcuDataFileMergesCommonPages) {
  18. // Create two temporary files mocking Ash and Lacros's versions of ICU.
  19. base::ScopedTempDir temp_dir;
  20. ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  21. FilePath ash_path = temp_dir.GetPath().AppendASCII("ash_icudtl.dat");
  22. FilePath lacros_path = temp_dir.GetPath().AppendASCII("lacros_icudtl.dat");
  23. uint32_t flags =
  24. base::File::FLAG_CREATE | base::File::FLAG_READ | base::File::FLAG_WRITE;
  25. File ash_file(ash_path, flags);
  26. File lacros_file(lacros_path, flags);
  27. ASSERT_TRUE(ash_file.IsValid());
  28. ASSERT_TRUE(lacros_file.IsValid());
  29. // Prepare some data to use for filling in the mock files.
  30. std::vector<char> pg0(0x1000, 0x00);
  31. std::vector<char> pg1(0x1000, 0x11);
  32. std::vector<char> pg2(0x1000, 0x22);
  33. std::vector<char> pg3(0x1000, 0x33);
  34. std::vector<char> pg4(0x0333, 0x44);
  35. int pg0_sz = pg0.size(), pg1_sz = pg1.size(), pg2_sz = pg2.size(),
  36. pg3_sz = pg3.size(), pg4_sz = pg4.size();
  37. // Build Ash's file structure:
  38. // 0x0000 .. 0x1000 => { 0x00, ... } | Shared
  39. // 0x1000 .. 0x2000 => { 0x11, ... } | Shared
  40. // 0x2000 .. 0x3000 => { 0x22, ... }
  41. // 0x3000 .. 0x3333 => { 0x44, ... } | Shared
  42. ASSERT_EQ(pg0_sz, ash_file.WriteAtCurrentPos(pg0.data(), pg0_sz));
  43. ASSERT_EQ(pg1_sz, ash_file.WriteAtCurrentPos(pg1.data(), pg1_sz));
  44. ASSERT_EQ(pg2_sz, ash_file.WriteAtCurrentPos(pg2.data(), pg2_sz));
  45. ASSERT_EQ(pg4_sz, ash_file.WriteAtCurrentPos(pg4.data(), pg4_sz));
  46. ASSERT_TRUE(ash_file.Flush());
  47. ash_file.Close();
  48. // Build Lacros's file structure:
  49. // 0x0000 .. 0x1000 => { 0x00, ... } | Shared
  50. // 0x1000 .. 0x2000 => { 0x11, ... } | Shared
  51. // 0x2000 .. 0x3000 => { 0x33, ... }
  52. // 0x3000 .. 0x3333 => { 0x44, ... } | Shared
  53. ASSERT_EQ(pg0_sz, lacros_file.WriteAtCurrentPos(pg0.data(), pg0_sz));
  54. ASSERT_EQ(pg1_sz, lacros_file.WriteAtCurrentPos(pg1.data(), pg1_sz));
  55. ASSERT_EQ(pg3_sz, lacros_file.WriteAtCurrentPos(pg3.data(), pg3_sz));
  56. ASSERT_EQ(pg4_sz, lacros_file.WriteAtCurrentPos(pg4.data(), pg4_sz));
  57. ASSERT_TRUE(lacros_file.Flush());
  58. // Load Lacros's file and try to merge against Ash's.
  59. IcuDataFile icu_data_file;
  60. ASSERT_TRUE(icu_data_file.Initialize(std::move(lacros_file),
  61. MemoryMappedFile::Region::kWholeFile));
  62. // NOTE: we need to manually call MergeWithAshVersion with a custom path,
  63. // because this test will be run in a linux-lacros-rel environment where
  64. // there's no Ash installed in the default ChromeOS directory.
  65. ASSERT_TRUE(icu_data_file.MergeWithAshVersion(ash_path));
  66. // Check that Lacros's file content is correct.
  67. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x0000, pg0.data(), pg0_sz));
  68. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x1000, pg1.data(), pg1_sz));
  69. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x2000, pg3.data(), pg3_sz));
  70. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x3000, pg4.data(), pg4_sz));
  71. // Parse the kernel's memory map structures to check if the merge happened.
  72. std::string proc_maps;
  73. std::vector<debug::MappedMemoryRegion> regions;
  74. ASSERT_TRUE(debug::ReadProcMaps(&proc_maps));
  75. ASSERT_TRUE(ParseProcMaps(proc_maps, &regions));
  76. uintptr_t lacros_start = reinterpret_cast<uintptr_t>(icu_data_file.data());
  77. bool region1_ok = false, region2_ok = false, region3_ok = false;
  78. for (const auto& region : regions) {
  79. if (region.start == lacros_start) {
  80. // 0x0000 .. 0x2000 => Ash (merged)
  81. EXPECT_EQ(lacros_start + 0x2000, region.end);
  82. EXPECT_EQ(ash_path.value(), region.path);
  83. region1_ok = true;
  84. } else if (region.start == lacros_start + 0x2000) {
  85. // 0x2000 .. 0x3000 => Lacros (not merged)
  86. EXPECT_EQ(lacros_start + 0x3000, region.end);
  87. EXPECT_EQ(lacros_path.value(), region.path);
  88. region2_ok = true;
  89. } else if (region.start == lacros_start + 0x3000) {
  90. // 0x3000 .. 0x3333 => Ash (merged)
  91. EXPECT_EQ(lacros_start + 0x4000, region.end); // Page-aligned address.
  92. EXPECT_EQ(ash_path.value(), region.path);
  93. region3_ok = true;
  94. }
  95. }
  96. EXPECT_TRUE(region1_ok && region2_ok && region3_ok);
  97. EXPECT_FALSE(icu_data_file.used_cached_hashes());
  98. }
  99. TEST_F(IcuMergeableDataFileTest, IcuDataFileMergesCommonPagesWithCachedHashes) {
  100. // Create two temporary files mocking Ash and Lacros's versions of ICU.
  101. base::ScopedTempDir temp_dir;
  102. ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  103. FilePath ash_path = temp_dir.GetPath().AppendASCII("ash_icudtl.dat");
  104. FilePath lacros_path = temp_dir.GetPath().AppendASCII("lacros_icudtl.dat");
  105. // Create the hash files as well.
  106. FilePath ash_hash_path = ash_path.AddExtensionASCII(
  107. IcuMergeableDataFile::kIcuDataFileHashExtension);
  108. FilePath lacros_hash_path = lacros_path.AddExtensionASCII(
  109. IcuMergeableDataFile::kIcuDataFileHashExtension);
  110. uint32_t flags =
  111. base::File::FLAG_CREATE | base::File::FLAG_READ | base::File::FLAG_WRITE;
  112. File ash_file(ash_path, flags);
  113. File ash_hash_file(ash_hash_path, flags);
  114. File lacros_file(lacros_path, flags);
  115. File lacros_hash_file(lacros_hash_path, flags);
  116. ASSERT_TRUE(ash_file.IsValid());
  117. ASSERT_TRUE(ash_hash_file.IsValid());
  118. ASSERT_TRUE(lacros_file.IsValid());
  119. ASSERT_TRUE(lacros_hash_file.IsValid());
  120. // Prepare some data to use for filling in the mock files.
  121. std::vector<char> pg0(0x1000, 0x00);
  122. std::vector<char> pg1(0x1000, 0x11);
  123. std::vector<char> pg2(0x1000, 0x22);
  124. std::vector<char> pg3(0x1000, 0x33);
  125. std::vector<char> pg4(0x0333, 0x44);
  126. int pg0_sz = pg0.size(), pg1_sz = pg1.size(), pg2_sz = pg2.size(),
  127. pg3_sz = pg3.size(), pg4_sz = pg4.size();
  128. // Build Ash's file structure:
  129. // 0x0000 .. 0x1000 => { 0x00, ... } | Shared
  130. // 0x1000 .. 0x2000 => { 0x11, ... } | Shared
  131. // 0x2000 .. 0x3000 => { 0x22, ... }
  132. // 0x3000 .. 0x3333 => { 0x44, ... } | Shared
  133. ASSERT_EQ(pg0_sz, ash_file.WriteAtCurrentPos(pg0.data(), pg0_sz));
  134. ASSERT_EQ(pg1_sz, ash_file.WriteAtCurrentPos(pg1.data(), pg1_sz));
  135. ASSERT_EQ(pg2_sz, ash_file.WriteAtCurrentPos(pg2.data(), pg2_sz));
  136. ASSERT_EQ(pg4_sz, ash_file.WriteAtCurrentPos(pg4.data(), pg4_sz));
  137. ASSERT_TRUE(ash_file.Flush());
  138. ash_file.Close();
  139. // Build Ash's hash file structure. Actual hashes don't matter.
  140. ASSERT_EQ(8, ash_hash_file.WriteAtCurrentPos(
  141. "\x00\x00\x00\x00\x00\x00\x00\x00", 8));
  142. ASSERT_EQ(8, ash_hash_file.WriteAtCurrentPos(
  143. "\x11\x11\x11\x11\x11\x11\x11\x11", 8));
  144. ASSERT_EQ(8, ash_hash_file.WriteAtCurrentPos(
  145. "\x22\x22\x22\x22\x22\x22\x22\x22", 8));
  146. ASSERT_EQ(8, ash_hash_file.WriteAtCurrentPos(
  147. "\x44\x44\x44\x44\x44\x44\x44\x44", 8));
  148. ASSERT_TRUE(ash_hash_file.Flush());
  149. ash_hash_file.Close();
  150. // Build Lacros's file structure:
  151. // 0x0000 .. 0x1000 => { 0x00, ... } | Shared
  152. // 0x1000 .. 0x2000 => { 0x11, ... } | Shared
  153. // 0x2000 .. 0x3000 => { 0x33, ... }
  154. // 0x3000 .. 0x3333 => { 0x44, ... } | Shared
  155. ASSERT_EQ(pg0_sz, lacros_file.WriteAtCurrentPos(pg0.data(), pg0_sz));
  156. ASSERT_EQ(pg1_sz, lacros_file.WriteAtCurrentPos(pg1.data(), pg1_sz));
  157. ASSERT_EQ(pg3_sz, lacros_file.WriteAtCurrentPos(pg3.data(), pg3_sz));
  158. ASSERT_EQ(pg4_sz, lacros_file.WriteAtCurrentPos(pg4.data(), pg4_sz));
  159. ASSERT_TRUE(lacros_file.Flush());
  160. // Build Lacros's hash file structure. Actual hashes don't matter.
  161. ASSERT_EQ(8, lacros_hash_file.WriteAtCurrentPos(
  162. "\x00\x00\x00\x00\x00\x00\x00\x00", 8));
  163. ASSERT_EQ(8, lacros_hash_file.WriteAtCurrentPos(
  164. "\x11\x11\x11\x11\x11\x11\x11\x11", 8));
  165. // NOTE: Simulate hash collision.
  166. ASSERT_EQ(8, lacros_hash_file.WriteAtCurrentPos(
  167. "\x22\x22\x22\x22\x22\x22\x22\x22", 8));
  168. ASSERT_EQ(8, lacros_hash_file.WriteAtCurrentPos(
  169. "\x44\x44\x44\x44\x44\x44\x44\x44", 8));
  170. ASSERT_TRUE(lacros_hash_file.Flush());
  171. lacros_hash_file.Close();
  172. // Load Lacros's file and try to merge against Ash's.
  173. IcuDataFile icu_data_file;
  174. ASSERT_TRUE(icu_data_file.Initialize(std::move(lacros_file),
  175. MemoryMappedFile::Region::kWholeFile));
  176. ASSERT_TRUE(icu_data_file.MergeWithAshVersion(ash_path));
  177. // Check that Lacros's file content is correct.
  178. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x0000, pg0.data(), pg0_sz));
  179. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x1000, pg1.data(), pg1_sz));
  180. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x2000, pg3.data(), pg3_sz));
  181. EXPECT_EQ(0, memcmp(icu_data_file.data() + 0x3000, pg4.data(), pg4_sz));
  182. // Parse the kernel's memory map structures to check if the merge happened.
  183. std::string proc_maps;
  184. std::vector<debug::MappedMemoryRegion> regions;
  185. ASSERT_TRUE(debug::ReadProcMaps(&proc_maps));
  186. ASSERT_TRUE(ParseProcMaps(proc_maps, &regions));
  187. uintptr_t lacros_start = reinterpret_cast<uintptr_t>(icu_data_file.data());
  188. bool region1_ok = false, region2_ok = false, region3_ok = false;
  189. for (const auto& region : regions) {
  190. if (region.start == lacros_start) {
  191. // 0x0000 .. 0x2000 => Ash (merged)
  192. EXPECT_EQ(lacros_start + 0x2000, region.end);
  193. EXPECT_EQ(ash_path.value(), region.path);
  194. region1_ok = true;
  195. } else if (region.start == lacros_start + 0x2000) {
  196. // 0x2000 .. 0x3000 => Lacros (not merged)
  197. EXPECT_EQ(lacros_start + 0x3000, region.end);
  198. EXPECT_EQ(lacros_path.value(), region.path);
  199. region2_ok = true;
  200. } else if (region.start == lacros_start + 0x3000) {
  201. // 0x3000 .. 0x3333 => Ash (merged)
  202. EXPECT_EQ(lacros_start + 0x4000, region.end); // Page-aligned address.
  203. EXPECT_EQ(ash_path.value(), region.path);
  204. region3_ok = true;
  205. }
  206. }
  207. EXPECT_TRUE(region1_ok && region2_ok && region3_ok);
  208. EXPECT_TRUE(icu_data_file.used_cached_hashes());
  209. }
  210. } // namespace base::i18n
  211. #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
  212. #endif // !BUILDFLAG(IS_NACL)