just playing with tangled
at ig/vimdiffwarn 566 lines 15 kB view raw
1// Copyright 2022 The Jujutsu Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15use std::path::Path; 16 17use indoc::indoc; 18 19use crate::common::CommandOutput; 20use crate::common::TestEnvironment; 21 22#[test] 23fn test_templater_parse_error() { 24 let test_env = TestEnvironment::default(); 25 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 26 let repo_path = test_env.env_root().join("repo"); 27 let render = |template| get_template_output(&test_env, &repo_path, "@-", template); 28 29 insta::assert_snapshot!(render(r#"description ()"#), @r" 30 ------- stderr ------- 31 Error: Failed to parse template: Syntax error 32 Caused by: --> 1:13 33 | 34 1 | description () 35 | ^--- 36 | 37 = expected <EOI>, `++`, `||`, `&&`, `==`, `!=`, `>=`, `>`, `<=`, or `<` 38 [EOF] 39 [exit status: 1] 40 "); 41 42 // Typo 43 test_env.add_config( 44 r###" 45 [template-aliases] 46 'conflicting' = '' 47 'shorted()' = '' 48 'socat(x)' = 'x' 49 'format_id(id)' = 'id.sort()' 50 "###, 51 ); 52 insta::assert_snapshot!(render(r#"conflicts"#), @r" 53 ------- stderr ------- 54 Error: Failed to parse template: Keyword `conflicts` doesn't exist 55 Caused by: --> 1:1 56 | 57 1 | conflicts 58 | ^-------^ 59 | 60 = Keyword `conflicts` doesn't exist 61 Hint: Did you mean `conflict`, `conflicting`? 62 [EOF] 63 [exit status: 1] 64 "); 65 insta::assert_snapshot!(render(r#"commit_id.shorter()"#), @r" 66 ------- stderr ------- 67 Error: Failed to parse template: Method `shorter` doesn't exist for type `CommitOrChangeId` 68 Caused by: --> 1:11 69 | 70 1 | commit_id.shorter() 71 | ^-----^ 72 | 73 = Method `shorter` doesn't exist for type `CommitOrChangeId` 74 Hint: Did you mean `short`, `shortest`? 75 [EOF] 76 [exit status: 1] 77 "); 78 insta::assert_snapshot!(render(r#"oncat()"#), @r" 79 ------- stderr ------- 80 Error: Failed to parse template: Function `oncat` doesn't exist 81 Caused by: --> 1:1 82 | 83 1 | oncat() 84 | ^---^ 85 | 86 = Function `oncat` doesn't exist 87 Hint: Did you mean `concat`, `socat`? 88 [EOF] 89 [exit status: 1] 90 "); 91 insta::assert_snapshot!(render(r#""".lines().map(|s| se)"#), @r#" 92 ------- stderr ------- 93 Error: Failed to parse template: Keyword `se` doesn't exist 94 Caused by: --> 1:20 95 | 96 1 | "".lines().map(|s| se) 97 | ^^ 98 | 99 = Keyword `se` doesn't exist 100 Hint: Did you mean `s`, `self`? 101 [EOF] 102 [exit status: 1] 103 "#); 104 insta::assert_snapshot!(render(r#"format_id(commit_id)"#), @r" 105 ------- stderr ------- 106 Error: Failed to parse template: In alias `format_id(id)` 107 Caused by: 108 1: --> 1:1 109 | 110 1 | format_id(commit_id) 111 | ^------------------^ 112 | 113 = In alias `format_id(id)` 114 2: --> 1:4 115 | 116 1 | id.sort() 117 | ^--^ 118 | 119 = Method `sort` doesn't exist for type `CommitOrChangeId` 120 Hint: Did you mean `short`, `shortest`? 121 [EOF] 122 [exit status: 1] 123 "); 124 125 // "at least N arguments" 126 insta::assert_snapshot!(render("separate()"), @r" 127 ------- stderr ------- 128 Error: Failed to parse template: Function `separate`: Expected at least 1 arguments 129 Caused by: --> 1:10 130 | 131 1 | separate() 132 | ^ 133 | 134 = Function `separate`: Expected at least 1 arguments 135 [EOF] 136 [exit status: 1] 137 "); 138 139 // -Tbuiltin shows the predefined builtin_* aliases. This isn't 100% 140 // guaranteed, but is nice. 141 insta::assert_snapshot!(render(r#"builtin"#), @r" 142 ------- stderr ------- 143 Error: Failed to parse template: Keyword `builtin` doesn't exist 144 Caused by: --> 1:1 145 | 146 1 | builtin 147 | ^-----^ 148 | 149 = Keyword `builtin` doesn't exist 150 Hint: Did you mean `builtin_config_list`, `builtin_config_list_detailed`, `builtin_draft_commit_description`, `builtin_log_comfortable`, `builtin_log_compact`, `builtin_log_compact_full_description`, `builtin_log_detailed`, `builtin_log_node`, `builtin_log_node_ascii`, `builtin_log_oneline`, `builtin_op_log_comfortable`, `builtin_op_log_compact`, `builtin_op_log_node`, `builtin_op_log_node_ascii`, `builtin_op_log_oneline`? 151 [EOF] 152 [exit status: 1] 153 "); 154} 155 156#[test] 157fn test_template_parse_warning() { 158 let test_env = TestEnvironment::default(); 159 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 160 let repo_path = test_env.env_root().join("repo"); 161 162 let template = indoc! {r#" 163 separate(' ', 164 author.username(), 165 ) 166 "#}; 167 let output = test_env.run_jj_in(&repo_path, ["log", "-r@", "-T", template]); 168 insta::assert_snapshot!(output, @r" 169 @ test.user 170171 ~ 172 [EOF] 173 ------- stderr ------- 174 Warning: In template expression 175 --> 2:10 176 | 177 2 | author.username(), 178 | ^------^ 179 | 180 = username() is deprecated; use email().local() instead 181 [EOF] 182 "); 183} 184 185#[test] 186fn test_templater_upper_lower() { 187 let test_env = TestEnvironment::default(); 188 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 189 let repo_path = test_env.env_root().join("repo"); 190 let render = |template| get_colored_template_output(&test_env, &repo_path, "@-", template); 191 192 insta::assert_snapshot!( 193 render(r#"change_id.shortest(4).upper() ++ change_id.shortest(4).upper().lower()"#), 194 @"ZZZZzzzz[EOF]"); 195 insta::assert_snapshot!( 196 render(r#""Hello".upper() ++ "Hello".lower()"#), @"HELLOhello[EOF]"); 197} 198 199#[test] 200fn test_templater_alias() { 201 let test_env = TestEnvironment::default(); 202 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 203 let repo_path = test_env.env_root().join("repo"); 204 let render = |template| get_template_output(&test_env, &repo_path, "@-", template); 205 206 test_env.add_config( 207 r###" 208 [template-aliases] 209 'my_commit_id' = 'commit_id.short()' 210 'syntax_error' = 'foo.' 211 'name_error' = 'unknown_id' 212 'recurse' = 'recurse1' 213 'recurse1' = 'recurse2()' 214 'recurse2()' = 'recurse' 215 'identity(x)' = 'x' 216 'coalesce(x, y)' = 'if(x, x, y)' 217 'deprecated()' = 'author.username()' 218 'builtin_log_node' = '"#"' 219 'builtin_op_log_node' = '"#"' 220 "###, 221 ); 222 223 insta::assert_snapshot!(render("my_commit_id"), @"000000000000[EOF]"); 224 insta::assert_snapshot!(render("identity(my_commit_id)"), @"000000000000[EOF]"); 225 226 insta::assert_snapshot!(render("commit_id ++ syntax_error"), @r" 227 ------- stderr ------- 228 Error: Failed to parse template: In alias `syntax_error` 229 Caused by: 230 1: --> 1:14 231 | 232 1 | commit_id ++ syntax_error 233 | ^----------^ 234 | 235 = In alias `syntax_error` 236 2: --> 1:5 237 | 238 1 | foo. 239 | ^--- 240 | 241 = expected <identifier> 242 [EOF] 243 [exit status: 1] 244 "); 245 246 insta::assert_snapshot!(render("commit_id ++ name_error"), @r" 247 ------- stderr ------- 248 Error: Failed to parse template: In alias `name_error` 249 Caused by: 250 1: --> 1:14 251 | 252 1 | commit_id ++ name_error 253 | ^--------^ 254 | 255 = In alias `name_error` 256 2: --> 1:1 257 | 258 1 | unknown_id 259 | ^--------^ 260 | 261 = Keyword `unknown_id` doesn't exist 262 [EOF] 263 [exit status: 1] 264 "); 265 266 insta::assert_snapshot!(render(r#"identity(identity(commit_id.short("")))"#), @r#" 267 ------- stderr ------- 268 Error: Failed to parse template: In alias `identity(x)` 269 Caused by: 270 1: --> 1:1 271 | 272 1 | identity(identity(commit_id.short(""))) 273 | ^-------------------------------------^ 274 | 275 = In alias `identity(x)` 276 2: --> 1:1 277 | 278 1 | x 279 | ^ 280 | 281 = In function parameter `x` 282 3: --> 1:10 283 | 284 1 | identity(identity(commit_id.short(""))) 285 | ^---------------------------^ 286 | 287 = In alias `identity(x)` 288 4: --> 1:1 289 | 290 1 | x 291 | ^ 292 | 293 = In function parameter `x` 294 5: --> 1:35 295 | 296 1 | identity(identity(commit_id.short(""))) 297 | ^^ 298 | 299 = Expected expression of type `Integer`, but actual type is `String` 300 [EOF] 301 [exit status: 1] 302 "#); 303 304 insta::assert_snapshot!(render("commit_id ++ recurse"), @r" 305 ------- stderr ------- 306 Error: Failed to parse template: In alias `recurse` 307 Caused by: 308 1: --> 1:14 309 | 310 1 | commit_id ++ recurse 311 | ^-----^ 312 | 313 = In alias `recurse` 314 2: --> 1:1 315 | 316 1 | recurse1 317 | ^------^ 318 | 319 = In alias `recurse1` 320 3: --> 1:1 321 | 322 1 | recurse2() 323 | ^--------^ 324 | 325 = In alias `recurse2()` 326 4: --> 1:1 327 | 328 1 | recurse 329 | ^-----^ 330 | 331 = Alias `recurse` expanded recursively 332 [EOF] 333 [exit status: 1] 334 "); 335 336 insta::assert_snapshot!(render("identity()"), @r" 337 ------- stderr ------- 338 Error: Failed to parse template: Function `identity`: Expected 1 arguments 339 Caused by: --> 1:10 340 | 341 1 | identity() 342 | ^ 343 | 344 = Function `identity`: Expected 1 arguments 345 [EOF] 346 [exit status: 1] 347 "); 348 insta::assert_snapshot!(render("identity(commit_id, commit_id)"), @r" 349 ------- stderr ------- 350 Error: Failed to parse template: Function `identity`: Expected 1 arguments 351 Caused by: --> 1:10 352 | 353 1 | identity(commit_id, commit_id) 354 | ^------------------^ 355 | 356 = Function `identity`: Expected 1 arguments 357 [EOF] 358 [exit status: 1] 359 "); 360 361 insta::assert_snapshot!(render(r#"coalesce(label("x", "not boolean"), "")"#), @r#" 362 ------- stderr ------- 363 Error: Failed to parse template: In alias `coalesce(x, y)` 364 Caused by: 365 1: --> 1:1 366 | 367 1 | coalesce(label("x", "not boolean"), "") 368 | ^-------------------------------------^ 369 | 370 = In alias `coalesce(x, y)` 371 2: --> 1:4 372 | 373 1 | if(x, x, y) 374 | ^ 375 | 376 = In function parameter `x` 377 3: --> 1:10 378 | 379 1 | coalesce(label("x", "not boolean"), "") 380 | ^-----------------------^ 381 | 382 = Expected expression of type `Boolean`, but actual type is `Template` 383 [EOF] 384 [exit status: 1] 385 "#); 386 387 insta::assert_snapshot!(render("(-my_commit_id)"), @r" 388 ------- stderr ------- 389 Error: Failed to parse template: In alias `my_commit_id` 390 Caused by: 391 1: --> 1:3 392 | 393 1 | (-my_commit_id) 394 | ^----------^ 395 | 396 = In alias `my_commit_id` 397 2: --> 1:1 398 | 399 1 | commit_id.short() 400 | ^---------------^ 401 | 402 = Expected expression of type `Integer`, but actual type is `String` 403 [EOF] 404 [exit status: 1] 405 "); 406 407 let output = test_env.run_jj_in(&repo_path, ["log", "-r@", "-Tdeprecated()"]); 408 insta::assert_snapshot!(output, @r" 409 # test.user 410411 ~ 412 [EOF] 413 ------- stderr ------- 414 Warning: In template expression 415 --> 1:1 416 | 417 1 | deprecated() 418 | ^----------^ 419 | 420 = In alias `deprecated()` 421 --> 1:8 422 | 423 1 | author.username() 424 | ^------^ 425 | 426 = username() is deprecated; use email().local() instead 427 [EOF] 428 "); 429} 430 431#[test] 432fn test_templater_alias_override() { 433 let test_env = TestEnvironment::default(); 434 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 435 let repo_path = test_env.env_root().join("repo"); 436 437 test_env.add_config( 438 r#" 439 [template-aliases] 440 'f(x)' = '"user"' 441 "#, 442 ); 443 444 // 'f(x)' should be overridden by --config 'f(a)'. If aliases were sorted 445 // purely by name, 'f(a)' would come first. 446 let output = test_env.run_jj_in( 447 &repo_path, 448 [ 449 "log", 450 "--no-graph", 451 "-r@", 452 "-T", 453 r#"f(_)"#, 454 r#"--config=template-aliases.'f(a)'='"arg"'"#, 455 ], 456 ); 457 insta::assert_snapshot!(output, @"arg[EOF]"); 458} 459 460#[test] 461fn test_templater_bad_alias_decl() { 462 let test_env = TestEnvironment::default(); 463 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 464 let repo_path = test_env.env_root().join("repo"); 465 466 test_env.add_config( 467 r###" 468 [template-aliases] 469 'badfn(a, a)' = 'a' 470 'my_commit_id' = 'commit_id.short()' 471 "###, 472 ); 473 474 // Invalid declaration should be warned and ignored. 475 let output = test_env.run_jj_in(&repo_path, ["log", "--no-graph", "-r@-", "-Tmy_commit_id"]); 476 insta::assert_snapshot!(output, @r" 477 000000000000[EOF] 478 ------- stderr ------- 479 Warning: Failed to load `template-aliases.badfn(a, a)`: --> 1:7 480 | 481 1 | badfn(a, a) 482 | ^--^ 483 | 484 = Redefinition of function parameter 485 [EOF] 486 "); 487} 488 489#[test] 490fn test_templater_config_function() { 491 let test_env = TestEnvironment::default(); 492 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 493 let repo_path = test_env.env_root().join("repo"); 494 let render = |template| get_template_output(&test_env, &repo_path, "@-", template); 495 496 insta::assert_snapshot!( 497 render("config('user.name')"), 498 @r#""Test User"[EOF]"#); 499 insta::assert_snapshot!( 500 render("config('user')"), 501 @r#"{ email = "test.user@example.com", name = "Test User" }[EOF]"#); 502 insta::assert_snapshot!(render("config('invalid name')"), @r" 503 ------- stderr ------- 504 Error: Failed to parse template: Failed to parse config name 505 Caused by: 506 1: --> 1:8 507 | 508 1 | config('invalid name') 509 | ^------------^ 510 | 511 = Failed to parse config name 512 2: TOML parse error at line 1, column 9 513 | 514 1 | invalid name 515 | ^ 516 517 518 [EOF] 519 [exit status: 1] 520 "); 521 insta::assert_snapshot!(render("config('unknown')"), @r" 522 ------- stderr ------- 523 Error: Failed to parse template: Failed to get config value 524 Caused by: 525 1: --> 1:1 526 | 527 1 | config('unknown') 528 | ^----^ 529 | 530 = Failed to get config value 531 2: Value not found for unknown 532 [EOF] 533 [exit status: 1] 534 "); 535} 536 537#[must_use] 538fn get_template_output( 539 test_env: &TestEnvironment, 540 repo_path: &Path, 541 rev: &str, 542 template: &str, 543) -> CommandOutput { 544 test_env.run_jj_in(repo_path, ["log", "--no-graph", "-r", rev, "-T", template]) 545} 546 547#[must_use] 548fn get_colored_template_output( 549 test_env: &TestEnvironment, 550 repo_path: &Path, 551 rev: &str, 552 template: &str, 553) -> CommandOutput { 554 test_env.run_jj_in( 555 repo_path, 556 [ 557 "log", 558 "--color=always", 559 "--no-graph", 560 "-r", 561 rev, 562 "-T", 563 template, 564 ], 565 ) 566}