Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/diff/prose/__tests__/PhutilProseDiffTestCase.php
- This file was added.
| <?php | |||||
| final class PhutilProseDiffTestCase extends PhutilTestCase { | |||||
| public function testProseDiffsDistance() { | |||||
| $this->assertProseParts( | |||||
| '', | |||||
| '', | |||||
| array(), | |||||
| pht('Empty')); | |||||
| $this->assertProseParts( | |||||
| "xxx\nyyy", | |||||
| "xxx\nzzz\nyyy", | |||||
| array( | |||||
| "= xxx\n", | |||||
| "+ zzz\n", | |||||
| '= yyy', | |||||
| ), | |||||
| pht('Add Paragraph')); | |||||
| $this->assertProseParts( | |||||
| "xxx\nzzz\nyyy", | |||||
| "xxx\nyyy", | |||||
| array( | |||||
| "= xxx\n", | |||||
| "- zzz\n", | |||||
| '= yyy', | |||||
| ), | |||||
| pht('Remove Paragraph')); | |||||
| // Without smoothing, the alogorithm identifies that "shark" and "cat" | |||||
| // both contain the letter "a" and tries to express this as a very | |||||
| // fine-grained edit which replaces "sh" with "c" and then "rk" with "t". | |||||
| // This is technically correct, but it is much easier for human viewers to | |||||
| // parse if we smooth this into a single removal and a single addition. | |||||
| $this->assertProseParts( | |||||
| 'They say the shark has nine lives.', | |||||
| 'They say the cat has nine lives.', | |||||
| array( | |||||
| '= They say the ', | |||||
| '- shark', | |||||
| '+ cat', | |||||
| '= has nine lives.', | |||||
| ), | |||||
| pht('"Shark/cat" word edit smoothenss.')); | |||||
| $this->assertProseParts( | |||||
| 'Rising quickly, she says', | |||||
| 'Rising quickly, she remarks:', | |||||
| array( | |||||
| '= Rising quickly, she ', | |||||
| '- says', | |||||
| '+ remarks:', | |||||
| ), | |||||
| pht('"Says/remarks" word edit smoothenss.')); | |||||
| $this->assertProseParts( | |||||
| 'See screenshots', | |||||
| 'Viewed video files', | |||||
| array( | |||||
| '- See screenshots', | |||||
| '+ Viewed video files', | |||||
| ), | |||||
| pht('Complete paragraph rewrite.')); | |||||
| $this->assertProseParts( | |||||
| 'xaaax', | |||||
| 'xbbbx', | |||||
| array( | |||||
| '- xaaax', | |||||
| '+ xbbbx', | |||||
| ), | |||||
| pht('Whole word rewrite with common prefix and suffix.')); | |||||
| $this->assertProseParts( | |||||
| ' aaa ', | |||||
| ' bbb ', | |||||
| array( | |||||
| '= ', | |||||
| '- aaa', | |||||
| '+ bbb', | |||||
| '= ', | |||||
| ), | |||||
| pht('Whole word rewrite with whitespace prefix and suffix.')); | |||||
| $this->assertSummaryProseParts( | |||||
| "a\nb\nc\nd\ne\nf\ng\nh\n", | |||||
| "a\nb\nc\nd\nX\nf\ng\nh\n", | |||||
| array( | |||||
| '.', | |||||
| "= d\n", | |||||
| '- e', | |||||
| '+ X', | |||||
| "= \nf", | |||||
| '.', | |||||
| ), | |||||
| pht('Summary diff with middle change.')); | |||||
| $this->assertSummaryProseParts( | |||||
| "a\nb\nc\nd\ne\nf\ng\nh\n", | |||||
| "X\nb\nc\nd\ne\nf\ng\nh\n", | |||||
| array( | |||||
| '- a', | |||||
| '+ X', | |||||
| "= \nb", | |||||
| '.', | |||||
| ), | |||||
| pht('Summary diff with head change.')); | |||||
| $this->assertSummaryProseParts( | |||||
| "a\nb\nc\nd\ne\nf\ng\nh\n", | |||||
| "a\nb\nc\nd\ne\nf\ng\nX\n", | |||||
| array( | |||||
| '.', | |||||
| "= g\n", | |||||
| '- h', | |||||
| '+ X', | |||||
| "= \n", | |||||
| ), | |||||
| pht('Summary diff with last change.')); | |||||
| $this->assertProseParts( | |||||
| 'aaa aaa aaa aaa, bbb bbb bbb bbb.', | |||||
| "aaa aaa aaa aaa, bbb bbb bbb bbb.\n\n- ccc ccc ccc", | |||||
| array( | |||||
| '= aaa aaa aaa aaa, bbb bbb bbb bbb.', | |||||
| "+ \n\n- ccc ccc ccc", | |||||
| ), | |||||
| pht('Diff with new trailing content.')); | |||||
| $this->assertProseParts( | |||||
| 'aaa aaa aaa aaa, bbb bbb bbb bbb.', | |||||
| 'aaa aaa aaa aaa bbb bbb bbb bbb.', | |||||
| array( | |||||
| '= aaa aaa aaa aaa', | |||||
| '- ,', | |||||
| '= bbb bbb bbb bbb.', | |||||
| ), | |||||
| pht('Diff with a removed comma.')); | |||||
| $this->assertProseParts( | |||||
| 'aaa aaa aaa aaa, bbb bbb bbb bbb.', | |||||
| "aaa aaa aaa aaa bbb bbb bbb bbb.\n\n- ccc ccc ccc!", | |||||
| array( | |||||
| '= aaa aaa aaa aaa', | |||||
| '- ,', | |||||
| '= bbb bbb bbb bbb.', | |||||
| "+ \n\n- ccc ccc ccc!", | |||||
| ), | |||||
| pht('Diff with a removed comma and new trailing content.')); | |||||
| $this->assertProseParts( | |||||
| '[ ] Walnuts', | |||||
| '[X] Walnuts', | |||||
| array( | |||||
| '= [', | |||||
| '- ', | |||||
| '+ X', | |||||
| '= ] Walnuts', | |||||
| ), | |||||
| pht('Diff adding a tickmark to a checkbox list.')); | |||||
| $this->assertProseParts( | |||||
| '[[ ./week49 ]]', | |||||
| '[[ ./week50 ]]', | |||||
| array( | |||||
| '= [[ ./week', | |||||
| '- 49', | |||||
| '+ 50', | |||||
| '= ]]', | |||||
| ), | |||||
| pht('Diff changing a remarkup wiki link target.')); | |||||
| // Create a large corpus with many sentences and paragraphs. | |||||
| $large_paragraph = 'xyz. '; | |||||
| $large_paragraph = str_repeat($large_paragraph, 50); | |||||
| $large_paragraph = rtrim($large_paragraph); | |||||
| $large_corpus = $large_paragraph."\n\n"; | |||||
| $large_corpus = str_repeat($large_corpus, 50); | |||||
| $large_corpus = rtrim($large_corpus); | |||||
| $this->assertProseParts( | |||||
| $large_corpus, | |||||
| "aaa\n\n".$large_corpus."\n\nzzz", | |||||
| array( | |||||
| "+ aaa\n\n", | |||||
| '= '.$large_corpus, | |||||
| "+ \n\nzzz", | |||||
| ), | |||||
| pht('Adding initial and final lines to a large corpus.')); | |||||
| } | |||||
| private function assertProseParts($old, $new, array $expect_parts, $label) { | |||||
| $engine = new PhutilProseDifferenceEngine(); | |||||
| $diff = $engine->getDiff($old, $new); | |||||
| $parts = $diff->getParts(); | |||||
| $this->assertParts($expect_parts, $parts, $label); | |||||
| } | |||||
| private function assertSummaryProseParts( | |||||
| $old, | |||||
| $new, | |||||
| array $expect_parts, | |||||
| $label) { | |||||
| $engine = new PhutilProseDifferenceEngine(); | |||||
| $diff = $engine->getDiff($old, $new); | |||||
| $parts = $diff->getSummaryParts(); | |||||
| $this->assertParts($expect_parts, $parts, $label); | |||||
| } | |||||
| private function assertParts( | |||||
| array $expect, | |||||
| array $actual_parts, | |||||
| $label) { | |||||
| $actual = array(); | |||||
| foreach ($actual_parts as $actual_part) { | |||||
| $type = $actual_part['type']; | |||||
| $text = $actual_part['text']; | |||||
| switch ($type) { | |||||
| case '.': | |||||
| $actual[] = $type; | |||||
| break; | |||||
| default: | |||||
| $actual[] = "{$type} {$text}"; | |||||
| break; | |||||
| } | |||||
| } | |||||
| $this->assertEqual($expect, $actual, $label); | |||||
| } | |||||
| } | |||||