crash_test.cr 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # SPDX-FileCopyrightText: 2024 mio <stigma@disroot.org>
  2. # SPDX-License-Identifier: BSD-3-Clause
  3. # A rather slow (and not necessarily correct*) method of testing luce.
  4. # It retrieves a list of shards from shardbox.org (by category), and
  5. # attempts to parse any markdown file found in the shard's repository.
  6. #
  7. # After each test there is a delay (SHARD_TEST_DELAY_SECONDS), and
  8. # once a category has been complete there is another delay
  9. # (CATEGORY_TEST_DELAY_SECONDS).
  10. #
  11. # In its current incarnation, this script may take a few hours to
  12. # complete.
  13. #
  14. # * This only checks whether luce raises an exception, not if the HTML
  15. # generated is correct.
  16. require "file_utils"
  17. require "http/client"
  18. require "yaml"
  19. require "../src/luce.cr"
  20. SHARD_TEST_DELAY_SECONDS = 8
  21. CATEGORY_TEST_DELAY_SECONDS = 16
  22. enum TestResultType
  23. Success
  24. Skipped
  25. Failed
  26. end
  27. record TestResult, type : TestResultType, message : String
  28. # tip to update:
  29. # git clone https://github.com/shardbox/catalog --depth 1
  30. # cd catalog/catalog
  31. # find -printf "%f\n" | sort | cut -d'.' -f1
  32. SHARDBOX_CATEGORIES = [
  33. "Algorithms_and_Data_structures",
  34. "Api_Builders",
  35. "Audio",
  36. "Blockchain",
  37. "Caching",
  38. "C_bindings",
  39. "CLI_Applications",
  40. "CLI_Builders",
  41. "CLI_Utils",
  42. "Code_Analysis_and_Metrics",
  43. "Compression",
  44. "Concurrency",
  45. "Configuration",
  46. "Converters",
  47. "Cryptography",
  48. "Database_Drivers_Clients",
  49. "Database_Tools",
  50. "Data_Formats",
  51. "Data_Generators",
  52. "Debugging",
  53. "Dependency_Injection",
  54. "Development_Tools",
  55. "Email",
  56. "Environment_Management",
  57. "Examples_and_funny_stuff",
  58. "Feature_Flipping",
  59. "Framework_Components",
  60. "Game_Development",
  61. "GUI_Development",
  62. "GUI_library",
  63. "HTML_Builders",
  64. "HTML_XML_Parsing",
  65. "HTTP",
  66. "Image_processing",
  67. "Implementations_Compilers",
  68. "Internationalization",
  69. "Logging_and_monitoring",
  70. "Machine_Learning",
  71. "Markdown_Text_Processors",
  72. "Misc",
  73. "Natural_Language",
  74. "Networking",
  75. "Network_Protocols",
  76. "ORM_ODM_Extensions",
  77. "Package_Management",
  78. "Parser_Generators",
  79. "Processes_and_Threads",
  80. "Project_Generators",
  81. "Queues_and_Messaging",
  82. "Routing",
  83. "Scheduling",
  84. "Science_and_Data_analysis",
  85. "Search",
  86. "Serverless_Computing",
  87. "System",
  88. "Task_management",
  89. "Template_Engine",
  90. "Testing",
  91. "Third-party_APIs",
  92. "Time_Date",
  93. "Uncategorized",
  94. "Validation",
  95. "Web_Applications",
  96. "Web_Frameworks",
  97. "Web_Servers",
  98. ]
  99. def fetch_category_yaml(category : String) : YAML::Any?
  100. puts "Downloading #{category} YAML file..."
  101. yaml_file_res = HTTP::Client.get "https://raw.githubusercontent.com/shardbox/catalog/master/catalog/#{category}.yml"
  102. unless yaml_file_res.success?
  103. STDERR.puts "Failed to download YAML file for category: #{category}"
  104. STDERR.puts "#{yaml_file_res.status} (#{yaml_file_res.status_code}): #{yaml_file_res.status_message}"
  105. return nil
  106. end
  107. YAML.parse(yaml_file_res.body)
  108. end
  109. def test_shard(shard : YAML::Any) : TestResult
  110. url = if shard["github"]?
  111. "https://github.com/#{shard["github"]}"
  112. elsif shard["gitlab"]?
  113. "https://gitlab.com/#{shard["gitlab"]}"
  114. elsif shard["bitbucket"]?
  115. "https://bitbucket.org/#{shard["bitbucket"]}"
  116. elsif shard["git"]?
  117. "#{shard["git"]}"
  118. else
  119. raise "Unsupported shard host: #{shard}"
  120. end
  121. HTTP::Client.head(url) do |response|
  122. return TestResult.new(TestResultType::Skipped, "404: #{url}") unless response.success?
  123. end
  124. dir = File.basename(url)
  125. # TODO: Get terminal width and truncate url
  126. puts " Cloning git repository: #{url}"
  127. Process.run("git", ["clone", url, "--depth=1", dir])
  128. done = Channel(Nil).new
  129. Dir.glob("#{dir}/**/*.md").each do |md|
  130. spawn do
  131. contents = File.read(md)
  132. Luce.to_html(contents, extension_set: Luce::ExtensionSet::GITHUB_WEB)
  133. done.send(nil)
  134. end
  135. select
  136. when res = done.receive
  137. break
  138. when timeout(2.minutes)
  139. return TestResult.new(TestResultType::Failed, "#{shard}\n Reason: Timed out")
  140. end
  141. rescue ex
  142. return TestResult.new(TestResultType::Failed, "#{shard}\n Reason: #{ex}")
  143. end
  144. sleep SHARD_TEST_DELAY_SECONDS
  145. TestResult.new(TestResultType::Success, "")
  146. ensure
  147. FileUtils.rm_rf(dir) unless dir.nil?
  148. end
  149. def main(args = ARGV)
  150. errors = [] of String
  151. skipped = [] of String
  152. scanned_shards = 0
  153. start = Time.monotonic
  154. Process.on_interrupt do
  155. print "\r"
  156. puts "## Finished scanning"
  157. puts "Scanned #{scanned_shards} shards in #{Time.monotonic - start}"
  158. unless skipped.empty?
  159. puts "Skipped shards:"
  160. skipped.each { |skip| puts " #{skip}" }
  161. puts "Found #{skipped.size} case(s) where we couldn't test Luce.to_html"
  162. end
  163. unless errors.empty?
  164. puts "Found issues:"
  165. errors.each { |error| puts " #{error}" }
  166. puts "Found #{errors.size} case(s) where Luce.to_html raised an error!"
  167. end
  168. abort
  169. end
  170. SHARDBOX_CATEGORIES.each do |category|
  171. yaml = fetch_category_yaml(category)
  172. next if yaml.nil?
  173. puts "\rTesting all shards from #{yaml["name"]}..."
  174. yaml["shards"].as_a.each do |shard|
  175. result = test_shard shard
  176. case result.type
  177. when TestResultType::Failed
  178. errors << result.message
  179. when TestResultType::Skipped
  180. skipped << result.message
  181. end
  182. scanned_shards += 1
  183. end
  184. sleep CATEGORY_TEST_DELAY_SECONDS
  185. end
  186. puts "## Finished scanning"
  187. puts "Scanned #{scanned_shards} shards in #{Time.monotonic - start}"
  188. unless skipped.empty?
  189. puts "Skipped shards:"
  190. skipped.each { |skip| puts " #{skip}" }
  191. puts "Found #{skipped.size} case(s) where we couldn't test Luce.to_html"
  192. end
  193. unless errors.empty?
  194. puts "Found issues:"
  195. errors.each { |error| puts " #{error}" }
  196. puts "Found #{errors.size} case(s) where Luce.to_html raised an error!"
  197. end
  198. end
  199. begin
  200. Process.run("git")
  201. rescue
  202. STDERR.puts "`git` executable not available. See https://git-scm.com"
  203. exit 1
  204. end
  205. main