#!/usr/bin/env perl # # Copyright (C) 2016 Apple Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; use warnings; use File::Temp; use FindBin; use Test::More; use VCSUtils; use lib File::Spec->catdir($FindBin::Bin, ".."); use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog); my $EXAMPLE_CPP = < #include #include #include unsigned fib(unsigned); unsigned fibPlusFive(unsigned n) { return fib(n) + 5; } unsigned fib(unsigned n) { // Derived from Binet's formula, . assert(n <= std::floor(std::log(std::numeric_limits::max() * std::sqrt(5)) / std::log((1 + std::sqrt(5)) / 2))); if (!n) return 0; if (n == 1) return 1; --n; unsigned a = 0; unsigned b = 1; do { unsigned k = a + b; a = b; b = k; } while (--n); return b; } int main(int argc, const char* argv[]) { std::cout << fib(1) << std::endl; std::cout << fibPlusFive(1) << std::endl; std::cout << fib(5) << std::endl; std::cout << fibPlusFive(5) << std::endl; return 0; } EOF my $EXAMPLE_JAVASCRIPT_WITH_SAME_FUNCTION_DEFINED_TWICE = < "Unified diff with 0 context", inputText => $EXAMPLE_CPP, diffToApply => <. EOF expected => < "Unified diff with 0 context; combine two adjacent functions", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; remove function", inputText => $EXAMPLE_CPP, diffToApply => <. - assert(n <= std::floor(std::log(std::numeric_limits::max() * std::sqrt(5)) / std::log((1 + std::sqrt(5)) / 2))); - if (!n) - return 0; - if (n == 1) - return 1; - --n; - unsigned a = 0; - unsigned b = 1; - do { - unsigned k = a + b; - a = b; - b = k; - } while (--n); - return b; -} - EOF expected => < "Unified diff with 0 context; add function immediately after another function", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; add function at the end of the file", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; rename function", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; replace function", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; move function from the top of the file to the end of the file", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; remove functions with the same name", inputText => $EXAMPLE_JAVASCRIPT_WITH_SAME_FUNCTION_DEFINED_TWICE, diffToApply => < < "Unified diff with 0 context; add function above function with the same name", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 0 context; add function after function with the same name", inputText => $EXAMPLE_CPP, diffToApply => < < "Unified diff with 3 lines of context", inputText => $EXAMPLE_CPP, diffToApply => <. assert(n <= std::floor(std::log(std::numeric_limits::max() * std::sqrt(5)) / std::log((1 + std::sqrt(5)) / 2))); if (!n) return 0; EOF expected => < "Unified diff with 3 lines of context; combine two adjacent functions", inputText => $EXAMPLE_CPP, diffToApply => <. assert(n <= std::floor(std::log(std::numeric_limits::max() * std::sqrt(5)) / std::log((1 + std::sqrt(5)) / 2))); if (!n) EOF expected => < "Unified diff with 3 lines of context; remove function", inputText => $EXAMPLE_CPP, diffToApply => <. - assert(n <= std::floor(std::log(std::numeric_limits::max() * std::sqrt(5)) / std::log((1 + std::sqrt(5)) / 2))); - if (!n) - return 0; - if (n == 1) - return 1; - --n; - unsigned a = 0; - unsigned b = 1; - do { - unsigned k = a + b; - a = b; - b = k; - } while (--n); - return b; -} - int main(int argc, const char* argv[]) { std::cout << fib(1) << std::endl; EOF # Note that this output is expected given that the chunk range includes the removed content plus context lines. # The context lines make the after chunk range (+10, 6) overlap the implementation of fibPlusFive() and main(). expected => < "Unified diff; delete contents of entire file", inputText => $EXAMPLE_CPP, diffToApply => < -#include -#include -#include - -unsigned fib(unsigned); - -unsigned fibPlusFive(unsigned n) -{ - return fib(n) + 5; -} - -unsigned fib(unsigned n) -{ - // Derived from Binet's formula, . - assert(n <= std::floor(std::log(std::numeric_limits::max() * std::sqrt(5)) / std::log((1 + std::sqrt(5)) / 2))); - if (!n) - return 0; - if (n == 1) - return 1; - --n; - unsigned a = 0; - unsigned b = 1; - do { - unsigned k = a + b; - a = b; - b = k; - } while (--n); - return b; -} - -int main(int argc, const char* argv[]) -{ - std::cout << fib(1) << std::endl; - std::cout << fibPlusFive(1) << std::endl; - std::cout << fib(5) << std::endl; - std::cout << fibPlusFive(5) << std::endl; - return 0; -} EOF expected => <&STDOUT") or die "Cannot dup STDOUT: $!"; open(my $savedStderr, ">&STDERR") or die "Cannot dup STDERR: $!"; open(STDOUT, ">", File::Spec->devnull()); open(STDERR, ">", File::Spec->devnull()); &$functionRef(); open(STDOUT, ">&", $savedStdout) or die "Cannot restore stdout: $!"; open(STDERR, ">&", $savedStderr) or die "Cannot restore stderr: $!"; } sub writeFileWithContent($$) { my ($file, $content) = @_; open(FILE, ">", $file) or die "Cannot open $file: $!"; print FILE $content; close(FILE); } my $testCasesCount = @testCaseHashRefs; plan(tests => $testCasesCount); my $temporaryDirectory = File::Temp->newdir(); my $filename = "fib.cpp"; my $originalFile = File::Spec->catfile($temporaryDirectory, $filename); my $patchedFile = File::Spec->catfile($temporaryDirectory, "patched-$filename"); my $diffFile = File::Spec->catfile($temporaryDirectory, "a.diff"); foreach my $testCase (@testCaseHashRefs) { writeFileWithContent($originalFile, $testCase->{inputText}); writeFileWithContent($diffFile, $testCase->{diffToApply}); my $exitCode = exitStatus(system("patch", "-s", "-d", $temporaryDirectory, "-i", $diffFile, "-o", $patchedFile)); die "Failed to apply patch for $testCase->{testName}: $exitCode" if $exitCode; my %delegateHash = ( openDiff => sub () { my $fileHandle; open($fileHandle, "<", \$testCase->{diffToApply}); return $fileHandle; }, openFile => sub () { return unless open(PATCHED_FILE, "<", $patchedFile); return \*PATCHED_FILE; }, openOriginalFile => sub () { my $fileHandle; open($fileHandle, "<", \$testCase->{inputText}); return $fileHandle; }, normalizePath => sub () { return $filename; }, ); my %functionLists; discardOutput(sub { PrepareChangeLog::actuallyGenerateFunctionLists([$filename], \%functionLists, undef, undef, undef, \%delegateHash); }); chomp(my $expected = $testCase->{expected}); is($functionLists{$filename}, $expected, $testCase->{testName}); }