diff --git a/.gitignore b/.gitignore index 3de2d377b4e..3b09835e4ac 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ forc-plugins/forc-debug/tests/**/Forc.lock # Generated files in example directories examples/**/*/Forc.lock docs/reference/src/code/examples/**/*/Forc.lock + +# Insta files +*.snap.new diff --git a/Cargo.lock b/Cargo.lock index b723e910ec4..012aea11ff3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1743,6 +1743,12 @@ dependencies = [ "str-buf", ] +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "escargot" version = "0.5.12" @@ -4048,6 +4054,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libtest-mimic" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58" +dependencies = [ + "clap 4.5.16", + "escape8259", + "termcolor", + "threadpool", +] + [[package]] name = "libz-sys" version = "1.1.19" @@ -4624,6 +4642,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + [[package]] name = "num_enum" version = "0.5.11" @@ -7245,6 +7273,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminal_size" version = "0.3.0" @@ -7284,7 +7321,10 @@ dependencies = [ "fuel-vm", "futures", "gag", + "glob", "hex", + "insta", + "libtest-mimic", "miden", "prettydiff 0.6.4", "rand", @@ -7300,6 +7340,7 @@ dependencies = [ "tokio", "toml 0.7.8", "tracing", + "vte 0.13.0", ] [[package]] @@ -7366,6 +7407,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tikv-jemalloc-sys" version = "0.5.4+5.3.0-patched" @@ -8058,7 +8108,7 @@ dependencies = [ "itoa", "log", "unicode-width", - "vte", + "vte 0.11.1", ] [[package]] @@ -8072,6 +8122,17 @@ dependencies = [ "vte_generate_state_changes", ] +[[package]] +name = "vte" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40eb22ae96f050e0c0d6f7ce43feeae26c348fc4dea56928ca81537cfaa6188b" +dependencies = [ + "arrayvec 0.7.4", + "utf8parse", + "vte_generate_state_changes", +] + [[package]] name = "vte_generate_state_changes" version = "0.1.2" diff --git a/sway-core/src/language/ty/declaration/function.rs b/sway-core/src/language/ty/declaration/function.rs index f6304845c35..cc7f79f75dd 100644 --- a/sway-core/src/language/ty/declaration/function.rs +++ b/sway-core/src/language/ty/declaration/function.rs @@ -572,15 +572,16 @@ impl TyFunctionSig { } pub fn is_concrete(&self, engines: &Engines) -> bool { - self.return_type.is_concrete(engines, IncludeNumeric::No) + self.return_type + .is_concrete(engines, TreatNumericAs::Concrete) && self .parameters .iter() - .all(|p| p.is_concrete(engines, IncludeNumeric::No)) + .all(|p| p.is_concrete(engines, TreatNumericAs::Concrete)) && self .type_parameters .iter() - .all(|p| p.is_concrete(engines, IncludeNumeric::No)) + .all(|p| p.is_concrete(engines, TreatNumericAs::Concrete)) } /// Returns a String representing the function. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 0df9f72e458..70c83d9b07a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -523,7 +523,7 @@ fn type_check_encode_append( }; // only supported types - if item_type.is_concrete(engines, IncludeNumeric::Yes) { + if item_type.is_concrete(engines, TreatNumericAs::Abstract) { match &*engines.te().get(item_type) { TypeInfo::Boolean | TypeInfo::UnsignedInteger(IntegerBits::Eight) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 26c862b21a1..0778bb1b19e 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -38,6 +38,7 @@ use crate::{ use ast_node::declaration::{insert_supertraits_into_namespace, SupertraitOf}; use either::Either; use indexmap::IndexMap; +use itertools::Itertools; use rustc_hash::FxHashSet; use std::collections::{HashMap, VecDeque}; use sway_ast::intrinsics::Intrinsic; @@ -1858,7 +1859,36 @@ impl ty::TyExpression { }) .collect(); - let elem_type = typed_contents[0].return_type; + // choose the best type to be the array elem type + use itertools::FoldWhile::{Continue, Done}; + let elem_type = typed_contents + .iter() + .fold_while(None, |last, current| match last { + None => Continue(Some(current.return_type)), + Some(last) => { + if last.is_concrete(engines, TreatNumericAs::Abstract) { + return Done(Some(last)); + } + + if current + .return_type + .is_concrete(engines, TreatNumericAs::Abstract) + { + return Done(Some(current.return_type)); + } + + let last_info = ctx.engines().te().get(last); + let current_info = ctx.engines().te().get(current.return_type); + match (&*last_info, &*current_info) { + (TypeInfo::Numeric, TypeInfo::UnsignedInteger(_)) => { + Done(Some(current.return_type)) + } + _ => Continue(Some(last)), + } + } + }) + .into_inner(); + let elem_type = elem_type.unwrap_or_else(|| typed_contents[0].return_type); let array_count = typed_contents.len(); Ok(ty::TyExpression { diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index ea83cd08847..8cd4b8aac08 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -60,7 +60,7 @@ pub(crate) fn type_check_method_application( x.return_type .extract_inner_types(engines, IncludeSelf::Yes) .iter() - .any(|x| !x.is_concrete(engines, IncludeNumeric::Yes)) + .any(|x| !x.is_concrete(engines, TreatNumericAs::Abstract)) }) .unwrap_or_default(); let needs_second_pass = has_errors || is_not_concrete; diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index 9410fd2d6ac..273fd1f0366 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -27,9 +27,9 @@ pub enum IncludeSelf { No, } -pub enum IncludeNumeric { - Yes, - No, +pub enum TreatNumericAs { + Abstract, + Concrete, } /// A identifier to uniquely refer to our type terms @@ -461,27 +461,33 @@ impl TypeId { })) } - pub(crate) fn is_concrete(&self, engines: &Engines, include_numeric: IncludeNumeric) -> bool { + pub(crate) fn is_concrete( + &self, + engines: &Engines, + numeric_non_concrete: TreatNumericAs, + ) -> bool { let nested_types = (*self).extract_nested_types(engines); - !nested_types.into_iter().any(|x| match include_numeric { - IncludeNumeric::Yes => matches!( - x, - TypeInfo::UnknownGeneric { .. } - | TypeInfo::Custom { .. } - | TypeInfo::Placeholder(..) - | TypeInfo::TraitType { .. } - | TypeInfo::TypeParam(..) - | TypeInfo::Numeric - ), - IncludeNumeric::No => matches!( - x, - TypeInfo::UnknownGeneric { .. } - | TypeInfo::Custom { .. } - | TypeInfo::Placeholder(..) - | TypeInfo::TraitType { .. } - | TypeInfo::TypeParam(..) - ), - }) + !nested_types + .into_iter() + .any(|x| match numeric_non_concrete { + TreatNumericAs::Abstract => matches!( + x, + TypeInfo::UnknownGeneric { .. } + | TypeInfo::Custom { .. } + | TypeInfo::Placeholder(..) + | TypeInfo::TraitType { .. } + | TypeInfo::TypeParam(..) + | TypeInfo::Numeric + ), + TreatNumericAs::Concrete => matches!( + x, + TypeInfo::UnknownGeneric { .. } + | TypeInfo::Custom { .. } + | TypeInfo::Placeholder(..) + | TypeInfo::TraitType { .. } + | TypeInfo::TypeParam(..) + ), + }) } /// `check_type_parameter_bounds` does two types of checks. Lets use the example below for demonstrating the two checks: diff --git a/sway-core/src/type_system/priv_prelude.rs b/sway-core/src/type_system/priv_prelude.rs index b28ac22152d..c841399c56c 100644 --- a/sway-core/src/type_system/priv_prelude.rs +++ b/sway-core/src/type_system/priv_prelude.rs @@ -16,6 +16,6 @@ pub use super::{ type_parameter::TypeParameter, }, engine::TypeEngine, - id::{IncludeNumeric, IncludeSelf, TypeId}, + id::{IncludeSelf, TreatNumericAs, TypeId}, info::{AbiEncodeSizeHint, AbiName, TypeInfo, TypeSourceInfo}, }; diff --git a/test/Cargo.toml b/test/Cargo.toml index 92479161ca0..9d1d5248d68 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -19,7 +19,10 @@ forc-tracing = { path = "../forc-tracing" } fuel-vm = { workspace = true, features = ["random"] } futures = "0.3.24" gag = "1.0" +glob = "0.3.1" hex = "0.4.3" +insta = "1.39.0" +libtest-mimic = "0.7.3" miden = "0.3.0" prettydiff = "0.6" rand = "0.8" @@ -35,3 +38,9 @@ textwrap = "0.16.0" tokio = "1.12" toml = { version = "0.7", features = ["parse"] } tracing = "0.1" +vte = "0.13.0" + +[[test]] +name = "tests" +path = "tests/tests.rs" +harness = false diff --git a/test/src/e2e_vm_tests/README.md b/test/src/e2e_vm_tests/README.md index fdc8af3fa0a..1f71421bf8a 100644 --- a/test/src/e2e_vm_tests/README.md +++ b/test/src/e2e_vm_tests/README.md @@ -3,6 +3,7 @@ In order to minimize compilation time of individual tests, strive to reduce dependencies in tests. To achieve that, follow these guidelines: + - Use `implicit-std = false` if dependency on `core` is not needed. This is often possible when testing `should_pass/language` features. - If the dependency on `core` is not needed, instead of using the project type `script`, that will, because of the encoding, depend on `core`, try using `library` instead. - Do not use `std` just to conveniently get an arbitrary type or trait. E.g., if a test requires an arbitrary type or trait, go with `struct Dummy {}` or `trait Trait {}` instead of importing `Option` or `Hash`. @@ -87,3 +88,17 @@ SWAY_TEST_VERBOSE=true cargo run [pattern] ``` from the `sway/test` directory. + +# Snapshot tests + +When an "e2e" test has a file named `snapshot.toml` it will run as `cargo insta` snapshot tests. +These tests can be run as normal: `cargo r -p test`, and in two new ways: + +``` +> cargo t -p test +> cargo insta test +``` + +Snapshots can be reviewed using normal "cargo insta" workflow (see [insta.rs](https://insta.rs/)). + +For the moment, there is no configuration for `snapshot.toml`, so they are just empty files. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/snapshot.toml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/snapshot.toml @@ -0,0 +1 @@ + diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw index 3286688e0b9..e0d26feec0b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw @@ -1,7 +1,6 @@ script; fn main() { - // u8 let _a = 0x100; Vec::::new().push(_a); @@ -15,4 +14,20 @@ fn main() { // Array let a = [1, 2, "hello"]; + + // Array - different numerics + let a = [1, 2u8, 3u16, 4u32, 5u64]; + + // Array - unspecified generic structs + let a = [None, Some(1), Some(1u8)]; + let _b: Option = a[1]; + + // Wrong cast + let a = [8, 256u16, 8u8]; + let b: u32 = a[2]; +} + +fn insufficient_type_check(arg: u64) -> [u32;2] { + let res = [1u32, arg]; + res } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap new file mode 100644 index 00000000000..decd4fba792 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/stdout.snap @@ -0,0 +1,180 @@ +--- +source: test/tests/tests.rs +--- +exit status: 1 +stdout: + Building src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors + Compiling library core (sway-lib-core) + Compiling library std (sway-lib-std) + Compiling script type_check_analyze_errors (test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors) + +stderr: +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:23:27 + | +21 | +22 | let a = [None, Some(1), Some(1u8)]; +23 | let _b: Option = a[1]; + | ^^^^ Mismatched types. +expected: Option +found: Option. +help: Variable declaration's type annotation does not match up with the assigned expression's type. +24 | +25 | // Wrong cast + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:27:18 + | +25 | +26 | let a = [8, 256u16, 8u8]; +27 | let b: u32 = a[2]; + | ^^^^ Mismatched types. +expected: u32 +found: u16. +help: Variable declaration's type annotation does not match up with the assigned expression's type. +28 | } + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:4:14 + | +2 | +3 | fn main() { +4 | let _a = 0x100; + | ^^^^^ Literal would overflow because its value does not fit into "u8" +5 | Vec::::new().push(_a); + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:8:14 + | +6 | +7 | // u16 +8 | let _a = 0x10000; + | ^^^^^^^ Literal would overflow because its value does not fit into "u16" +9 | Vec::::new().push(_a); + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:12:14 + | +10 | +11 | // u32 +12 | let _a = 0x100000000; + | ^^^^^^^^^^^ Literal would overflow because its value does not fit into "u32" +13 | Vec::::new().push(_a); + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:16:14 + | +14 | +15 | // Array +16 | let a = [1, 2, "hello"]; + | ^ Mismatched types. +expected: str +found: numeric. + +17 | +18 | // Array - different numerics + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:16:17 + | +14 | +15 | // Array +16 | let a = [1, 2, "hello"]; + | ^ Mismatched types. +expected: str +found: numeric. + +17 | +18 | // Array - different numerics + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:19:22 + | +17 | +18 | // Array - different numerics +19 | let a = [1, 2u8, 3u16, 4u32, 5u64]; + | ^^^^ Mismatched types. +expected: u8 +found: u16. + +20 | +21 | // Array - unspecified generic structs + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:19:28 + | +17 | +18 | // Array - different numerics +19 | let a = [1, 2u8, 3u16, 4u32, 5u64]; + | ^^^^ Mismatched types. +expected: u8 +found: u32. + +20 | +21 | // Array - unspecified generic structs + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:19:34 + | +17 | +18 | // Array - different numerics +19 | let a = [1, 2u8, 3u16, 4u32, 5u64]; + | ^^^^ Mismatched types. +expected: u8 +found: u64. + +20 | +21 | // Array - unspecified generic structs + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:26:25 + | +24 | +25 | // Wrong cast +26 | let a = [8, 256u16, 8u8]; + | ^^^ Mismatched types. +expected: u16 +found: u8. + +27 | let b: u32 = a[2]; +28 | } + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/src/main.sw:31:22 + | +29 | +30 | fn insufficient_type_check(arg: u64) -> [u32;2] { +31 | let res = [1u32, arg]; + | ^^^ Mismatched types. +expected: u32 +found: u64. + +32 | res +33 | } + | +____ + + Aborting due to 12 errors. +error: Failed to compile type_check_analyze_errors diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml deleted file mode 100644 index 65ba3c1072b..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/type_check_analyze_errors/test.toml +++ /dev/null @@ -1,15 +0,0 @@ -category = "fail" - -# check: $()let _a = 0x100; -# nextln: $()Literal would overflow because its value does not fit into "u8" - -# check: $()let _a = 0x10000; -# nextln: $()Literal would overflow because its value does not fit into "u16" - -# check: $()let _a = 0x100000000; -# nextln: $()Literal would overflow because its value does not fit into "u32" - -# check: $()let a = [1, 2, "hello"]; -# nextln: $()Mismatched types -# nextln: $()expected: numeric -# nextln: $()found: str diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index 0c6107aaccf..9de1ab9f2e1 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -31,7 +31,7 @@ impl Checker { /// of the file. /// Example: /// - /// ``` + /// ```sway /// // ::check-ir:: /// // ::check-ir-optimized:: /// // ::check-ir-asm:: @@ -42,7 +42,7 @@ impl Checker { /// Optimized IR checker can be configured with `pass: `. When /// `o1` is chosen, all the configured passes are chosen automatically. /// - /// ``` + /// ```sway /// // ::check-ir-optimized:: /// // pass: o1 /// ``` diff --git a/test/src/main.rs b/test/src/main.rs index 8dff1419ee7..22a6c01c0e1 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -175,5 +175,13 @@ async fn main() -> Result<()> { .await?; } + // Run snapshot tests + let args = vec!["t", "--release", "-p", "test"]; + let mut t = std::process::Command::new("cargo") + .args(args) + .spawn() + .unwrap(); + assert!(t.wait().unwrap().success()); + Ok(()) } diff --git a/test/tests/tests.rs b/test/tests/tests.rs new file mode 100644 index 00000000000..b526159f038 --- /dev/null +++ b/test/tests/tests.rs @@ -0,0 +1,119 @@ +use libtest_mimic::{Arguments, Trial}; +use std::{path::PathBuf, sync::Once}; + +static FORC_COMPILATION: Once = Once::new(); + +fn compile_forc() { + let args = vec!["b", "--release", "-p", "forc"]; + let o = std::process::Command::new("cargo") + .args(args) + .output() + .unwrap(); + assert!(o.status.success()); +} + +pub fn main() { + let mut args = Arguments::from_args(); + args.nocapture = true; + + let tests = discover_test() + .into_iter() + .map(|dir| { + let manifest_dir = "src/e2e_vm_tests/test_programs/"; + let name = dir.to_str().unwrap().to_string().replace(manifest_dir, ""); + Trial::test(name, move || { + FORC_COMPILATION.call_once(|| { + compile_forc(); + }); + + let root = dir.to_str().unwrap(); + + let args = vec!["build", "--path", root]; + let o = std::process::Command::new("../target/release/forc") + .args(args) + .output() + .unwrap(); + + let snapshot = clean_output(&format!( + "exit status: {}\nstdout:\n{}\nstderr:\n{}", + o.status.code().unwrap(), + String::from_utf8(o.stdout).unwrap(), + String::from_utf8(o.stderr).unwrap() + )); + + fn stdout(root: &str, snapshot: &str) { + let mut insta = insta::Settings::new(); + insta.set_snapshot_path(root); + insta.set_prepend_module_to_snapshot(false); + insta.set_omit_expression(true); + let scope = insta.bind_to_scope(); + insta::assert_snapshot!(snapshot); + drop(scope); + } + stdout(&format!("../{root}"), &snapshot); + + Ok(()) + }) + }) + .collect(); + libtest_mimic::run(&args, tests).exit(); +} + +pub fn discover_test() -> Vec { + use glob::glob; + + let mut entries = vec![]; + + for entry in glob("**/snapshot.toml") + .expect("Failed to read glob pattern") + .flatten() + { + entries.push(entry.parent().unwrap().to_owned()) + } + + entries +} + +fn clean_output(output: &str) -> String { + #[derive(Default)] + struct RawText(String); + + impl vte::Perform for RawText { + fn print(&mut self, c: char) { + self.0.push(c); + } + + fn execute(&mut self, _: u8) {} + + fn hook(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} + + fn put(&mut self, b: u8) { + self.0.push(b as char); + } + + fn unhook(&mut self) {} + + fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {} + + fn csi_dispatch(&mut self, _: &vte::Params, _: &[u8], _: bool, _: char) {} + + fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {} + } + + let mut raw = String::new(); + for line in output.lines() { + let mut performer = RawText::default(); + let mut p = vte::Parser::new(); + for b in line.as_bytes() { + p.advance(&mut performer, *b); + } + raw.push_str(&performer.0); + raw.push('\n'); + } + + // Remove absolute paths from snapshot tests + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let manifest_dir: PathBuf = PathBuf::from(manifest_dir); + let parent = manifest_dir.parent().unwrap(); + raw.replace(&format!("{}/", parent.display()), "") +}