codegen.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // SPDX-License-Identifier: Apache-2.0
  2. use assert_cmd::Command;
  3. use rayon::prelude::*;
  4. use std::ffi::OsString;
  5. use std::fs::File;
  6. use std::io::{BufRead, BufReader};
  7. use std::{fs, path::PathBuf};
  8. #[test]
  9. fn solidity_testcases() {
  10. run_test_for_path("./tests/codegen_testcases/solidity/");
  11. }
  12. #[test]
  13. fn yul_testcases() {
  14. run_test_for_path("./tests/codegen_testcases/yul/")
  15. }
  16. fn run_test_for_path(path: &str) {
  17. let mut tests = Vec::new();
  18. let ext = OsString::from("sol");
  19. for entry in fs::read_dir(path).unwrap() {
  20. let path = entry.unwrap().path();
  21. if path.is_file() && path.extension() == Some(&ext) {
  22. tests.push(path);
  23. }
  24. }
  25. tests.into_par_iter().for_each(testcase);
  26. }
  27. #[derive(Debug)]
  28. enum Test {
  29. Check(String),
  30. CheckAbsent(String),
  31. NotCheck(String),
  32. Fail(String),
  33. Rewind,
  34. }
  35. fn testcase(path: PathBuf) {
  36. // find the args to run.
  37. println!("testcase: {}", path.display());
  38. let file = File::open(&path).unwrap();
  39. let reader = BufReader::new(file);
  40. let mut command_line: Option<String> = None;
  41. let mut checks = Vec::new();
  42. let mut fails = Vec::new();
  43. let mut read_from = None;
  44. for line in reader.lines() {
  45. let mut line = line.unwrap();
  46. line = line.trim().parse().unwrap();
  47. // The first line should be a command line (excluding "solang compile") after // RUN:
  48. if let Some(args) = line.strip_prefix("// RUN: ") {
  49. assert_eq!(command_line, None);
  50. command_line = Some(String::from(args));
  51. // Read the contents of a file, e.g. the llvm-ir output of // RUN: --emit llvm-ir
  52. // rather than the stdout of the command
  53. } else if let Some(check) = line.strip_prefix("// READ:") {
  54. read_from = Some(check.trim().to_string());
  55. // Read more input until you find a line that contains the needle // CHECK: needle
  56. } else if let Some(check) = line.strip_prefix("// CHECK:") {
  57. checks.push(Test::Check(check.trim().to_string()));
  58. //
  59. } else if let Some(fail) = line.strip_prefix("// FAIL:") {
  60. fails.push(Test::Fail(fail.trim().to_string()));
  61. // Ensure that the following line in the input does not match
  62. } else if let Some(not_check) = line.strip_prefix("// NOT-CHECK:") {
  63. checks.push(Test::NotCheck(not_check.trim().to_string()));
  64. // Check the output from here until the end of the file does not contain the needle
  65. } else if let Some(check_absent) = line.strip_prefix("// CHECK-ABSENT:") {
  66. checks.push(Test::CheckAbsent(check_absent.trim().to_string()));
  67. // Go back to the beginning and find the needle from there, like // CHECK: but from
  68. // the beginning of the file.
  69. } else if let Some(check) = line.strip_prefix("// BEGIN-CHECK:") {
  70. checks.push(Test::Rewind);
  71. checks.push(Test::Check(check.trim().to_string()));
  72. }
  73. }
  74. let args = command_line.expect("cannot find RUN: line");
  75. assert_ne!(checks.len() + fails.len(), 0);
  76. let mut cmd = Command::cargo_bin("solang").unwrap();
  77. let assert = cmd
  78. .arg("compile")
  79. .args(args.split_whitespace())
  80. .arg(format!("{}", path.canonicalize().unwrap().display()))
  81. .assert();
  82. let output = assert.get_output();
  83. let stdout = String::from_utf8_lossy(&output.stdout);
  84. let stderr = String::from_utf8_lossy(&output.stderr);
  85. let mut current_check = 0;
  86. let mut current_fail = 0;
  87. let mut current_line = 0;
  88. let contents = if let Some(file) = read_from {
  89. fs::read_to_string(file).unwrap()
  90. } else {
  91. stdout.to_string()
  92. };
  93. let lines: Vec<&str> = contents.split('\n').chain(stderr.split('\n')).collect();
  94. while current_line < lines.len() {
  95. let line = lines[current_line];
  96. match checks.get(current_check) {
  97. Some(Test::Check(needle)) => {
  98. if line.contains(needle) {
  99. current_check += 1;
  100. }
  101. }
  102. Some(Test::NotCheck(needle)) => {
  103. if !line.contains(needle) {
  104. current_check += 1;
  105. // We should not advance line during a not check
  106. current_line -= 1;
  107. }
  108. }
  109. Some(Test::CheckAbsent(needle)) => {
  110. for line in lines.iter().skip(current_line) {
  111. if line.contains(needle) {
  112. panic!(
  113. "FOUND CHECK-ABSENT: {:?}, {}",
  114. checks[current_check],
  115. path.display()
  116. );
  117. }
  118. }
  119. current_check += 1;
  120. }
  121. Some(Test::Rewind) => {
  122. current_line = 0;
  123. current_check += 1;
  124. continue;
  125. }
  126. _ => (),
  127. }
  128. if let Some(Test::Fail(needle)) = fails.get(current_fail) {
  129. if line.contains(needle) {
  130. current_fail += 1;
  131. }
  132. }
  133. current_line += 1;
  134. }
  135. if current_check < checks.len() {
  136. println!("{stderr}");
  137. println!("OUTPUT: \n===8<===8<===\n{stdout}===8<===8<===\n");
  138. panic!(
  139. "NOT FOUND CHECK: {:?}, {}",
  140. checks[current_check],
  141. path.display()
  142. );
  143. } else if current_fail < fails.len() {
  144. println!("STDERR: \n===8<===8<===\n{stderr}===8<===8<===\n");
  145. panic!("NOT FOUND FAIL: {:?}", fails[current_check]);
  146. }
  147. }