main.rs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * This is a small program that is meant for testing the AML parser on artificial
  3. * AML. We want to:
  4. * - scan a directory for ASL files
  5. * - compile them using `iasl` into AML files (these should be gitignored), but only if the ASL file has a
  6. * newer timestamp than the AML file (or just compile if there isn't a corresponding AML file)
  7. * - Run the AML parser on each AML file, printing test output like `cargo test` does in a nice table for
  8. * each AML file
  9. * - For failing tests, print out a nice summary of the errors for each file
  10. */
  11. use aml::{AmlContext, DebugVerbosity};
  12. use clap::{Arg, ArgAction};
  13. use std::{
  14. ffi::OsStr,
  15. fs::{self, File},
  16. io::{Read, Write},
  17. ops::Not,
  18. path::Path,
  19. process::Command,
  20. };
  21. fn main() -> std::io::Result<()> {
  22. log::set_logger(&Logger).unwrap();
  23. log::set_max_level(log::LevelFilter::Trace);
  24. let matches = clap::Command::new("aml_tester")
  25. .version("v0.1.0")
  26. .author("Isaac Woods")
  27. .about("Compiles and tests ASL files")
  28. .arg(Arg::new("path").short('p').long("path").required(true).action(ArgAction::Set).value_name("DIR"))
  29. .arg(Arg::new("no_compile").long("no-compile").action(ArgAction::SetTrue))
  30. .get_matches();
  31. let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
  32. println!("Running tests in directory: {:?}", dir_path);
  33. if !matches.get_flag("no_compile") {
  34. let (passed, failed) = compile_asl_files(dir_path)?;
  35. println!("Compiled {} ASL files: {} passed, {} failed.", passed + failed, passed, failed);
  36. }
  37. /*
  38. * Now, we find all the AML files in the directory, and try to compile them with the `aml`
  39. * parser.
  40. */
  41. let aml_files = fs::read_dir(dir_path)?
  42. .filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("aml")))
  43. .map(Result::unwrap);
  44. let (passed, failed) = aml_files.fold((0, 0), |(passed, failed), file_entry| {
  45. print!("Testing AML file: {:?}... ", file_entry.path());
  46. std::io::stdout().flush().unwrap();
  47. let mut file = File::open(file_entry.path()).unwrap();
  48. let mut contents = Vec::new();
  49. file.read_to_end(&mut contents).unwrap();
  50. const AML_TABLE_HEADER_LENGTH: usize = 36;
  51. let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
  52. match context.parse_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
  53. Ok(()) => {
  54. println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
  55. println!("Namespace: {:#?}", context.namespace);
  56. (passed + 1, failed)
  57. }
  58. Err(err) => {
  59. println!("{}Failed ({:?}){}", termion::color::Fg(termion::color::Red), err, termion::style::Reset);
  60. println!("Namespace: {:#?}", context.namespace);
  61. (passed, failed + 1)
  62. }
  63. }
  64. });
  65. println!("Test results: {} passed, {} failed", passed, failed);
  66. Ok(())
  67. }
  68. fn compile_asl_files(dir_path: &Path) -> std::io::Result<(u32, u32)> {
  69. let mut asl_files = fs::read_dir(dir_path)?
  70. .filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("asl")))
  71. .map(Result::unwrap)
  72. .peekable();
  73. if !asl_files.peek().is_none() {
  74. // Test if `iasl` is installed, so we can give a good error if it's not
  75. match Command::new("iasl").arg("-v").status() {
  76. Ok(exit_status) => if exit_status.success().not() {
  77. panic!("`iasl` exited with unsuccessfull status: {:?}", exit_status);
  78. },
  79. Err(_) => panic!("`iasl` is not installed, but we want to compile some ASL files! Pass --no-compile, or install `iasl`"),
  80. }
  81. }
  82. let mut passed = 0;
  83. let mut failed = 0;
  84. for file in asl_files {
  85. let aml_path = file.path().with_extension(OsStr::new("aml"));
  86. /*
  87. * Check if an AML path with a matching last-modified date exists. If it
  88. * does, we don't need to compile the ASL file again.
  89. */
  90. if aml_path.is_file() {
  91. let asl_last_modified = file.metadata()?.modified()?;
  92. let aml_last_modified = aml_path.metadata()?.modified()?;
  93. if asl_last_modified <= aml_last_modified {
  94. continue;
  95. }
  96. }
  97. // Compile the ASL file using `iasl`
  98. println!("Compiling file: {}", file.path().to_str().unwrap());
  99. let output = Command::new("iasl").arg(file.path()).output()?;
  100. if output.status.success() {
  101. passed += 1;
  102. } else {
  103. failed += 1;
  104. println!(
  105. "Failed to compile ASL file: {}. Output from iasl:\n {}",
  106. file.path().to_str().unwrap(),
  107. String::from_utf8_lossy(&output.stderr)
  108. );
  109. }
  110. }
  111. Ok((passed, failed))
  112. }
  113. struct Logger;
  114. impl log::Log for Logger {
  115. fn enabled(&self, _: &log::Metadata) -> bool {
  116. true
  117. }
  118. fn log(&self, record: &log::Record) {
  119. println!("[{}] {}", record.level(), record.args());
  120. }
  121. fn flush(&self) {
  122. std::io::stdout().flush().unwrap();
  123. }
  124. }
  125. struct Handler;
  126. impl aml::Handler for Handler {
  127. fn read_u8(&self, _address: usize) -> u8 {
  128. unimplemented!()
  129. }
  130. fn read_u16(&self, _address: usize) -> u16 {
  131. unimplemented!()
  132. }
  133. fn read_u32(&self, _address: usize) -> u32 {
  134. unimplemented!()
  135. }
  136. fn read_u64(&self, _address: usize) -> u64 {
  137. unimplemented!()
  138. }
  139. fn write_u8(&mut self, _address: usize, _value: u8) {
  140. unimplemented!()
  141. }
  142. fn write_u16(&mut self, _address: usize, _value: u16) {
  143. unimplemented!()
  144. }
  145. fn write_u32(&mut self, _address: usize, _value: u32) {
  146. unimplemented!()
  147. }
  148. fn write_u64(&mut self, _address: usize, _value: u64) {
  149. unimplemented!()
  150. }
  151. fn read_io_u8(&self, _port: u16) -> u8 {
  152. unimplemented!()
  153. }
  154. fn read_io_u16(&self, _port: u16) -> u16 {
  155. unimplemented!()
  156. }
  157. fn read_io_u32(&self, _port: u16) -> u32 {
  158. unimplemented!()
  159. }
  160. fn write_io_u8(&self, _port: u16, _value: u8) {
  161. unimplemented!()
  162. }
  163. fn write_io_u16(&self, _port: u16, _value: u16) {
  164. unimplemented!()
  165. }
  166. fn write_io_u32(&self, _port: u16, _value: u32) {
  167. unimplemented!()
  168. }
  169. fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 {
  170. unimplemented!()
  171. }
  172. fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 {
  173. unimplemented!()
  174. }
  175. fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 {
  176. unimplemented!()
  177. }
  178. fn write_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u8) {
  179. unimplemented!()
  180. }
  181. fn write_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u16) {
  182. unimplemented!()
  183. }
  184. fn write_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u32) {
  185. unimplemented!()
  186. }
  187. }