| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='split index mode tests' |
| 4 | |
| 5 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 6 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 7 | |
| 8 | . ./test-lib.sh |
| 9 | |
| 10 | # We need total control of index splitting here |
| 11 | sane_unset GIT_TEST_SPLIT_INDEX |
| 12 | |
| 13 | # Testing a hard coded SHA against an index with an extension |
| 14 | # that can vary from run to run is problematic so we disable |
| 15 | # those extensions. |
| 16 | sane_unset GIT_TEST_FSMONITOR |
| 17 | sane_unset GIT_TEST_INDEX_THREADS |
| 18 | |
| 19 | # Create a file named as $1 with content read from stdin. |
| 20 | # Set the file's mtime to a few seconds in the past to avoid racy situations. |
| 21 | create_non_racy_file () { |
| 22 | cat >"$1" && |
| 23 | test-tool chmtime =-5 "$1" |
| 24 | } |
| 25 | |
| 26 | test_expect_success 'setup' ' |
| 27 | test_oid_cache <<-EOF |
| 28 | own_v3 sha1:8299b0bcd1ac364e5f1d7768efb62fa2da79a339 |
| 29 | own_v3 sha256:38a6d2925e3eceec33ad7b34cbff4e0086caa0daf28f31e51f5bd94b4a7af86b |
| 30 | |
| 31 | base_v3 sha1:39d890139ee5356c7ef572216cebcd27aa41f9df |
| 32 | base_v3 sha256:c9baeadf905112bf6c17aefbd7d02267afd70ded613c30cafed2d40cb506e1ed |
| 33 | |
| 34 | own_v4 sha1:432ef4b63f32193984f339431fd50ca796493569 |
| 35 | own_v4 sha256:6738ac6319c25b694afa7bcc313deb182d1a59b68bf7a47b4296de83478c0420 |
| 36 | |
| 37 | base_v4 sha1:508851a7f0dfa8691e9f69c7f055865389012491 |
| 38 | base_v4 sha256:3177d4adfdd4b6904f7e921d91d715a471c0dde7cf6a4bba574927f02b699508 |
| 39 | EOF |
| 40 | ' |
| 41 | |
| 42 | test_expect_success 'enable split index' ' |
| 43 | git config splitIndex.maxPercentChange 100 && |
| 44 | git update-index --split-index && |
| 45 | test-tool dump-split-index .git/index >actual && |
| 46 | indexversion=$(git update-index --show-index-version) && |
| 47 | |
| 48 | # NEEDSWORK: Stop hard-coding checksums. |
| 49 | if test "$indexversion" = "4" |
| 50 | then |
| 51 | own=$(test_oid own_v4) && |
| 52 | base=$(test_oid base_v4) |
| 53 | else |
| 54 | own=$(test_oid own_v3) && |
| 55 | base=$(test_oid base_v3) |
| 56 | fi && |
| 57 | |
| 58 | cat >expect <<-EOF && |
| 59 | own $own |
| 60 | base $base |
| 61 | replacements: |
| 62 | deletions: |
| 63 | EOF |
| 64 | test_cmp expect actual |
| 65 | ' |
| 66 | |
| 67 | test_expect_success 'add one file' ' |
| 68 | create_non_racy_file one && |
| 69 | git update-index --add one && |
| 70 | git ls-files --stage >ls-files.actual && |
| 71 | cat >ls-files.expect <<-EOF && |
| 72 | 100644 $EMPTY_BLOB 0 one |
| 73 | EOF |
| 74 | test_cmp ls-files.expect ls-files.actual && |
| 75 | |
| 76 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 77 | cat >expect <<-EOF && |
| 78 | base $base |
| 79 | 100644 $EMPTY_BLOB 0 one |
| 80 | replacements: |
| 81 | deletions: |
| 82 | EOF |
| 83 | test_cmp expect actual |
| 84 | ' |
| 85 | |
| 86 | test_expect_success 'disable split index' ' |
| 87 | git update-index --no-split-index && |
| 88 | git ls-files --stage >ls-files.actual && |
| 89 | cat >ls-files.expect <<-EOF && |
| 90 | 100644 $EMPTY_BLOB 0 one |
| 91 | EOF |
| 92 | test_cmp ls-files.expect ls-files.actual && |
| 93 | |
| 94 | BASE=$(test-tool dump-split-index .git/index | sed -n "s/^own/base/p") && |
| 95 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 96 | cat >expect <<-EOF && |
| 97 | not a split index |
| 98 | EOF |
| 99 | test_cmp expect actual |
| 100 | ' |
| 101 | |
| 102 | test_expect_success 'enable split index again, "one" now belongs to base index"' ' |
| 103 | git update-index --split-index && |
| 104 | git ls-files --stage >ls-files.actual && |
| 105 | cat >ls-files.expect <<-EOF && |
| 106 | 100644 $EMPTY_BLOB 0 one |
| 107 | EOF |
| 108 | test_cmp ls-files.expect ls-files.actual && |
| 109 | |
| 110 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 111 | cat >expect <<-EOF && |
| 112 | $BASE |
| 113 | replacements: |
| 114 | deletions: |
| 115 | EOF |
| 116 | test_cmp expect actual |
| 117 | ' |
| 118 | |
| 119 | test_expect_success 'modify original file, base index untouched' ' |
| 120 | echo modified | create_non_racy_file one && |
| 121 | file1_blob=$(git hash-object one) && |
| 122 | git update-index one && |
| 123 | git ls-files --stage >ls-files.actual && |
| 124 | cat >ls-files.expect <<-EOF && |
| 125 | 100644 $file1_blob 0 one |
| 126 | EOF |
| 127 | test_cmp ls-files.expect ls-files.actual && |
| 128 | |
| 129 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 130 | q_to_tab >expect <<-EOF && |
| 131 | $BASE |
| 132 | 100644 $file1_blob 0Q |
| 133 | replacements: 0 |
| 134 | deletions: |
| 135 | EOF |
| 136 | test_cmp expect actual |
| 137 | ' |
| 138 | |
| 139 | test_expect_success 'add another file, which stays index' ' |
| 140 | create_non_racy_file two && |
| 141 | git update-index --add two && |
| 142 | git ls-files --stage >ls-files.actual && |
| 143 | cat >ls-files.expect <<-EOF && |
| 144 | 100644 $file1_blob 0 one |
| 145 | 100644 $EMPTY_BLOB 0 two |
| 146 | EOF |
| 147 | test_cmp ls-files.expect ls-files.actual && |
| 148 | |
| 149 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 150 | q_to_tab >expect <<-EOF && |
| 151 | $BASE |
| 152 | 100644 $file1_blob 0Q |
| 153 | 100644 $EMPTY_BLOB 0 two |
| 154 | replacements: 0 |
| 155 | deletions: |
| 156 | EOF |
| 157 | test_cmp expect actual |
| 158 | ' |
| 159 | |
| 160 | test_expect_success 'remove file not in base index' ' |
| 161 | git update-index --force-remove two && |
| 162 | git ls-files --stage >ls-files.actual && |
| 163 | cat >ls-files.expect <<-EOF && |
| 164 | 100644 $file1_blob 0 one |
| 165 | EOF |
| 166 | test_cmp ls-files.expect ls-files.actual && |
| 167 | |
| 168 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 169 | q_to_tab >expect <<-EOF && |
| 170 | $BASE |
| 171 | 100644 $file1_blob 0Q |
| 172 | replacements: 0 |
| 173 | deletions: |
| 174 | EOF |
| 175 | test_cmp expect actual |
| 176 | ' |
| 177 | |
| 178 | test_expect_success 'remove file in base index' ' |
| 179 | git update-index --force-remove one && |
| 180 | git ls-files --stage >ls-files.actual && |
| 181 | test_must_be_empty ls-files.actual && |
| 182 | |
| 183 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 184 | cat >expect <<-EOF && |
| 185 | $BASE |
| 186 | replacements: |
| 187 | deletions: 0 |
| 188 | EOF |
| 189 | test_cmp expect actual |
| 190 | ' |
| 191 | |
| 192 | test_expect_success 'add original file back' ' |
| 193 | create_non_racy_file one && |
| 194 | git update-index --add one && |
| 195 | git ls-files --stage >ls-files.actual && |
| 196 | cat >ls-files.expect <<-EOF && |
| 197 | 100644 $EMPTY_BLOB 0 one |
| 198 | EOF |
| 199 | test_cmp ls-files.expect ls-files.actual && |
| 200 | |
| 201 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 202 | cat >expect <<-EOF && |
| 203 | $BASE |
| 204 | 100644 $EMPTY_BLOB 0 one |
| 205 | replacements: |
| 206 | deletions: 0 |
| 207 | EOF |
| 208 | test_cmp expect actual |
| 209 | ' |
| 210 | |
| 211 | test_expect_success 'add new file' ' |
| 212 | create_non_racy_file two && |
| 213 | git update-index --add two && |
| 214 | git ls-files --stage >actual && |
| 215 | cat >expect <<-EOF && |
| 216 | 100644 $EMPTY_BLOB 0 one |
| 217 | 100644 $EMPTY_BLOB 0 two |
| 218 | EOF |
| 219 | test_cmp expect actual |
| 220 | ' |
| 221 | |
| 222 | test_expect_success 'unify index, two files remain' ' |
| 223 | git update-index --no-split-index && |
| 224 | git ls-files --stage >ls-files.actual && |
| 225 | cat >ls-files.expect <<-EOF && |
| 226 | 100644 $EMPTY_BLOB 0 one |
| 227 | 100644 $EMPTY_BLOB 0 two |
| 228 | EOF |
| 229 | test_cmp ls-files.expect ls-files.actual && |
| 230 | |
| 231 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 232 | cat >expect <<-EOF && |
| 233 | not a split index |
| 234 | EOF |
| 235 | test_cmp expect actual |
| 236 | ' |
| 237 | |
| 238 | test_expect_success 'rev-parse --shared-index-path' ' |
| 239 | test_create_repo split-index && |
| 240 | ( |
| 241 | cd split-index && |
| 242 | git update-index --split-index && |
| 243 | echo .git/sharedindex* >expect && |
| 244 | git rev-parse --shared-index-path >actual && |
| 245 | test_cmp expect actual && |
| 246 | mkdir subdirectory && |
| 247 | cd subdirectory && |
| 248 | echo ../.git/sharedindex* >expect && |
| 249 | git rev-parse --shared-index-path >actual && |
| 250 | test_cmp expect actual |
| 251 | ) |
| 252 | ' |
| 253 | |
| 254 | test_expect_success 'set core.splitIndex config variable to true' ' |
| 255 | git config core.splitIndex true && |
| 256 | create_non_racy_file three && |
| 257 | git update-index --add three && |
| 258 | git ls-files --stage >ls-files.actual && |
| 259 | cat >ls-files.expect <<-EOF && |
| 260 | 100644 $EMPTY_BLOB 0 one |
| 261 | 100644 $EMPTY_BLOB 0 three |
| 262 | 100644 $EMPTY_BLOB 0 two |
| 263 | EOF |
| 264 | test_cmp ls-files.expect ls-files.actual && |
| 265 | BASE=$(test-tool dump-split-index .git/index | grep "^base") && |
| 266 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 267 | cat >expect <<-EOF && |
| 268 | $BASE |
| 269 | replacements: |
| 270 | deletions: |
| 271 | EOF |
| 272 | test_cmp expect actual |
| 273 | ' |
| 274 | |
| 275 | test_expect_success 'set core.splitIndex config variable to false' ' |
| 276 | git config core.splitIndex false && |
| 277 | git update-index --force-remove three && |
| 278 | git ls-files --stage >ls-files.actual && |
| 279 | cat >ls-files.expect <<-EOF && |
| 280 | 100644 $EMPTY_BLOB 0 one |
| 281 | 100644 $EMPTY_BLOB 0 two |
| 282 | EOF |
| 283 | test_cmp ls-files.expect ls-files.actual && |
| 284 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 285 | cat >expect <<-EOF && |
| 286 | not a split index |
| 287 | EOF |
| 288 | test_cmp expect actual |
| 289 | ' |
| 290 | |
| 291 | test_expect_success 'set core.splitIndex config variable back to true' ' |
| 292 | git config core.splitIndex true && |
| 293 | create_non_racy_file three && |
| 294 | git update-index --add three && |
| 295 | BASE=$(test-tool dump-split-index .git/index | grep "^base") && |
| 296 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 297 | cat >expect <<-EOF && |
| 298 | $BASE |
| 299 | replacements: |
| 300 | deletions: |
| 301 | EOF |
| 302 | test_cmp expect actual && |
| 303 | create_non_racy_file four && |
| 304 | git update-index --add four && |
| 305 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 306 | cat >expect <<-EOF && |
| 307 | $BASE |
| 308 | 100644 $EMPTY_BLOB 0 four |
| 309 | replacements: |
| 310 | deletions: |
| 311 | EOF |
| 312 | test_cmp expect actual |
| 313 | ' |
| 314 | |
| 315 | test_expect_success 'check behavior with splitIndex.maxPercentChange unset' ' |
| 316 | git config --unset splitIndex.maxPercentChange && |
| 317 | create_non_racy_file five && |
| 318 | git update-index --add five && |
| 319 | BASE=$(test-tool dump-split-index .git/index | grep "^base") && |
| 320 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 321 | cat >expect <<-EOF && |
| 322 | $BASE |
| 323 | replacements: |
| 324 | deletions: |
| 325 | EOF |
| 326 | test_cmp expect actual && |
| 327 | create_non_racy_file six && |
| 328 | git update-index --add six && |
| 329 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 330 | cat >expect <<-EOF && |
| 331 | $BASE |
| 332 | 100644 $EMPTY_BLOB 0 six |
| 333 | replacements: |
| 334 | deletions: |
| 335 | EOF |
| 336 | test_cmp expect actual |
| 337 | ' |
| 338 | |
| 339 | test_expect_success 'check splitIndex.maxPercentChange set to 0' ' |
| 340 | git config splitIndex.maxPercentChange 0 && |
| 341 | create_non_racy_file seven && |
| 342 | git update-index --add seven && |
| 343 | BASE=$(test-tool dump-split-index .git/index | grep "^base") && |
| 344 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 345 | cat >expect <<-EOF && |
| 346 | $BASE |
| 347 | replacements: |
| 348 | deletions: |
| 349 | EOF |
| 350 | test_cmp expect actual && |
| 351 | create_non_racy_file eight && |
| 352 | git update-index --add eight && |
| 353 | BASE=$(test-tool dump-split-index .git/index | grep "^base") && |
| 354 | test-tool dump-split-index .git/index | sed "/^own/d" >actual && |
| 355 | cat >expect <<-EOF && |
| 356 | $BASE |
| 357 | replacements: |
| 358 | deletions: |
| 359 | EOF |
| 360 | test_cmp expect actual |
| 361 | ' |
| 362 | |
| 363 | test_expect_success 'shared index files expire after 2 weeks by default' ' |
| 364 | create_non_racy_file ten && |
| 365 | git update-index --add ten && |
| 366 | test $(ls .git/sharedindex.* | wc -l) -gt 2 && |
| 367 | just_under_2_weeks_ago=$((5-14*86400)) && |
| 368 | test-tool chmtime =$just_under_2_weeks_ago .git/sharedindex.* && |
| 369 | create_non_racy_file eleven && |
| 370 | git update-index --add eleven && |
| 371 | test $(ls .git/sharedindex.* | wc -l) -gt 2 && |
| 372 | just_over_2_weeks_ago=$((-1-14*86400)) && |
| 373 | test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* && |
| 374 | create_non_racy_file twelve && |
| 375 | git update-index --add twelve && |
| 376 | test $(ls .git/sharedindex.* | wc -l) -le 2 |
| 377 | ' |
| 378 | |
| 379 | test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' ' |
| 380 | git config splitIndex.sharedIndexExpire "16.days.ago" && |
| 381 | test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* && |
| 382 | create_non_racy_file thirteen && |
| 383 | git update-index --add thirteen && |
| 384 | test $(ls .git/sharedindex.* | wc -l) -gt 2 && |
| 385 | just_over_16_days_ago=$((-1-16*86400)) && |
| 386 | test-tool chmtime =$just_over_16_days_ago .git/sharedindex.* && |
| 387 | create_non_racy_file fourteen && |
| 388 | git update-index --add fourteen && |
| 389 | test $(ls .git/sharedindex.* | wc -l) -le 2 |
| 390 | ' |
| 391 | |
| 392 | test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"' ' |
| 393 | git config splitIndex.sharedIndexExpire never && |
| 394 | just_10_years_ago=$((-365*10*86400)) && |
| 395 | test-tool chmtime =$just_10_years_ago .git/sharedindex.* && |
| 396 | create_non_racy_file fifteen && |
| 397 | git update-index --add fifteen && |
| 398 | test $(ls .git/sharedindex.* | wc -l) -gt 2 && |
| 399 | git config splitIndex.sharedIndexExpire now && |
| 400 | just_1_second_ago=-1 && |
| 401 | test-tool chmtime =$just_1_second_ago .git/sharedindex.* && |
| 402 | create_non_racy_file sixteen && |
| 403 | git update-index --add sixteen && |
| 404 | test $(ls .git/sharedindex.* | wc -l) -le 2 |
| 405 | ' |
| 406 | |
| 407 | test_expect_success POSIXPERM 'same mode for index & split index' ' |
| 408 | git init same-mode && |
| 409 | ( |
| 410 | cd same-mode && |
| 411 | test_commit A && |
| 412 | test_modebits .git/index >index_mode && |
| 413 | test_must_fail git config core.sharedRepository && |
| 414 | git -c core.splitIndex=true status && |
| 415 | shared=$(ls .git/sharedindex.*) && |
| 416 | case "$shared" in |
| 417 | *" "*) |
| 418 | # we have more than one??? |
| 419 | false ;; |
| 420 | *) |
| 421 | test_modebits "$shared" >split_index_mode && |
| 422 | test_cmp index_mode split_index_mode ;; |
| 423 | esac |
| 424 | ) |
| 425 | ' |
| 426 | |
| 427 | while read -r mode modebits |
| 428 | do |
| 429 | test_expect_success POSIXPERM "split index respects core.sharedrepository $mode" ' |
| 430 | # Remove existing shared index files |
| 431 | git config core.splitIndex false && |
| 432 | git update-index --force-remove one && |
| 433 | rm -f .git/sharedindex.* && |
| 434 | # Create one new shared index file |
| 435 | git config core.sharedrepository "$mode" && |
| 436 | git config core.splitIndex true && |
| 437 | create_non_racy_file one && |
| 438 | git update-index --add one && |
| 439 | echo "$modebits" >expect && |
| 440 | test_modebits .git/index >actual && |
| 441 | test_cmp expect actual && |
| 442 | shared=$(ls .git/sharedindex.*) && |
| 443 | case "$shared" in |
| 444 | *" "*) |
| 445 | # we have more than one??? |
| 446 | false ;; |
| 447 | *) |
| 448 | test_modebits "$shared" >actual && |
| 449 | test_cmp expect actual ;; |
| 450 | esac |
| 451 | ' |
| 452 | done <<\EOF |
| 453 | 0666 -rw-rw-rw- |
| 454 | 0642 -rw-r---w- |
| 455 | EOF |
| 456 | |
| 457 | test_expect_success POSIXPERM,SANITY 'graceful handling when splitting index is not allowed' ' |
| 458 | test_create_repo ro && |
| 459 | ( |
| 460 | cd ro && |
| 461 | test_commit initial && |
| 462 | git update-index --split-index && |
| 463 | test -f .git/sharedindex.* |
| 464 | ) && |
| 465 | cp ro/.git/index new-index && |
| 466 | test_when_finished "chmod u+w ro/.git" && |
| 467 | chmod u-w ro/.git && |
| 468 | GIT_INDEX_FILE="$(pwd)/new-index" git -C ro update-index --split-index && |
| 469 | chmod u+w ro/.git && |
| 470 | rm ro/.git/sharedindex.* && |
| 471 | GIT_INDEX_FILE=new-index git ls-files >actual && |
| 472 | echo initial.t >expected && |
| 473 | test_cmp expected actual |
| 474 | ' |
| 475 | |
| 476 | test_expect_success 'writing split index with null sha1 does not write cache tree' ' |
| 477 | git config core.splitIndex true && |
| 478 | git config splitIndex.maxPercentChange 0 && |
| 479 | git commit -m "commit" && |
| 480 | { |
| 481 | git ls-tree HEAD && |
| 482 | printf "160000 commit $ZERO_OID\\tbroken\\n" |
| 483 | } >broken-tree && |
| 484 | echo "add broken entry" >msg && |
| 485 | |
| 486 | tree=$(git mktree <broken-tree) && |
| 487 | test_tick && |
| 488 | commit=$(git commit-tree $tree -p HEAD <msg) && |
| 489 | git update-ref HEAD "$commit" && |
| 490 | GIT_ALLOW_NULL_SHA1=1 git reset --hard && |
| 491 | test_might_fail test-tool dump-cache-tree >cache-tree.out && |
| 492 | test_line_count = 0 cache-tree.out |
| 493 | ' |
| 494 | |
| 495 | test_expect_success 'do not refresh null base index' ' |
| 496 | test_create_repo merge && |
| 497 | ( |
| 498 | cd merge && |
| 499 | test_commit initial && |
| 500 | git checkout -b side-branch && |
| 501 | test_commit extra && |
| 502 | git checkout main && |
| 503 | git update-index --split-index && |
| 504 | test_commit more && |
| 505 | # must not write a new shareindex, or we will not catch the problem |
| 506 | git -c splitIndex.maxPercentChange=100 merge --no-edit side-branch 2>err && |
| 507 | # i.e. do not expect warnings like |
| 508 | # could not freshen shared index .../shareindex.00000... |
| 509 | test_must_be_empty err |
| 510 | ) |
| 511 | ' |
| 512 | |
| 513 | test_expect_success 'reading split index at alternate location' ' |
| 514 | git init reading-alternate-location && |
| 515 | ( |
| 516 | cd reading-alternate-location && |
| 517 | >file-in-alternate && |
| 518 | git update-index --split-index --add file-in-alternate |
| 519 | ) && |
| 520 | echo file-in-alternate >expect && |
| 521 | |
| 522 | # Should be able to find the shared index both right next to |
| 523 | # the specified split index file ... |
| 524 | GIT_INDEX_FILE=./reading-alternate-location/.git/index \ |
| 525 | git ls-files --cached >actual && |
| 526 | test_cmp expect actual && |
| 527 | |
| 528 | # ... and, for backwards compatibility, in the current GIT_DIR |
| 529 | # as well. |
| 530 | mv ./reading-alternate-location/.git/sharedindex.* .git && |
| 531 | GIT_INDEX_FILE=./reading-alternate-location/.git/index \ |
| 532 | git ls-files --cached >actual && |
| 533 | test_cmp expect actual |
| 534 | ' |
| 535 | |
| 536 | test_expect_success 'GIT_TEST_SPLIT_INDEX works' ' |
| 537 | git init git-test-split-index && |
| 538 | ( |
| 539 | cd git-test-split-index && |
| 540 | >file && |
| 541 | GIT_TEST_SPLIT_INDEX=1 git update-index --add file && |
| 542 | ls -l .git/sharedindex.* >actual && |
| 543 | test_line_count = 1 actual |
| 544 | ) |
| 545 | ' |
| 546 | |
| 547 | test_done |