diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 8e19b001f3..3b422cc6d3 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -293,6 +293,7 @@ phutil_register_library_map(array( 'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php', 'DifferentialActionMenuEventListener' => 'applications/differential/event/DifferentialActionMenuEventListener.php', 'DifferentialAddCommentView' => 'applications/differential/view/DifferentialAddCommentView.php', + 'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php', 'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php', 'DifferentialApplyPatchField' => 'applications/differential/customfield/DifferentialApplyPatchField.php', 'DifferentialArcanistProjectField' => 'applications/differential/customfield/DifferentialArcanistProjectField.php', @@ -391,6 +392,7 @@ phutil_register_library_map(array( 'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php', 'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php', 'DifferentialLegacyHunk' => 'applications/differential/storage/DifferentialLegacyHunk.php', + 'DifferentialLineAdjustmentMap' => 'applications/differential/parser/DifferentialLineAdjustmentMap.php', 'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php', 'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php', 'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php', @@ -3530,6 +3532,7 @@ phutil_register_library_map(array( 'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand', 'DifferentialActionMenuEventListener' => 'PhabricatorEventListener', 'DifferentialAddCommentView' => 'AphrontView', + 'DifferentialAdjustmentMapTestCase' => 'ArcanistPhutilTestCase', 'DifferentialAffectedPath' => 'DifferentialDAO', 'DifferentialApplyPatchField' => 'DifferentialCustomField', 'DifferentialArcanistProjectField' => 'DifferentialCustomField', @@ -3632,6 +3635,7 @@ phutil_register_library_map(array( 'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField', 'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener', 'DifferentialLegacyHunk' => 'DifferentialHunk', + 'DifferentialLineAdjustmentMap' => 'Phobject', 'DifferentialLintField' => 'DifferentialCustomField', 'DifferentialLocalCommitsView' => 'AphrontView', 'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField', diff --git a/src/applications/differential/parser/DifferentialLineAdjustmentMap.php b/src/applications/differential/parser/DifferentialLineAdjustmentMap.php new file mode 100644 index 0000000000..fde8f61f7d --- /dev/null +++ b/src/applications/differential/parser/DifferentialLineAdjustmentMap.php @@ -0,0 +1,376 @@ +map; + } + + public function getNearestMap() { + if ($this->nearestMap === null) { + $this->buildNearestMap(); + } + + return $this->nearestMap; + } + + public function getFinalOffset() { + // Make sure we've built this map already. + $this->getNearestMap(); + return $this->finalOffset; + } + + + /** + * Add a map to the end of the chain. + * + * When a line is mapped with @{method:mapLine}, it is mapped through all + * maps in the chain. + */ + public function addMapToChain(DifferentialLineAdjustmentMap $map) { + if ($this->nextMapInChain) { + $this->nextMapInChain->addMapToChain($map); + } else { + $this->nextMapInChain = $map; + } + return $this; + } + + + /** + * Map a line across a change, or a series of changes. + * + * @param int Line to map + * @param bool True to map it as the end of a range. + * @return wild Spooky magic. + */ + public function mapLine($line, $is_end) { + $nmap = $this->getNearestMap(); + + $deleted = false; + $offset = false; + if (isset($nmap[$line])) { + $line_range = $nmap[$line]; + if ($is_end) { + $to_line = end($line_range); + } else { + $to_line = reset($line_range); + } + if ($to_line <= 0) { + // If we're tracing the first line and this block is collapsing, + // compute the offset from the top of the block. + if (!$is_end && $this->isInverse) { + $offset = 0; + $cursor = $line - 1; + while (isset($nmap[$cursor])) { + $prev = $nmap[$cursor]; + $prev = reset($prev); + if ($prev == $to_line) { + $offset++; + } else { + break; + } + $cursor--; + } + } + + $to_line = -$to_line; + if (!$this->isInverse) { + $deleted = true; + } + } + $line = $to_line; + } else { + $line = $line + $this->finalOffset; + } + + if ($this->nextMapInChain) { + $chain = $this->nextMapInChain->mapLine($line, $is_end); + list($chain_deleted, $chain_offset, $line) = $chain; + $deleted = ($deleted || $chain_deleted); + if ($chain_offset !== false) { + if ($offset === false) { + $offset = 0; + } + $offset += $chain_offset; + } + } + + return array($deleted, $offset, $line); + } + + + /** + * Build a derived map which maps deleted lines to the nearest valid line. + * + * This computes a "nearest line" map and a final-line offset. These + * derived maps allow us to map deleted code to the previous (or next) line + * which actually exists. + */ + private function buildNearestMap() { + $map = $this->map; + $nmap = array(); + + $nearest = 0; + foreach ($map as $key => $value) { + if ($value) { + $nmap[$key] = $value; + $nearest = end($value); + } else { + $nmap[$key][0] = -$nearest; + } + } + + if (isset($key)) { + $this->finalOffset = ($nearest - $key); + } else { + $this->finalOffset = 0; + } + + foreach (array_reverse($map, true) as $key => $value) { + if ($value) { + $nearest = reset($value); + } else { + $nmap[$key][1] = -$nearest; + } + } + + $this->nearestMap = $nmap; + + return $this; + } + + public static function newFromHunks(array $hunks) { + assert_instances_of($hunks, 'DifferentialHunk'); + + $map = array(); + $o = 0; + $n = 0; + + $hunks = msort($hunks, 'getOldOffset'); + foreach ($hunks as $hunk) { + + // If the hunks are disjoint, add the implied missing lines where + // nothing changed. + $min = ($hunk->getOldOffset() - 1); + while ($o < $min) { + $o++; + $n++; + $map[$o][] = $n; + } + + $lines = $hunk->getStructuredLines(); + foreach ($lines as $line) { + switch ($line['type']) { + case '-': + $o++; + $map[$o] = array(); + break; + case '+': + $n++; + $map[$o][] = $n; + break; + case ' ': + $o++; + $n++; + $map[$o][] = $n; + break; + default: + break; + } + } + } + + $map = self::reduceMapRanges($map); + + return self::newFromMap($map); + } + + public static function newFromMap(array $map) { + $obj = new DifferentialLineAdjustmentMap(); + $obj->map = $map; + return $obj; + } + + public static function newInverseMap(DifferentialLineAdjustmentMap $map) { + $old = $map->getMap(); + $inv = array(); + $last = 0; + foreach ($old as $k => $v) { + if (count($v) > 1) { + $v = range(reset($v), end($v)); + } + if ($k == 0) { + foreach ($v as $line) { + $inv[$line] = array(); + $last = $line; + } + } else if ($v) { + $first = true; + foreach ($v as $line) { + if ($first) { + $first = false; + $inv[$line][] = $k; + $last = $line; + } else { + $inv[$line] = array(); + } + } + } else { + $inv[$last][] = $k; + } + } + + $inv = self::reduceMapRanges($inv); + + $obj = new DifferentialLineAdjustmentMap(); + $obj->map = $inv; + $obj->isInverse = !$map->isInverse; + return $obj; + } + + private static function reduceMapRanges(array $map) { + foreach ($map as $key => $values) { + if (count($values) > 2) { + $map[$key] = array(reset($values), end($values)); + } + } + return $map; + } + + + public static function loadMaps(array $maps) { + $keys = array(); + foreach ($maps as $map) { + list($u, $v) = $map; + $keys[self::getCacheKey($u, $v)] = $map; + } + + $cache = new PhabricatorKeyValueDatabaseCache(); + $cache = new PhutilKeyValueCacheProfiler($cache); + $cache->setProfiler(PhutilServiceProfiler::getInstance()); + + $results = array(); + + if ($keys) { + $caches = $cache->getKeys(array_keys($keys)); + foreach ($caches as $key => $value) { + list($u, $v) = $keys[$key]; + try { + $results[$u][$v] = self::newFromMap( + phutil_json_decode($value)); + } catch (Exception $ex) { + // Ignore, rebuild below. + } + unset($keys[$key]); + } + } + + if ($keys) { + $built = self::buildMaps($maps); + + $write = array(); + foreach ($built as $u => $list) { + foreach ($list as $v => $map) { + $write[self::getCacheKey($u, $v)] = json_encode($map->getMap()); + $results[$u][$v] = $map; + } + } + + $cache->setKeys($write); + } + + return $results; + } + + private static function buildMaps(array $maps) { + $need = array(); + foreach ($maps as $map) { + list($u, $v) = $map; + $need[$u] = $u; + $need[$v] = $v; + } + + if ($need) { + $changesets = id(new DifferentialChangesetQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withIDs($need) + ->needHunks(true) + ->execute(); + $changesets = mpull($changesets, null, 'getID'); + } + + $results = array(); + foreach ($maps as $map) { + list($u, $v) = $map; + $u_set = idx($changesets, $u); + $v_set = idx($changesets, $v); + + if (!$u_set || !$v_set) { + continue; + } + + // This is the simple case. + if ($u == $v) { + $results[$u][$v] = self::newFromHunks( + $u_set->getHunks()); + continue; + } + + $u_old = $u_set->makeOldFile(); + $v_old = $v_set->makeOldFile(); + + // No difference between the two left sides. + if ($u_old == $v_old) { + $results[$u][$v] = self::newFromMap( + array()); + continue; + } + + // If we're missing context, this won't currently work. We can + // make this case work, but it's fairly rare. + $u_hunks = $u_set->getHunks(); + $v_hunks = $v_set->getHunks(); + if (count($u_hunks) != 1 || + count($v_hunks) != 1 || + head($u_hunks)->getOldOffset() != 1 || + head($u_hunks)->getNewOffset() != 1 || + head($v_hunks)->getOldOffset() != 1 || + head($v_hunks)->getNewOffset() != 1) { + continue; + } + + $changeset = id(new PhabricatorDifferenceEngine()) + ->setIgnoreWhitespace(true) + ->generateChangesetFromFileContent($u_old, $v_old); + + $results[$u][$v] = self::newFromHunks( + $changeset->getHunks()); + } + + return $results; + } + + private static function getCacheKey($u, $v) { + return 'diffadjust.v1('.$u.','.$v.')'; + } + +} diff --git a/src/applications/differential/query/DifferentialInlineCommentQuery.php b/src/applications/differential/query/DifferentialInlineCommentQuery.php index 711c638bf1..3fb26739c0 100644 --- a/src/applications/differential/query/DifferentialInlineCommentQuery.php +++ b/src/applications/differential/query/DifferentialInlineCommentQuery.php @@ -323,6 +323,7 @@ final class DifferentialInlineCommentQuery 'new' => $is_new, 'reason' => $reason, 'href' => $href, + 'originalID' => $changeset->getID(), )); $results[] = $inline; @@ -348,6 +349,107 @@ final class DifferentialInlineCommentQuery } } + // Adjust inline line numbers to account for content changes across + // updates and rebases. + $plan = array(); + $need = array(); + foreach ($results as $inline) { + $ghost = $inline->getIsGhost(); + if (!$ghost) { + // If this isn't a "ghost" inline, ignore it. + continue; + } + + $src_id = $ghost['originalID']; + $dst_id = $inline->getChangesetID(); + + $xforms = array(); + + // If the comment is on the right, transform it through the inverse map + // back to the left. + if ($inline->getIsNewFile()) { + $xforms[] = array($src_id, $src_id, true); + } + + // Transform it across rebases. + $xforms[] = array($src_id, $dst_id, false); + + // If the comment is on the right, transform it back onto the right. + if ($inline->getIsNewFile()) { + $xforms[] = array($dst_id, $dst_id, false); + } + + $key = array(); + foreach ($xforms as $xform) { + list($u, $v, $inverse) = $xform; + + $short = $u.'/'.$v; + $need[$short] = array($u, $v); + + $part = $u.($inverse ? '<' : '>').$v; + $key[] = $part; + } + $key = implode(',', $key); + + if (empty($plan[$key])) { + $plan[$key] = array( + 'xforms' => $xforms, + 'inlines' => array(), + ); + } + + $plan[$key]['inlines'][] = $inline; + } + + if ($need) { + $maps = DifferentialLineAdjustmentMap::loadMaps($need); + } else { + $maps = array(); + } + + foreach ($plan as $step) { + $xforms = $step['xforms']; + + $chain = null; + foreach ($xforms as $xform) { + list($u, $v, $inverse) = $xform; + $map = idx(idx($maps, $u, array()), $v); + if (!$map) { + continue 2; + } + + if ($inverse) { + $map = DifferentialLineAdjustmentMap::newInverseMap($map); + } else { + $map = clone $map; + } + + if ($chain) { + $chain->addMapToChain($map); + } else { + $chain = $map; + } + } + + foreach ($step['inlines'] as $inline) { + $head_line = $inline->getLineNumber(); + $tail_line = ($head_line + $inline->getLineLength()); + + $head_info = $chain->mapLine($head_line, false); + $tail_info = $chain->mapLine($tail_line, true); + + list($head_deleted, $head_offset, $head_line) = $head_info; + list($tail_deleted, $tail_offset, $tail_line) = $tail_info; + + if ($head_offset !== false) { + $inline->setLineNumber($head_line + 1 + $head_offset); + } else { + $inline->setLineNumber($head_line); + $inline->setLineLength($tail_line - $head_line); + } + } + } + return $results; } diff --git a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php index a9eef41ac8..3eb0190664 100644 --- a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php @@ -278,6 +278,7 @@ final class DifferentialChangesetTwoUpRenderer $scaffold->addInlineView($companion); unset($new_comments[$n_num][$key]); + break; } } } diff --git a/src/applications/differential/storage/DifferentialHunk.php b/src/applications/differential/storage/DifferentialHunk.php index f22ba59d2b..bd13cadfec 100644 --- a/src/applications/differential/storage/DifferentialHunk.php +++ b/src/applications/differential/storage/DifferentialHunk.php @@ -117,7 +117,7 @@ abstract class DifferentialHunk extends DifferentialDAO return $this->splitLines; } - private function getStructuredLines() { + public function getStructuredLines() { if ($this->structuredLines === null) { $lines = $this->getSplitLines(); diff --git a/src/applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php b/src/applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php new file mode 100644 index 0000000000..8fc28953fc --- /dev/null +++ b/src/applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php @@ -0,0 +1,294 @@ + array(1), + 2 => array(2), + 3 => array(3), + 4 => array(), + 5 => array(), + 6 => array(), + 7 => array(4), + 8 => array(5), + 9 => array(6), + 10 => array(7), + 11 => array(8), + 12 => array(9), + 13 => array(10), + 14 => array(11), + 15 => array(12), + 16 => array(13), + 17 => array(14), + 18 => array(15), + 19 => array(16), + 20 => array(17, 20), + 21 => array(21), + 22 => array(22), + 23 => array(23), + 24 => array(24), + 25 => array(25), + 26 => array(26), + ); + + $hunks = $this->loadHunks('add.diff'); + $this->assertEqual( + array( + 0 => array(1, 26), + ), + DifferentialLineAdjustmentMap::newFromHunks($hunks)->getMap()); + + $hunks = $this->loadHunks('change.diff'); + $this->assertEqual( + $change_map, + DifferentialLineAdjustmentMap::newFromHunks($hunks)->getMap()); + + $hunks = $this->loadHunks('remove.diff'); + $this->assertEqual( + array_fill_keys(range(1, 26), array()), + DifferentialLineAdjustmentMap::newFromHunks($hunks)->getMap()); + + // With the contextless diff, we don't get the last few similar lines + // in the map. + $reduced_map = $change_map; + unset($reduced_map[24]); + unset($reduced_map[25]); + unset($reduced_map[26]); + + $hunks = $this->loadHunks('context.diff'); + $this->assertEqual( + $reduced_map, + DifferentialLineAdjustmentMap::newFromHunks($hunks)->getMap()); + } + + + public function testInverseMaps() { + $change_map = array( + 1 => array(1), + 2 => array(2), + 3 => array(3, 6), + 4 => array(7), + 5 => array(8), + 6 => array(9), + 7 => array(10), + 8 => array(11), + 9 => array(12), + 10 => array(13), + 11 => array(14), + 12 => array(15), + 13 => array(16), + 14 => array(17), + 15 => array(18), + 16 => array(19), + 17 => array(20), + 18 => array(), + 19 => array(), + 20 => array(), + 21 => array(21), + 22 => array(22), + 23 => array(23), + 24 => array(24), + 25 => array(25), + 26 => array(26), + ); + + $hunks = $this->loadHunks('add.diff'); + $this->assertEqual( + array_fill_keys(range(1, 26), array()), + DifferentialLineAdjustmentMap::newInverseMap( + DifferentialLineAdjustmentMap::newFromHunks($hunks))->getMap()); + + $hunks = $this->loadHunks('change.diff'); + $this->assertEqual( + $change_map, + DifferentialLineAdjustmentMap::newInverseMap( + DifferentialLineAdjustmentMap::newFromHunks($hunks))->getMap()); + + $hunks = $this->loadHunks('remove.diff'); + $this->assertEqual( + array( + 0 => array(1, 26), + ), + DifferentialLineAdjustmentMap::newInverseMap( + DifferentialLineAdjustmentMap::newFromHunks($hunks))->getMap()); + + // With the contextless diff, we don't get the last few similar lines + // in the map. + $reduced_map = $change_map; + unset($reduced_map[24]); + unset($reduced_map[25]); + unset($reduced_map[26]); + + $hunks = $this->loadHunks('context.diff'); + $this->assertEqual( + $reduced_map, + DifferentialLineAdjustmentMap::newInverseMap( + DifferentialLineAdjustmentMap::newFromHunks($hunks))->getMap()); + } + + + public function testNearestMaps() { + $change_map = array( + 1 => array(1), + 2 => array(2), + 3 => array(3), + 4 => array(-3, -4), + 5 => array(-3, -4), + 6 => array(-3, -4), + 7 => array(4), + 8 => array(5), + 9 => array(6), + 10 => array(7), + 11 => array(8), + 12 => array(9), + 13 => array(10), + 14 => array(11), + 15 => array(12), + 16 => array(13), + 17 => array(14), + 18 => array(15), + 19 => array(16), + 20 => array(17, 20), + 21 => array(21), + 22 => array(22), + 23 => array(23), + 24 => array(24), + 25 => array(25), + 26 => array(26), + ); + + $hunks = $this->loadHunks('add.diff'); + $map = DifferentialLineAdjustmentMap::newFromHunks($hunks); + $this->assertEqual( + array( + 0 => array(1, 26), + ), + $map->getNearestMap()); + $this->assertEqual(26, $map->getFinalOffset()); + + + $hunks = $this->loadHunks('change.diff'); + $map = DifferentialLineAdjustmentMap::newFromHunks($hunks); + $this->assertEqual( + $change_map, + $map->getNearestMap()); + $this->assertEqual(0, $map->getFinalOffset()); + + + $hunks = $this->loadHunks('remove.diff'); + $map = DifferentialLineAdjustmentMap::newFromHunks($hunks); + $this->assertEqual( + array_fill_keys( + range(1, 26), + array(0, 0)), + $map->getNearestMap()); + $this->assertEqual(-26, $map->getFinalOffset()); + + + $reduced_map = $change_map; + unset($reduced_map[24]); + unset($reduced_map[25]); + unset($reduced_map[26]); + + $hunks = $this->loadHunks('context.diff'); + $map = DifferentialLineAdjustmentMap::newFromHunks($hunks); + $this->assertEqual( + $reduced_map, + $map->getNearestMap()); + $this->assertEqual(0, $map->getFinalOffset()); + + + $hunks = $this->loadHunks('insert.diff'); + $map = DifferentialLineAdjustmentMap::newFromHunks($hunks); + $this->assertEqual( + array( + 1 => array(1), + 2 => array(2), + 3 => array(3), + 4 => array(4), + 5 => array(5), + 6 => array(6), + 7 => array(7), + 8 => array(8), + 9 => array(9), + 10 => array(10, 13), + 11 => array(14), + 12 => array(15), + 13 => array(16), + ), + $map->getNearestMap()); + $this->assertEqual(3, $map->getFinalOffset()); + } + + + public function testChainMaps() { + // This test simulates porting inlines forward across a rebase. + // Part 1 is the original diff. + // Part 2 is the rebase, which we would normally compute synthetically. + // Part 3 is the updated diff against the rebased changes. + + $diff1 = $this->loadHunks('chain.adjust.1.diff'); + $diff2 = $this->loadHunks('chain.adjust.2.diff'); + $diff3 = $this->loadHunks('chain.adjust.3.diff'); + + $map = DifferentialLineAdjustmentMap::newInverseMap( + DifferentialLineAdjustmentMap::newFromHunks($diff1)); + + $map->addMapToChain( + DifferentialLineAdjustmentMap::newFromHunks($diff2)); + + $map->addMapToChain( + DifferentialLineAdjustmentMap::newFromHunks($diff3)); + + $actual = array(); + for ($ii = 1; $ii <= 13; $ii++) { + $actual[$ii] = array( + $map->mapLine($ii, false), + $map->mapLine($ii, true), + ); + } + + $this->assertEqual( + array( + 1 => array(array(false, false, 1), array(false, false, 1)), + 2 => array(array(true, false, 1), array(true, false, 2)), + 3 => array(array(true, false, 1), array(true, false, 2)), + 4 => array(array(false, false, 2), array(false, false, 2)), + 5 => array(array(false, false, 3), array(false, false, 3)), + 6 => array(array(false, false, 4), array(false, false, 4)), + 7 => array(array(false, false, 5), array(false, false, 8)), + 8 => array(array(false, 0, 5), array(false, false, 9)), + 9 => array(array(false, 1, 5), array(false, false, 9)), + 10 => array(array(false, 2, 5), array(false, false, 9)), + 11 => array(array(false, false, 9), array(false, false, 9)), + 12 => array(array(false, false, 10), array(false, false, 10)), + 13 => array(array(false, false, 11), array(false, false, 11)), + ), + $actual); + } + + + private function loadHunks($name) { + $root = dirname(__FILE__).'/map/'; + $data = Filesystem::readFile($root.$name); + + $parser = new ArcanistDiffParser(); + $changes = $parser->parseDiff($data); + + $viewer = PhabricatorUser::getOmnipotentUser(); + $diff = DifferentialDiff::newFromRawChanges($viewer, $changes); + + $changesets = $diff->getChangesets(); + if (count($changesets) !== 1) { + throw new Exception( + pht( + 'Expected exactly one changeset from "%s".', + $name)); + } + $changeset = head($changesets); + + return $changeset->getHunks(); + } + +} diff --git a/src/applications/differential/storage/__tests__/map/add.diff b/src/applications/differential/storage/__tests__/map/add.diff new file mode 100644 index 0000000000..97e60a8b7c --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/add.diff @@ -0,0 +1,32 @@ +diff --git a/alphabet b/alphabet +new file mode 100644 +index 0000000..0edb856 +--- /dev/null ++++ b/alphabet +@@ -0,0 +1,26 @@ ++a ++b ++c ++d ++e ++f ++g ++h ++i ++j ++k ++l ++m ++n ++o ++p ++q ++r ++s ++t ++u ++v ++w ++x ++y ++z diff --git a/src/applications/differential/storage/__tests__/map/chain.adjust.1.diff b/src/applications/differential/storage/__tests__/map/chain.adjust.1.diff new file mode 100644 index 0000000000..8370a66e1a --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/chain.adjust.1.diff @@ -0,0 +1,14 @@ +diff --git a/alphabet b/alphabet +index 92dfa21..292798b 100644 +--- a/alphabet ++++ b/alphabet +@@ -5,6 +5,9 @@ d + e + f + g ++G1 ++G2 ++G3 + h + i + j diff --git a/src/applications/differential/storage/__tests__/map/chain.adjust.2.diff b/src/applications/differential/storage/__tests__/map/chain.adjust.2.diff new file mode 100644 index 0000000000..ac6f8c854a --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/chain.adjust.2.diff @@ -0,0 +1,11 @@ +diff --git a/alphabet b/alphabet +index 92dfa21..e3344af 100644 +--- a/alphabet ++++ b/alphabet +@@ -1,6 +1,4 @@ + a +-b +-c + d + e + f diff --git a/src/applications/differential/storage/__tests__/map/chain.adjust.3.diff b/src/applications/differential/storage/__tests__/map/chain.adjust.3.diff new file mode 100644 index 0000000000..4d23f185fd --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/chain.adjust.3.diff @@ -0,0 +1,14 @@ +diff --git a/alphabet b/alphabet +index e3344af..febfe3e 100644 +--- a/alphabet ++++ b/alphabet +@@ -3,6 +3,9 @@ d + e + f + g ++G1x ++G2x ++G3x + h + i + j diff --git a/src/applications/differential/storage/__tests__/map/change.diff b/src/applications/differential/storage/__tests__/map/change.diff new file mode 100644 index 0000000000..7ef945267f --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/change.diff @@ -0,0 +1,34 @@ +diff --git a/alphabet b/alphabet +index 0edb856..2449de2 100644 +--- a/alphabet ++++ b/alphabet +@@ -1,26 +1,26 @@ + a + b + c +-d +-e +-f + g + h + i + j + k + l + m + n + o + p + q + r + s + t ++tx ++ty ++tz + u + v + w + x + y + z diff --git a/src/applications/differential/storage/__tests__/map/context.diff b/src/applications/differential/storage/__tests__/map/context.diff new file mode 100644 index 0000000000..ab77e4a9ba --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/context.diff @@ -0,0 +1,24 @@ +diff --git a/alphabet b/alphabet +index 0edb856..2449de2 100644 +--- a/alphabet ++++ b/alphabet +@@ -1,9 +1,6 @@ + a + b + c +-d +-e +-f + g + h + i +@@ -18,6 +15,9 @@ q + r + s + t ++tx ++ty ++tz + u + v + w diff --git a/src/applications/differential/storage/__tests__/map/insert.diff b/src/applications/differential/storage/__tests__/map/insert.diff new file mode 100644 index 0000000000..9a726955e7 --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/insert.diff @@ -0,0 +1,14 @@ +diff --git a/alphabet b/alphabet +index f2b41ef..755b349 100644 +--- a/alphabet ++++ b/alphabet +@@ -8,6 +8,9 @@ g + h + i + j ++j1 ++j2 ++j3 + k + l + n diff --git a/src/applications/differential/storage/__tests__/map/remove.diff b/src/applications/differential/storage/__tests__/map/remove.diff new file mode 100644 index 0000000000..0feafbbfc3 --- /dev/null +++ b/src/applications/differential/storage/__tests__/map/remove.diff @@ -0,0 +1,32 @@ +diff --git a/alphabet b/alphabet +deleted file mode 100644 +index 2449de2..0000000 +--- a/alphabet ++++ /dev/null +@@ -1,26 +0,0 @@ +-a +-b +-c +-g +-h +-i +-j +-k +-l +-m +-n +-o +-p +-q +-r +-s +-t +-tx +-ty +-tz +-u +-v +-w +-x +-y +-z