just playing with tangled
at diffedit3 678 lines 20 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 insta::assert_snapshot; 16use itertools::Itertools; 17use regex::Regex; 18 19use crate::common::TestEnvironment; 20 21#[test] 22fn test_config_list_single() { 23 let test_env = TestEnvironment::default(); 24 test_env.add_config( 25 r#" 26 [test-table] 27 somekey = "some value" 28 "#, 29 ); 30 31 let stdout = test_env.jj_cmd_success( 32 test_env.env_root(), 33 &["config", "list", "test-table.somekey"], 34 ); 35 insta::assert_snapshot!(stdout, @r###" 36 test-table.somekey="some value" 37 "###); 38 39 let stdout = test_env.jj_cmd_success( 40 test_env.env_root(), 41 &["config", "list", r#"-Tname ++ "\n""#, "test-table.somekey"], 42 ); 43 insta::assert_snapshot!(stdout, @r###" 44 test-table.somekey 45 "###); 46} 47 48#[test] 49fn test_config_list_nonexistent() { 50 let test_env = TestEnvironment::default(); 51 let (stdout, stderr) = test_env.jj_cmd_ok( 52 test_env.env_root(), 53 &["config", "list", "nonexistent-test-key"], 54 ); 55 insta::assert_snapshot!(stdout, @""); 56 insta::assert_snapshot!(stderr, @r###" 57 Warning: No matching config key for nonexistent-test-key 58 "###); 59} 60 61#[test] 62fn test_config_list_table() { 63 let test_env = TestEnvironment::default(); 64 test_env.add_config( 65 r#" 66 [test-table] 67 x = true 68 y.foo = "abc" 69 y.bar = 123 70 "#, 71 ); 72 let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list", "test-table"]); 73 insta::assert_snapshot!( 74 stdout, 75 @r###" 76 test-table.x=true 77 test-table.y.bar=123 78 test-table.y.foo="abc" 79 "###); 80} 81 82#[test] 83fn test_config_list_array() { 84 let test_env = TestEnvironment::default(); 85 test_env.add_config( 86 r#" 87 test-array = [1, "b", 3.4] 88 "#, 89 ); 90 let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list", "test-array"]); 91 insta::assert_snapshot!(stdout, @r###" 92 test-array=[1, "b", 3.4] 93 "###); 94} 95 96#[test] 97fn test_config_list_inline_table() { 98 let test_env = TestEnvironment::default(); 99 test_env.add_config( 100 r#" 101 [[test-table]] 102 x = 1 103 [[test-table]] 104 y = ["z"] 105 "#, 106 ); 107 let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list", "test-table"]); 108 insta::assert_snapshot!(stdout, @r###" 109 test-table=[{x=1}, {y=["z"]}] 110 "###); 111} 112 113#[test] 114fn test_config_list_all() { 115 let test_env = TestEnvironment::default(); 116 test_env.add_config( 117 r#" 118 test-val = [1, 2, 3] 119 [test-table] 120 x = true 121 y.foo = "abc" 122 y.bar = 123 123 "#, 124 ); 125 126 let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "list"]); 127 insta::assert_snapshot!( 128 find_stdout_lines(r"(test-val|test-table\b[^=]*)", &stdout), 129 @r###" 130 test-table.x=true 131 test-table.y.bar=123 132 test-table.y.foo="abc" 133 test-val=[1, 2, 3] 134 "###); 135} 136 137#[test] 138fn test_config_list_layer() { 139 let mut test_env = TestEnvironment::default(); 140 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 141 let user_config_path = test_env.config_path().join("config.toml"); 142 test_env.set_config_path(user_config_path.to_owned()); 143 let repo_path = test_env.env_root().join("repo"); 144 145 // User 146 test_env.jj_cmd_ok( 147 &repo_path, 148 &["config", "set", "--user", "test-key", "test-val"], 149 ); 150 151 test_env.jj_cmd_ok( 152 &repo_path, 153 &[ 154 "config", 155 "set", 156 "--user", 157 "test-layered-key", 158 "test-original-val", 159 ], 160 ); 161 162 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", "--user"]); 163 insta::assert_snapshot!(stdout, @r###" 164 test-key="test-val" 165 test-layered-key="test-original-val" 166 "###); 167 168 // Repo 169 test_env.jj_cmd_ok( 170 &repo_path, 171 &[ 172 "config", 173 "set", 174 "--repo", 175 "test-layered-key", 176 "test-layered-val", 177 ], 178 ); 179 180 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", "--user"]); 181 insta::assert_snapshot!(stdout, @r###" 182 test-key="test-val" 183 "###); 184 185 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", "--repo"]); 186 insta::assert_snapshot!(stdout, @r###" 187 test-layered-key="test-layered-val" 188 "###); 189} 190 191#[test] 192fn test_config_layer_override_default() { 193 let test_env = TestEnvironment::default(); 194 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 195 let repo_path = test_env.env_root().join("repo"); 196 let config_key = "merge-tools.vimdiff.program"; 197 198 // Default 199 let stdout = test_env.jj_cmd_success( 200 &repo_path, 201 &["config", "list", config_key, "--include-defaults"], 202 ); 203 insta::assert_snapshot!(stdout, @r###" 204 merge-tools.vimdiff.program="vim" 205 "###); 206 207 // User 208 test_env.add_config(&format!("{config_key} = {value:?}\n", value = "user")); 209 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", config_key]); 210 insta::assert_snapshot!(stdout, @r###" 211 merge-tools.vimdiff.program="user" 212 "###); 213 214 // Repo 215 std::fs::write( 216 repo_path.join(".jj/repo/config.toml"), 217 format!("{config_key} = {value:?}\n", value = "repo"), 218 ) 219 .unwrap(); 220 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", config_key]); 221 insta::assert_snapshot!(stdout, @r###" 222 merge-tools.vimdiff.program="repo" 223 "###); 224 225 // Command argument 226 let stdout = test_env.jj_cmd_success( 227 &repo_path, 228 &[ 229 "config", 230 "list", 231 config_key, 232 "--config-toml", 233 &format!("{config_key}={value:?}", value = "command-arg"), 234 ], 235 ); 236 insta::assert_snapshot!(stdout, @r###" 237 merge-tools.vimdiff.program="command-arg" 238 "###); 239 240 // Allow printing overridden values 241 let stdout = test_env.jj_cmd_success( 242 &repo_path, 243 &[ 244 "config", 245 "list", 246 config_key, 247 "--include-overridden", 248 "--config-toml", 249 &format!("{config_key}={value:?}", value = "command-arg"), 250 ], 251 ); 252 insta::assert_snapshot!(stdout, @r###" 253 # merge-tools.vimdiff.program="user" 254 # merge-tools.vimdiff.program="repo" 255 merge-tools.vimdiff.program="command-arg" 256 "###); 257 258 let stdout = test_env.jj_cmd_success( 259 &repo_path, 260 &[ 261 "config", 262 "list", 263 "--color=always", 264 config_key, 265 "--include-overridden", 266 ], 267 ); 268 insta::assert_snapshot!(stdout, @r###" 269 # merge-tools.vimdiff.program="user" 270 merge-tools.vimdiff.program="repo" 271 "###); 272} 273 274#[test] 275fn test_config_layer_override_env() { 276 let mut test_env = TestEnvironment::default(); 277 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 278 let repo_path = test_env.env_root().join("repo"); 279 let config_key = "ui.editor"; 280 281 // Environment base 282 test_env.add_env_var("EDITOR", "env-base"); 283 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", config_key]); 284 insta::assert_snapshot!(stdout, @r###" 285 ui.editor="env-base" 286 "###); 287 288 // User 289 test_env.add_config(&format!("{config_key} = {value:?}\n", value = "user")); 290 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", config_key]); 291 insta::assert_snapshot!(stdout, @r###" 292 ui.editor="user" 293 "###); 294 295 // Repo 296 std::fs::write( 297 repo_path.join(".jj/repo/config.toml"), 298 format!("{config_key} = {value:?}\n", value = "repo"), 299 ) 300 .unwrap(); 301 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", config_key]); 302 insta::assert_snapshot!(stdout, @r###" 303 ui.editor="repo" 304 "###); 305 306 // Environment override 307 test_env.add_env_var("JJ_EDITOR", "env-override"); 308 let stdout = test_env.jj_cmd_success(&repo_path, &["config", "list", config_key]); 309 insta::assert_snapshot!(stdout, @r###" 310 ui.editor="env-override" 311 "###); 312 313 // Command argument 314 let stdout = test_env.jj_cmd_success( 315 &repo_path, 316 &[ 317 "config", 318 "list", 319 config_key, 320 "--config-toml", 321 &format!("{config_key}={value:?}", value = "command-arg"), 322 ], 323 ); 324 insta::assert_snapshot!(stdout, @r###" 325 ui.editor="command-arg" 326 "###); 327 328 // Allow printing overridden values 329 let stdout = test_env.jj_cmd_success( 330 &repo_path, 331 &[ 332 "config", 333 "list", 334 config_key, 335 "--include-overridden", 336 "--config-toml", 337 &format!("{config_key}={value:?}", value = "command-arg"), 338 ], 339 ); 340 insta::assert_snapshot!(stdout, @r###" 341 # ui.editor="env-base" 342 # ui.editor="user" 343 # ui.editor="repo" 344 # ui.editor="env-override" 345 ui.editor="command-arg" 346 "###); 347} 348 349#[test] 350fn test_config_layer_workspace() { 351 let test_env = TestEnvironment::default(); 352 test_env.jj_cmd_ok(test_env.env_root(), &["init", "--git", "main"]); 353 let main_path = test_env.env_root().join("main"); 354 let secondary_path = test_env.env_root().join("secondary"); 355 let config_key = "ui.editor"; 356 357 std::fs::write(main_path.join("file"), "contents").unwrap(); 358 test_env.jj_cmd_ok(&main_path, &["new"]); 359 test_env.jj_cmd_ok( 360 &main_path, 361 &["workspace", "add", "--name", "second", "../secondary"], 362 ); 363 364 // Repo 365 std::fs::write( 366 main_path.join(".jj/repo/config.toml"), 367 format!("{config_key} = {value:?}\n", value = "main-repo"), 368 ) 369 .unwrap(); 370 let stdout = test_env.jj_cmd_success(&main_path, &["config", "list", config_key]); 371 insta::assert_snapshot!(stdout, @r###" 372 ui.editor="main-repo" 373 "###); 374 let stdout = test_env.jj_cmd_success(&secondary_path, &["config", "list", config_key]); 375 insta::assert_snapshot!(stdout, @r###" 376 ui.editor="main-repo" 377 "###); 378} 379 380#[test] 381fn test_config_set_missing_opts() { 382 let test_env = TestEnvironment::default(); 383 let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "set"]); 384 insta::assert_snapshot!(stderr, @r###" 385 error: the following required arguments were not provided: 386 <--user|--repo> 387 <NAME> 388 <VALUE> 389 390 Usage: jj config set <--user|--repo> <NAME> <VALUE> 391 392 For more information, try '--help'. 393 "###); 394} 395 396#[test] 397fn test_config_set_for_user() { 398 let mut test_env = TestEnvironment::default(); 399 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 400 // Point to a config file since `config set` can't handle directories. 401 let user_config_path = test_env.config_path().join("config.toml"); 402 test_env.set_config_path(user_config_path.to_owned()); 403 let repo_path = test_env.env_root().join("repo"); 404 405 test_env.jj_cmd_ok( 406 &repo_path, 407 &["config", "set", "--user", "test-key", "test-val"], 408 ); 409 test_env.jj_cmd_ok( 410 &repo_path, 411 &["config", "set", "--user", "test-table.foo", "true"], 412 ); 413 414 // Ensure test-key successfully written to user config. 415 let user_config_toml = std::fs::read_to_string(&user_config_path) 416 .unwrap_or_else(|_| panic!("Failed to read file {}", user_config_path.display())); 417 insta::assert_snapshot!(user_config_toml, @r###" 418 test-key = "test-val" 419 420 [test-table] 421 foo = true 422 "###); 423} 424 425#[test] 426fn test_config_set_for_repo() { 427 let test_env = TestEnvironment::default(); 428 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 429 let repo_path = test_env.env_root().join("repo"); 430 test_env.jj_cmd_ok( 431 &repo_path, 432 &["config", "set", "--repo", "test-key", "test-val"], 433 ); 434 test_env.jj_cmd_ok( 435 &repo_path, 436 &["config", "set", "--repo", "test-table.foo", "true"], 437 ); 438 // Ensure test-key successfully written to user config. 439 let expected_repo_config_path = repo_path.join(".jj/repo/config.toml"); 440 let repo_config_toml = 441 std::fs::read_to_string(&expected_repo_config_path).unwrap_or_else(|_| { 442 panic!( 443 "Failed to read file {}", 444 expected_repo_config_path.display() 445 ) 446 }); 447 insta::assert_snapshot!(repo_config_toml, @r###" 448 test-key = "test-val" 449 450 [test-table] 451 foo = true 452 "###); 453} 454 455#[test] 456fn test_config_set_toml_types() { 457 let mut test_env = TestEnvironment::default(); 458 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 459 let user_config_path = test_env.config_path().join("config.toml"); 460 test_env.set_config_path(user_config_path.clone()); 461 let repo_path = test_env.env_root().join("repo"); 462 463 let set_value = |key, value| { 464 test_env.jj_cmd_success(&repo_path, &["config", "set", "--user", key, value]); 465 }; 466 set_value("test-table.integer", "42"); 467 set_value("test-table.float", "3.14"); 468 set_value("test-table.array", r#"["one", "two"]"#); 469 set_value("test-table.boolean", "true"); 470 set_value("test-table.string", r#""foo""#); 471 set_value("test-table.invalid", r"a + b"); 472 insta::assert_snapshot!(std::fs::read_to_string(&user_config_path).unwrap(), @r###" 473 [test-table] 474 integer = 42 475 float = 3.14 476 array = ["one", "two"] 477 boolean = true 478 string = "foo" 479 invalid = "a + b" 480 "###); 481} 482 483#[test] 484fn test_config_set_type_mismatch() { 485 let mut test_env = TestEnvironment::default(); 486 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 487 let user_config_path = test_env.config_path().join("config.toml"); 488 test_env.set_config_path(user_config_path); 489 let repo_path = test_env.env_root().join("repo"); 490 491 test_env.jj_cmd_ok( 492 &repo_path, 493 &["config", "set", "--user", "test-table.foo", "test-val"], 494 ); 495 let stderr = test_env.jj_cmd_failure( 496 &repo_path, 497 &["config", "set", "--user", "test-table", "not-a-table"], 498 ); 499 insta::assert_snapshot!(stderr, @r###" 500 Error: Failed to set test-table: would overwrite entire table 501 "###); 502 503 // But it's fine to overwrite arrays and inline tables 504 test_env.jj_cmd_success( 505 &repo_path, 506 &["config", "set", "--user", "test-table.array", "[1,2,3]"], 507 ); 508 test_env.jj_cmd_success( 509 &repo_path, 510 &["config", "set", "--user", "test-table.array", "[4,5,6]"], 511 ); 512 test_env.jj_cmd_success( 513 &repo_path, 514 &["config", "set", "--user", "test-table.inline", "{ x = 42}"], 515 ); 516 test_env.jj_cmd_success( 517 &repo_path, 518 &["config", "set", "--user", "test-table.inline", "42"], 519 ); 520} 521 522#[test] 523fn test_config_set_nontable_parent() { 524 let mut test_env = TestEnvironment::default(); 525 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 526 let user_config_path = test_env.config_path().join("config.toml"); 527 test_env.set_config_path(user_config_path); 528 let repo_path = test_env.env_root().join("repo"); 529 530 test_env.jj_cmd_ok( 531 &repo_path, 532 &["config", "set", "--user", "test-nontable", "test-val"], 533 ); 534 let stderr = test_env.jj_cmd_failure( 535 &repo_path, 536 &["config", "set", "--user", "test-nontable.foo", "test-val"], 537 ); 538 insta::assert_snapshot!(stderr, @r###" 539 Error: Failed to set test-nontable.foo: would overwrite non-table value with parent table 540 "###); 541} 542 543#[test] 544fn test_config_edit_missing_opt() { 545 let test_env = TestEnvironment::default(); 546 let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["config", "edit"]); 547 insta::assert_snapshot!(stderr, @r###" 548 error: the following required arguments were not provided: 549 <--user|--repo> 550 551 Usage: jj config edit <--user|--repo> 552 553 For more information, try '--help'. 554 "###); 555} 556 557#[test] 558fn test_config_edit_user() { 559 let mut test_env = TestEnvironment::default(); 560 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 561 let repo_path = test_env.env_root().join("repo"); 562 let edit_script = test_env.set_up_fake_editor(); 563 564 std::fs::write( 565 edit_script, 566 format!("expectpath\n{}", test_env.config_path().to_str().unwrap()), 567 ) 568 .unwrap(); 569 test_env.jj_cmd_ok(&repo_path, &["config", "edit", "--user"]); 570} 571 572#[test] 573fn test_config_edit_repo() { 574 let mut test_env = TestEnvironment::default(); 575 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 576 let repo_path = test_env.env_root().join("repo"); 577 let edit_script = test_env.set_up_fake_editor(); 578 579 std::fs::write( 580 edit_script, 581 format!( 582 "expectpath\n{}", 583 repo_path.join(".jj/repo/config.toml").to_str().unwrap() 584 ), 585 ) 586 .unwrap(); 587 test_env.jj_cmd_ok(&repo_path, &["config", "edit", "--repo"]); 588} 589 590#[test] 591fn test_config_path() { 592 let test_env = TestEnvironment::default(); 593 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 594 let repo_path = test_env.env_root().join("repo"); 595 596 assert_snapshot!( 597 test_env.jj_cmd_success(&repo_path, &["config", "path", "--user"]), 598 @r###" 599 $TEST_ENV/config 600 "### 601 ); 602 assert_snapshot!( 603 test_env.jj_cmd_success(&repo_path, &["config", "path", "--repo"]), 604 @r###" 605 $TEST_ENV/repo/.jj/repo/config.toml 606 "### 607 ); 608} 609 610#[test] 611fn test_config_edit_repo_outside_repo() { 612 let test_env = TestEnvironment::default(); 613 let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["config", "edit", "--repo"]); 614 insta::assert_snapshot!(stderr, @r###" 615 Error: There is no jj repo in "." 616 "###); 617} 618 619#[test] 620fn test_config_get() { 621 let test_env = TestEnvironment::default(); 622 test_env.add_config( 623 r#" 624 [table] 625 string = "some value 1" 626 int = 123 627 list = ["list", "value"] 628 overridden = "foo" 629 "#, 630 ); 631 test_env.add_config( 632 r#" 633 [table] 634 overridden = "bar" 635 "#, 636 ); 637 638 let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "nonexistent"]); 639 insta::assert_snapshot!(stdout, @r###" 640 Config error: configuration property "nonexistent" not found 641 For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md. 642 "###); 643 644 let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "table.string"]); 645 insta::assert_snapshot!(stdout, @r###" 646 some value 1 647 "###); 648 649 let stdout = test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "table.int"]); 650 insta::assert_snapshot!(stdout, @r###" 651 123 652 "###); 653 654 let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "table.list"]); 655 insta::assert_snapshot!(stdout, @r###" 656 Config error: invalid type: sequence, expected a value convertible to a string 657 For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md. 658 "###); 659 660 let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["config", "get", "table"]); 661 insta::assert_snapshot!(stdout, @r###" 662 Config error: invalid type: map, expected a value convertible to a string 663 For help, see https://github.com/martinvonz/jj/blob/main/docs/config.md. 664 "###); 665 666 let stdout = 667 test_env.jj_cmd_success(test_env.env_root(), &["config", "get", "table.overridden"]); 668 insta::assert_snapshot!(stdout, @"bar"); 669} 670 671fn find_stdout_lines(keyname_pattern: &str, stdout: &str) -> String { 672 let key_line_re = Regex::new(&format!(r"(?m)^{keyname_pattern}=.*$")).unwrap(); 673 key_line_re 674 .find_iter(stdout) 675 .map(|m| m.as_str()) 676 .collect_vec() 677 .join("\n") 678}