codegen.rs 5.6 KB

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