#[macro_use] extern crate pretty_assertions; #[macro_use] extern crate indoc; extern crate bumpalo; extern crate roc_reporting; mod helpers; #[cfg(test)] mod test_reporting { use crate::helpers::test_home; use roc_module::symbol::{Interns, ModuleId}; use roc_reporting::report::{ can_problem, em_text, plain_text, region_slice, url, Report, ReportText, BLUE_CODE, BOLD_CODE, CYAN_CODE, GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, TEST_PALETTE, UNDERLINE_CODE, WHITE_CODE, YELLOW_CODE, }; use roc_types::pretty_print::name_all_type_vars; use roc_types::subs::Subs; use roc_types::types; use std::path::PathBuf; // use roc_region::all; use crate::helpers::{can_expr, infer_expr, CanExprOut}; use roc_reporting::report::ReportText::{Batch, Module, Region, Type, Value}; use roc_types::subs::Content::{FlexVar, RigidVar, Structure}; use roc_types::subs::FlatType::EmptyRecord; fn filename_from_string(str: &str) -> PathBuf { let mut filename = PathBuf::new(); filename.push(str); return filename; } // use roc_problem::can; fn to_simple_report(text: ReportText) -> Report { Report { text: text, filename: filename_from_string(r"\code\proj\Main.roc"), } } fn infer_expr_help( expr_src: &str, ) -> ( Vec, Vec, Subs, ModuleId, Interns, ) { let CanExprOut { output, var_store, var, constraint, home, interns, problems: can_problems, .. } = can_expr(expr_src); let mut subs = Subs::new(var_store.into()); for (var, name) in output.introduced_variables.name_by_var { subs.rigid_var(var, name); } let mut unify_problems = Vec::new(); let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); name_all_type_vars(var, &mut subs); (unify_problems, can_problems, subs, home, interns) } fn report_renders_as_from_src(src: &str, report: Report, expected_rendering: &str) { let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src); let mut buf: String = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); report .text .render_ci(&mut buf, &mut subs, home, &src_lines, &interns); assert_eq!(buf, expected_rendering); } fn report_problem_as(src: &str, expected_rendering: &str) { let (_type_problems, can_problems, mut subs, home, interns) = infer_expr_help(src); let mut buf: String = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); match can_problems.first() { None => {} Some(problem) => { let report = can_problem( filename_from_string(r"\code\proj\Main.roc"), src, problem.clone(), ); report .text .render_ci(&mut buf, &mut subs, home, &src_lines, &interns) } } assert_eq!(buf, expected_rendering); } fn human_readable(str: &str) -> String { return str .replace(RED_CODE, "") .replace(WHITE_CODE, "") .replace(BLUE_CODE, "") .replace(YELLOW_CODE, "") .replace(GREEN_CODE, "") .replace(CYAN_CODE, "") .replace(MAGENTA_CODE, "") .replace(RESET_CODE, "") .replace(BOLD_CODE, "") .replace(UNDERLINE_CODE, ""); } fn report_renders_in_color_from_src(src: &str, report: Report, expected_rendering: &str) { let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src); let mut buf: String = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); report.text.render_color_terminal( &mut buf, &mut subs, home, &src_lines, &interns, &TEST_PALETTE, ); assert_eq!(human_readable(&buf), expected_rendering); } fn report_renders_in_color(report: Report, expected_rendering: &str) { report_renders_in_color_from_src( indoc!( r#" x = 1 y = 2 x "# ), report, expected_rendering, ) } fn report_renders_as(report: Report, expected_rendering: &str) { report_renders_as_from_src( indoc!( r#" x = 1 y = 2 x "# ), report, expected_rendering, ) } #[test] fn report_plain() { report_renders_as(to_simple_report(plain_text("y")), "y"); } #[test] fn report_emphasized_text() { report_renders_as(to_simple_report(em_text("y")), "*y*"); } #[test] fn report_url() { report_renders_as( to_simple_report(url("package.roc.org")), "", ); } #[test] fn report_symbol() { let src: &str = indoc!( r#" x = 1 y = 2 x "# ); let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src); let mut buf = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); to_simple_report(Value(interns.symbol(test_home(), "x".into()))) .text .render_ci(&mut buf, &mut subs, home, &src_lines, &interns); assert_eq!(buf, "x"); } #[test] fn report_module() { let src: &str = indoc!( r#" x = 1 y = 2 x "# ); let (_type_problems, _can_problems, mut subs, home, mut interns) = infer_expr_help(src); let mut buf = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); let module_id = interns.module_id(&"Main".into()); to_simple_report(Module(module_id)) .text .render_ci(&mut buf, &mut subs, home, &src_lines, &interns); assert_eq!(buf, "Main"); } #[test] fn report_wildcard() { report_renders_as(to_simple_report(Type(FlexVar(None))), "*"); } #[test] fn report_flex_var() { report_renders_as(to_simple_report(Type(FlexVar(Some("msg".into())))), "msg"); } #[test] fn report_rigid_var() { report_renders_as(to_simple_report(Type(RigidVar("Str".into()))), "Str"); } #[test] fn report_empty_record() { report_renders_as(to_simple_report(Type(Structure(EmptyRecord))), "{}"); } #[test] fn report_batch_of_plain_text() { let mut report_texts = Vec::new(); report_texts.push(plain_text("Wait a second. ")); report_texts.push(plain_text("There is a problem here. -> ")); report_texts.push(em_text("y")); report_renders_as( to_simple_report(Batch(report_texts)), "Wait a second. There is a problem here. -> *y*", ); } #[test] fn report_unused_def() { report_problem_as( indoc!( r#" x = 1 y = 2 x "# ), indoc!( r#" y is not used anywhere in your code. 2 ┆ y = 2 ┆ ^ If you didn't intend on using y then remove it so future readers of your code don't wonder why it is there."# ), ) } // hits a TODO in reporting // #[test] // fn report_shadow() { // report_problem_as( // indoc!( // r#" // i = 1 // // s = \i -> // i + 1 // // s i // "# // ), // indoc!(r#" "#), // ) // } // #[test] // fn report_unsupported_top_level_def() { // report_problem_as( // indoc!( // r#" // x = 1 // // 5 = 2 + 1 // // x // "# // ), // indoc!(r#" "#), // ) // } #[test] fn report_precedence_problem_single_line() { report_problem_as( indoc!( r#"x = 1 y = if selectedId != thisId == adminsId then 4 else 5 { x, y } "# ), indoc!( r#" You cannot mix (!=) and (==) without parentheses 3 ┆ if selectedId != thisId == adminsId then ┆ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "# ), ) } #[test] fn report_precedence_problem_multiline() { report_problem_as( indoc!( r#" if 1 != 2 == 3 then 2 else 3 "# ), indoc!( r#" You cannot mix (!=) and (==) without parentheses 2 ┆> 1 3 ┆> != 2 4 ┆> == 3 "# ), ) } // #[test] // fn report_unused_argument() { // report_problem_as( // indoc!(r#" // y = 9 // // box = \class, htmlChildren -> // div [ class ] [] // // div = 4 // // box "wizard" [] // "#), // indoc!( // r#" // box doesn't use htmlChildren. // // 3 ┆ box = \class, htmlChildren -> // // If you don't need htmlChildren, then you can just remove it. However, if you really do need htmlChildren as an argument of box, prefix it with an underscore, like this: "_htmlChildren". Adding an underscore at the start of a variable name is a way of saying that the variable is not used."# // ), // ); // } // #[test] // fn report_unused_import() { // report_problem_as( // indoc!(r#" // interface Report // exposes [ // plainText, // emText // ] // imports [ // Symbol.{ Interns } // ] // // plainText = \str -> PlainText str // // emText = \str -> EmText str // "#), // indoc!( // r#" // Nothing from Symbol is used in this module. // // 6 ┆ imports [ // 7 ┆ Symbol.{ Interns } // ┆ ^^^^^^ // 8 ┆ ] // // Since Symbol isn't used, you don't need to import it."# // ), // ); // } #[test] fn report_plain_text_color() { report_renders_in_color(to_simple_report(plain_text("y")), "y"); } #[test] fn report_em_text_color() { report_renders_in_color(to_simple_report(em_text("HELLO!")), "HELLO!"); } #[test] fn report_url_color() { report_renders_in_color( to_simple_report(url("www.roc.com/blog")), "www.roc.com/blog", ); } #[test] fn report_value_color() { let src: &str = indoc!( r#" activityIndicatorLarge = div view activityIndicatorLarge "# ); let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src); let mut buf = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); to_simple_report(Value( interns.symbol(test_home(), "activityIndicatorLarge".into()), )) .text .render_color_terminal( &mut buf, &mut subs, home, &src_lines, &interns, &TEST_PALETTE, ); assert_eq!(human_readable(&buf), "activityIndicatorLarge"); } #[test] fn report_module_color() { let src: &str = indoc!( r#" x = 1 y = 2 x "# ); let (_type_problems, _can_problems, mut subs, home, mut interns) = infer_expr_help(src); let mut buf = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); let module_id = interns.module_id(&"Util.Int".into()); to_simple_report(Module(module_id)) .text .render_color_terminal( &mut buf, &mut subs, home, &src_lines, &interns, &TEST_PALETTE, ); assert_eq!(human_readable(&buf), "Util.Int"); } #[test] fn report_wildcard_in_color() { report_renders_in_color(to_simple_report(Type(FlexVar(None))), "*"); } #[test] fn report_flex_var_in_color() { report_renders_in_color( to_simple_report(Type(FlexVar(Some("msg".into())))), "msg", ); } #[test] fn report_rigid_var_in_color() { report_renders_in_color( to_simple_report(Type(RigidVar("Str".into()))), "Str", ); } #[test] fn report_empty_record_in_color() { report_renders_in_color( to_simple_report(Type(Structure(EmptyRecord))), "{}", ); } #[test] fn report_batch_in_color() { let mut report_texts = Vec::new(); report_texts.push(Type(RigidVar("List".into()))); report_texts.push(plain_text(" ")); report_texts.push(Type(Structure(EmptyRecord))); report_renders_in_color( to_simple_report(Batch(report_texts)), "List {}", ); } #[test] fn report_region_in_color() { report_renders_in_color_from_src( indoc!( r#" isDisabled = \user -> user.isAdmin theAdmin |> isDisabled "# ), to_simple_report(Region(roc_region::all::Region { start_line: 0, end_line: 3, start_col: 0, end_col: 0, })), indoc!( r#" 1> isDisabled = \user -> user.isAdmin 2> 3> theAdmin 4> |> isDisabled "# ), ); } #[test] fn report_region() { report_renders_as_from_src( indoc!( r#" x = 1 y = 2 f = \a -> a + 4 f x "# ), to_simple_report(Region(roc_region::all::Region { start_line: 1, end_line: 3, start_col: 0, end_col: 0, })), indoc!( r#" 2 ┆> y = 2 3 ┆> f = \a -> a + 4 4 ┆> "# ), ); } #[test] fn report_region_different_line_number_lengths() { report_renders_as_from_src( indoc!( r#" x = 1 y = 2 f = \a -> a + 4 f x "# ), to_simple_report(Region(roc_region::all::Region { start_line: 8, end_line: 10, start_col: 0, end_col: 0, })), indoc!( r#" 9 ┆> 10 ┆> y = 2 11 ┆> f = \a -> a + 4 "# ), ); } #[test] fn region_slice_test() { use roc_region::all::Region; assert_eq!( { let region = Region { start_line: 0, end_line: 0, start_col: 0, end_col: 0, }; region_slice(region, "The quick brown fox jumps over the lazy dog") }, "" ); assert_eq!( { let region = Region { start_line: 0, end_line: 0, start_col: 4, end_col: 9, }; region_slice(region, "The quick brown fox jumps over the lazy dog") }, "quick" ); assert_eq!( { let region = Region { start_line: 0, end_line: 0, start_col: 4, end_col: 9, }; region_slice(region, "The\nquick brown fox jumps over the lazy dog") }, "quick" ); assert_eq!( { let region = Region { start_line: 1, end_line: 1, start_col: 0, end_col: 5, }; region_slice( region, indoc!( r#" The quick brown fox jumps over the lazy dog "# ), ) }, "quick" ); assert_eq!( { let region = Region { start_line: 2, end_line: 2, start_col: 0, end_col: 5, }; region_slice( region, indoc!( r#" The quick brown fox jumps over the lazy dog "# ), ) }, "quick" ); assert_eq!( { let region = Region { start_line: 2, end_line: 2, start_col: 0, end_col: 5, }; region_slice( region, indoc!( r#" The quick brown fox jumps over the lazy dog "# ), ) }, "quick" ); } }