source_gen_test.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "testing/base/source_gen.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include "common/set.h"
  8. #include "testing/base/gtest_main.h"
  9. #include "toolchain/driver/driver.h"
  10. namespace Carbon::Testing {
  11. namespace {
  12. using ::testing::AllOf;
  13. using ::testing::ContainerEq;
  14. using ::testing::Contains;
  15. using ::testing::Each;
  16. using ::testing::Eq;
  17. using ::testing::Ge;
  18. using ::testing::Gt;
  19. using ::testing::Le;
  20. using ::testing::MatchesRegex;
  21. using ::testing::SizeIs;
  22. // Tiny helper to sum the sizes of a range of ranges. Uses a template to avoid
  23. // hard coding any specific types for the two ranges.
  24. template <typename T>
  25. static auto SumSizes(const T& range) -> ssize_t {
  26. ssize_t sum = 0;
  27. for (const auto& inner_range : range) {
  28. sum += inner_range.size();
  29. }
  30. return sum;
  31. }
  32. TEST(SourceGenTest, Identifiers) {
  33. SourceGen gen;
  34. auto idents = gen.GetShuffledIdentifiers(1000);
  35. EXPECT_THAT(idents.size(), Eq(1000));
  36. for (llvm::StringRef ident : idents) {
  37. EXPECT_THAT(ident, MatchesRegex("[A-Za-z][A-Za-z0-9_]*"));
  38. }
  39. // We should have at least one identifier of each length [1, 64]. The exact
  40. // distribution is an implementation detail designed to vaguely match the
  41. // expected distribution in source code.
  42. for (int size : llvm::seq_inclusive(1, 64)) {
  43. EXPECT_THAT(idents, Contains(SizeIs(size)));
  44. }
  45. // Check that identifiers 4 characters or shorter are more common than longer
  46. // lengths. This is a very rough way of double checking that we got the
  47. // intended distribution.
  48. for (int short_size : llvm::seq_inclusive(1, 4)) {
  49. int short_count = llvm::count_if(idents, [&](auto ident) {
  50. return static_cast<int>(ident.size()) == short_size;
  51. });
  52. for (int long_size : llvm::seq_inclusive(5, 64)) {
  53. EXPECT_THAT(short_count, Gt(llvm::count_if(idents, [&](auto ident) {
  54. return static_cast<int>(ident.size()) == long_size;
  55. })));
  56. }
  57. }
  58. // Check that repeated calls are different in interesting ways, but have the
  59. // exact same total bytes.
  60. ssize_t idents_size_sum = SumSizes(idents);
  61. for ([[maybe_unused]] int _ : llvm::seq(10)) {
  62. auto idents2 = gen.GetShuffledIdentifiers(1000);
  63. EXPECT_THAT(idents2, SizeIs(1000));
  64. // Should be (at least) a different shuffle of identifiers.
  65. EXPECT_THAT(idents2, Not(ContainerEq(idents)));
  66. // But the sum of lengths should be identical.
  67. EXPECT_THAT(SumSizes(idents2), Eq(idents_size_sum));
  68. }
  69. // Check length constraints have the desired effect.
  70. idents =
  71. gen.GetShuffledIdentifiers(1000, /*min_length=*/10, /*max_length=*/20);
  72. EXPECT_THAT(idents, Each(SizeIs(AllOf(Ge(10), Le(20)))));
  73. }
  74. TEST(SourceGenTest, UniformIdentifiers) {
  75. SourceGen gen;
  76. // Check that uniform identifier length results in exact coverage of each
  77. // possible length for an easy case, both without and with a remainder.
  78. auto idents =
  79. gen.GetShuffledIdentifiers(100, /*min_length=*/10, /*max_length=*/19,
  80. /*uniform=*/true);
  81. EXPECT_THAT(idents, Contains(SizeIs(10)).Times(10));
  82. EXPECT_THAT(idents, Contains(SizeIs(11)).Times(10));
  83. EXPECT_THAT(idents, Contains(SizeIs(12)).Times(10));
  84. EXPECT_THAT(idents, Contains(SizeIs(13)).Times(10));
  85. EXPECT_THAT(idents, Contains(SizeIs(14)).Times(10));
  86. EXPECT_THAT(idents, Contains(SizeIs(15)).Times(10));
  87. EXPECT_THAT(idents, Contains(SizeIs(16)).Times(10));
  88. EXPECT_THAT(idents, Contains(SizeIs(17)).Times(10));
  89. EXPECT_THAT(idents, Contains(SizeIs(18)).Times(10));
  90. EXPECT_THAT(idents, Contains(SizeIs(19)).Times(10));
  91. idents = gen.GetShuffledIdentifiers(97, /*min_length=*/10, /*max_length=*/19,
  92. /*uniform=*/true);
  93. EXPECT_THAT(idents, Contains(SizeIs(10)).Times(10));
  94. EXPECT_THAT(idents, Contains(SizeIs(11)).Times(10));
  95. EXPECT_THAT(idents, Contains(SizeIs(12)).Times(10));
  96. EXPECT_THAT(idents, Contains(SizeIs(13)).Times(10));
  97. EXPECT_THAT(idents, Contains(SizeIs(14)).Times(10));
  98. EXPECT_THAT(idents, Contains(SizeIs(15)).Times(10));
  99. EXPECT_THAT(idents, Contains(SizeIs(16)).Times(10));
  100. EXPECT_THAT(idents, Contains(SizeIs(17)).Times(9));
  101. EXPECT_THAT(idents, Contains(SizeIs(18)).Times(9));
  102. EXPECT_THAT(idents, Contains(SizeIs(19)).Times(9));
  103. }
  104. // Largely covered by `Identifiers` and `UniformIdentifiers`, but need to check
  105. // for uniqueness specifically.
  106. TEST(SourceGenTest, UniqueIdentifiers) {
  107. SourceGen gen;
  108. auto unique = gen.GetShuffledUniqueIdentifiers(1000);
  109. EXPECT_THAT(unique.size(), Eq(1000));
  110. Set<llvm::StringRef> set;
  111. for (llvm::StringRef ident : unique) {
  112. EXPECT_THAT(ident, MatchesRegex("[A-Za-z][A-Za-z0-9_]*"));
  113. EXPECT_TRUE(set.Insert(ident).is_inserted())
  114. << "Colliding identifier: " << ident;
  115. }
  116. // Check single length specifically where uniqueness is the most challenging.
  117. set.Clear();
  118. unique = gen.GetShuffledUniqueIdentifiers(1000, /*min_length=*/4,
  119. /*max_length=*/4);
  120. for (llvm::StringRef ident : unique) {
  121. EXPECT_TRUE(set.Insert(ident).is_inserted())
  122. << "Colliding identifier: " << ident;
  123. }
  124. }
  125. // Check that the source code doesn't have compiler errors.
  126. auto TestCompile(llvm::StringRef source) -> bool {
  127. llvm::vfs::InMemoryFileSystem fs;
  128. InstallPaths installation(
  129. InstallPaths::MakeForBazelRunfiles(Testing::GetTestExePath()));
  130. Driver driver(fs, &installation, llvm::outs(), llvm::errs());
  131. // Load the prelude into our VFS.
  132. //
  133. // TODO: Factor this and analogous code in file_test into a Driver helper.
  134. auto prelude =
  135. Driver::FindPreludeFiles(installation.core_package(), llvm::errs());
  136. CARBON_CHECK(!prelude.empty());
  137. for (const auto& path : prelude) {
  138. llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
  139. llvm::MemoryBuffer::getFile(path);
  140. CARBON_CHECK(file) << file.getError().message();
  141. CARBON_CHECK(fs.addFile(path, /*ModificationTime=*/0, std::move(*file)))
  142. << "Duplicate file: " << path;
  143. }
  144. fs.addFile("test.carbon", /*ModificationTime=*/0,
  145. llvm::MemoryBuffer::getMemBuffer(source));
  146. return driver.RunCommand({"compile", "--phase=check", "test.carbon"}).success;
  147. }
  148. TEST(SourceGenTest, GenAPIFileDenseDeclsTest) {
  149. SourceGen gen;
  150. std::string source =
  151. gen.GenAPIFileDenseDecls(1000, SourceGen::DenseDeclParams{});
  152. // Should be within 1% of the requested line count.
  153. EXPECT_THAT(source, Contains('\n').Times(AllOf(Ge(950), Le(1050))));
  154. // Make sure we generated valid Carbon code.
  155. EXPECT_TRUE(TestCompile(source));
  156. }
  157. TEST(SourceGenTest, GenAPIFileDenseDeclsCppTest) {
  158. SourceGen gen(SourceGen::Language::Cpp);
  159. // Generate a 1000-line file which is enough to have a reasonably accurate
  160. // line count estimate and have a few classes.
  161. std::string source =
  162. gen.GenAPIFileDenseDecls(1000, SourceGen::DenseDeclParams{});
  163. // Should be within 10% of the requested line count.
  164. EXPECT_THAT(source, Contains('\n').Times(AllOf(Ge(900), Le(1100))));
  165. // TODO: When the driver supports compiling C++ code as easily as Carbon, we
  166. // should test that the generated C++ code is valid.
  167. }
  168. } // namespace
  169. } // namespace Carbon::Testing