Repository: online-judge-tools/oj

Issue:
--display-mode=diff で例外が飛ぶことがある
## Summary / 概要


## Steps to reproduce / 再現方法

https://codeforces.com/gym/103081/problem/A のサンプルで以下を使って `$ oj t -c 'python3 main.py' -D`

``` python
#!/usr/bin/env python3
from typing import *


def solve(n: int, k: int, a: List[str]) -> List[str]:
    cnt = {}
    for a_i in a:
        if a_i not in cnt:
            cnt[a_i] = 0
        cnt[a_i] += 1
    que = []
    for i in range(3 * n):
        que.append((cnt[a[i]], - i, a[i]))
    used = set()
    ans = []
    while len(ans) < k and que:
        _, _, s = que.pop()
        if s not in used:
            used.add(s)
            ans.append(s)
    return ans


# generated by oj-template v4.8.0 (https://github.com/online-judge-tools/template-generator)
def main():
    n, k = map(int, input().split())  # TODO: edit here
    a = []
    for _ in range(3 * n):
        a.append(input().strip())
    ans = solve(n, k, a)
    for x in ans:
        print(x)  # TODO: edit here


if __name__ == '__main__':
    main()
```

environments:

-   version: https://github.com/online-judge-tools/oj/commit/d634042e30fe97dfd70301032840c83e60f48dbf

## Expected behavior / 期待される挙動


## Actual behavior / 実際の挙動

``` console
~/Algo/codeforces/103081/A> oj t -c 'python3 main.py' -D
[INFO] online-judge-tools 11.4.0 (+ online-judge-api-client 10.10.0)
[INFO] 2 cases found

[INFO] sample-1
[INFO] time: 0.018912 sec
[FAILURE] WA
input:
2_2
Supportive_parents
Being_able_to_solve_a_hard_problem
Good_food
Fun_game_with_friends
Good_food
Being_healthy

[ERROR]
Traceback (most recent call last):
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/main.py", line 101, in main
    sys.exit(run_program(parsed, parser=parser))
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/main.py", line 70, in run_program
    if not subcommand_test.run(args):
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/subcommand/test.py", line 338, in run
    history += [test_single_case(name, paths['in'], paths.get('out'), args=args)]
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/subcommand/test.py", line 276, in test_single_case
    status = display_result(proc, answer, memory, test_input_path, test_output_path, mle=args.mle, display_mode=DisplayMode(args.display_mode), compare_mode=CompareMode(args.compare_mode), does_print_input=args.print_input, silent=args.silent, match_result=match_result)
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/subcommand/test.py", line 229, in display_result
    logger.info(utils.NO_HEADER + pretty_printers.make_pretty_diff(answer.encode(), expected=expected, compare_mode=compare_mode, limit=40))
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/pretty_printers.py", line 663, in make_pretty_diff
    tokens += _tokenize_pretty_diff(output, expected=expected, compare_mode=compare_mode, char_in_line=char_in_line, limit=limit)
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/pretty_printers.py", line 651, in _tokenize_pretty_diff
    ops = _make_diff_between_file_and_file(output, expected, compare_mode=compare_mode)
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/pretty_printers.py", line 451, in _make_diff_between_file_and_file
    return _make_diff_between_file_and_file_by_comparing_line_by_line(a, b, compare_mode=compare_mode)
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/pretty_printers.py", line 363, in _make_diff_between_file_and_file_by_comparing_line_by_line
    tokens_a, tokens_b = _make_diff_between_line_and_line(lines_a[i], lines_b[i])
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/pretty_printers.py", line 339, in _make_diff_between_line_and_line
    return _make_diff_between_line_and_line_by_comparing_word_by_word(a, b)
  File "/home/ubuntu/GitHub/oj/onlinejudge_command/pretty_printers.py", line 290, in _make_diff_between_line_and_line_by_comparing_word_by_word
    assert l_a == len(a) and l_b == len(b)  # The two strings have the same number of words, so this must be true.
AssertionError
```


## Other notes / その他



Discussion:

Patch:
diff --git a/onlinejudge_command/pretty_printers.py b/onlinejudge_command/pretty_printers.py
index 579645c..1c70780 100644
--- a/onlinejudge_command/pretty_printers.py
+++ b/onlinejudge_command/pretty_printers.py
@@ -270,7 +270,7 @@ def _make_diff_between_line_and_line_by_comparing_word_by_word(a: str, b: str) -
         r_b = l_b + 1
         while r_a < len(a) and a[r_a] not in ' \t\r\n':
             r_a += 1
-        while r_b < len(a) and b[r_b] not in ' \t\r\n':
+        while r_b < len(b) and b[r_b] not in ' \t\r\n':
             r_b += 1
         word_a = a[l_a:r_a]
         word_b = b[l_b:r_b]


Test patch:
diff --git a/tests/command_test.py b/tests/command_test.py
index c0df055..d0723fb 100644
--- a/tests/command_test.py
+++ b/tests/command_test.py
@@ -3,7 +3,6 @@ import os
 import pathlib
 import platform
 import random
-import shutil
 import signal
 import sys
 import tempfile
@@ -487,15 +486,15 @@ class TestTest(unittest.TestCase):
                 },
                 {
                     'path': 'test/sample-2.in',
-                    'data': 'bar\nfoobar\n'
+                    'data': 'bar baz\nfoo bar\n'
                 },
                 {
                     'path': 'test/sample-2.out',
-                    'data': 'bar\nbarbar\n'
+                    'data': 'hello world\nhello world\n'
                 },
                 {
                     'path': 'test/sample-3.in',
-                    'data': 'bar\nfoobar\n'
+                    'data': 'bar\nfoo\n'
                 },
                 {
                     'path': 'test/sample-3.out',
@@ -526,7 +525,7 @@ class TestTest(unittest.TestCase):
                     'input': '%s/test/sample-2.in',
                     'output': '%s/test/sample-2.out',
                 },
-                'output': 'bar\nfoobar\n',
+                'output': 'bar baz\nfoo bar\n',
                 'exitcode': 0,
             }, {
                 'status': 'WA',
@@ -535,7 +534,7 @@ class TestTest(unittest.TestCase):
                     'input': '%s/test/sample-3.in',
                     'output': '%s/test/sample-3.out',
                 },
-                'output': 'bar\nfoobar\n',
+                'output': 'bar\nfoo\n',
                 'exitcode': 0,
             }, {
                 'status': 'WA',
@@ -1326,62 +1325,3 @@ class TestTest(unittest.TestCase):
         for cmdline in pathlib.Path('/proc').glob('*/cmdline'):
             with open(str(cmdline), 'rb') as fh:
                 self.assertNotIn(marker_for_callee.encode(), fh.read())
-
-
-@unittest.skip("This end-to-end test is too fragile. We should refactor the implementation and write unit tests.")
-class TestTestLog(unittest.TestCase):
-    max_chars = shutil.get_terminal_size()[0] // 2 - 2
-
-    def check_log_lines(self, result, expect):
-        self.assertEqual(len(result), len(expect))
-        for result_line, expect_line in zip(result, expect):
-            if expect_line.startswith('[x]'):
-                # Time and max memory can not be reproduced
-                self.assertTrue(result_line.startswith(expect_line))
-            else:
-                self.assertEqual(result_line, expect_line)
-
-    def snippet_call_test(self, args, files, expected_log_lines):
-        self.maxDiff = None
-        result = tests.utils.run_in_sandbox(args=['test'] + args, files=files, pipe_stderr=True)
-        print(result['proc'].stderr.decode(), file=sys.stderr)
-        self.check_log_lines(result['proc'].stderr.decode().split(os.linesep), expected_log_lines)
-
-    def test_trailing_spaces(self):
-        self.snippet_call_test(
-            args=['-c', tests.utils.python_c(r"import sys; sys.stdout.buffer.write(b'1\r\n2 \r\n3\n4  \t  \n5  \n6')")],
-            files=[
-                {
-                    'path': 'test/sample-1.in',
-                    'data': '',
-                },
-                {
-                    'path': 'test/sample-1.out',
-                    'data': '1\n2\n3\n',
-                },
-            ],
-            expected_log_lines=[
-                '[*] 1 cases found',
-                '',
-                '[*] sample-1',
-                '[x] time:',
-                '[-] WA',
-                'output:',
-                r'1\r',
-                r'2_\r(trailing spaces)',
-                r'3',
-                r'4__\t__(trailing spaces)',
-                r'5__(trailing spaces)',
-                r'6(no trailing newline)',
-                'expected:',
-                '1',
-                '2',
-                '3',
-                '',
-                '',
-                '[x] slowest:',
-                '[x] max memory:',
-                '[-] test failed: 0 AC / 1 cases',
-                '',
-            ],
-        )