KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_text_diff.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21
22#include <git/text_diff.h>
23
26
27BOOST_AUTO_TEST_SUITE( GitTextDiff )
28
29
30static wxString render( const std::vector<TEXT_DIFF_LINE>& aLines )
31{
32 wxString out;
33
34 for( const TEXT_DIFF_LINE& line : aLines )
35 {
36 switch( line.kind )
37 {
38 case TEXT_DIFF_KIND::CONTEXT: out << wxS( " " ); break;
39 case TEXT_DIFF_KIND::ADDED: out << wxS( "+" ); break;
40 case TEXT_DIFF_KIND::REMOVED: out << wxS( "-" ); break;
41 }
42
43 out << line.text << wxS( "|" );
44 }
45
46 return out;
47}
48
49
50BOOST_AUTO_TEST_CASE( IdenticalFilesAreAllContext )
51{
52 std::vector<wxString> lines = { wxT( "a" ), wxT( "b" ) };
53
54 BOOST_CHECK_EQUAL( render( KIGIT::BuildTextDiff( lines, lines ) ), wxString( wxT( " a| b|" ) ) );
55}
56
57
58BOOST_AUTO_TEST_CASE( ChangedLineIsRemovedThenAdded )
59{
60 std::vector<wxString> oldLines = { wxT( "a" ), wxT( "old" ), wxT( "c" ) };
61 std::vector<wxString> newLines = { wxT( "a" ), wxT( "new" ), wxT( "c" ) };
62
63 BOOST_CHECK_EQUAL( render( KIGIT::BuildTextDiff( oldLines, newLines ) ), wxString( wxT( " a|-old|+new| c|" ) ) );
64}
65
66
67BOOST_AUTO_TEST_CASE( InsertionAndDeletionKeepSurroundingContext )
68{
69 std::vector<wxString> oldLines = { wxT( "a" ), wxT( "b" ), wxT( "c" ) };
70 std::vector<wxString> inserted = { wxT( "a" ), wxT( "b" ), wxT( "x" ), wxT( "c" ) };
71 std::vector<wxString> removed = { wxT( "a" ), wxT( "c" ) };
72
73 BOOST_CHECK_EQUAL( render( KIGIT::BuildTextDiff( oldLines, inserted ) ), wxString( wxT( " a| b|+x| c|" ) ) );
74 BOOST_CHECK_EQUAL( render( KIGIT::BuildTextDiff( oldLines, removed ) ), wxString( wxT( " a|-b| c|" ) ) );
75}
76
77
78BOOST_AUTO_TEST_CASE( EmptySidesShowWholeFile )
79{
80 std::vector<wxString> lines = { wxT( "a" ), wxT( "b" ) };
81
82 BOOST_CHECK_EQUAL( render( KIGIT::BuildTextDiff( {}, lines ) ), wxString( wxT( "+a|+b|" ) ) );
83 BOOST_CHECK_EQUAL( render( KIGIT::BuildTextDiff( lines, {} ) ), wxString( wxT( "-a|-b|" ) ) );
84 BOOST_CHECK( KIGIT::BuildTextDiff( {}, {} ).empty() );
85}
86
87
88BOOST_AUTO_TEST_CASE( SplitHandlesLineEndings )
89{
90 std::vector<wxString> unix = KIGIT::SplitTextLines( "a\nb\n" );
91 std::vector<wxString> dos = KIGIT::SplitTextLines( "a\r\nb\r\n" );
92 std::vector<wxString> noFinalNewline = KIGIT::SplitTextLines( "a\nb" );
93
94 BOOST_REQUIRE_EQUAL( unix.size(), 2 );
95 BOOST_CHECK_EQUAL( unix[0], wxT( "a" ) );
96 BOOST_CHECK_EQUAL( unix[1], wxT( "b" ) );
97
98 BOOST_REQUIRE_EQUAL( dos.size(), 2 );
99 BOOST_CHECK_EQUAL( dos[0], wxT( "a" ) );
100 BOOST_CHECK_EQUAL( dos[1], wxT( "b" ) );
101
102 BOOST_REQUIRE_EQUAL( noFinalNewline.size(), 2 );
103 BOOST_CHECK_EQUAL( noFinalNewline[0], wxT( "a" ) );
104 BOOST_CHECK_EQUAL( noFinalNewline[1], wxT( "b" ) );
105
106 BOOST_CHECK( KIGIT::SplitTextLines( "" ).empty() );
107}
108
109
110// A tall narrow pair exercises the row-major indexing of the table. A line shared between the
111// two sides must be found whichever side is the tall one.
112BOOST_AUTO_TEST_CASE( TallNarrowTableFindsSharedLine )
113{
114 std::vector<wxString> many;
115 std::vector<wxString> one = { wxT( "only" ) };
116
117 for( int ii = 0; ii < 500; ii++ )
118 many.push_back( ii == 250 ? wxString( wxT( "only" ) ) : wxString::Format( wxT( "line %d" ), ii ) );
119
120 std::vector<TEXT_DIFF_LINE> removed = KIGIT::BuildTextDiff( many, one );
121 std::vector<TEXT_DIFF_LINE> added = KIGIT::BuildTextDiff( one, many );
122
123 for( const std::vector<TEXT_DIFF_LINE>& diff : { removed, added } )
124 {
125 int context = 0;
126
127 for( const TEXT_DIFF_LINE& line : diff )
128 {
129 if( line.kind == TEXT_DIFF_KIND::CONTEXT )
130 {
131 context++;
132 BOOST_CHECK_EQUAL( line.text, wxString( wxT( "only" ) ) );
133 }
134 }
135
136 BOOST_CHECK_EQUAL( diff.size(), many.size() );
137 BOOST_CHECK_EQUAL( context, 1 );
138 }
139}
140
141
142// A shared line in the middle of an otherwise different pair is kept as context.
143BOOST_AUTO_TEST_CASE( SharedLineInTheMiddleIsContext )
144{
145 std::vector<wxString> left = { wxT( "a" ), wxT( "shared" ), wxT( "b" ) };
146 std::vector<wxString> right = { wxT( "c" ), wxT( "shared" ), wxT( "d" ) };
147
148 int context = 0;
149
150 for( const TEXT_DIFF_LINE& line : KIGIT::BuildTextDiff( left, right ) )
151 {
152 if( line.kind == TEXT_DIFF_KIND::CONTEXT )
153 {
154 context++;
155 BOOST_CHECK_EQUAL( line.text, wxString( wxT( "shared" ) ) );
156 }
157 }
158
159 BOOST_CHECK_EQUAL( context, 1 );
160}
161
162
163BOOST_AUTO_TEST_CASE( BinarySniffFindsNulByte )
164{
165 BOOST_CHECK( KIGIT::TextLooksBinary( std::string( "PK\x03\x04\0\x06", 6 ) ) );
166 BOOST_CHECK( !KIGIT::TextLooksBinary( "plain text\nwith lines\n" ) );
167 BOOST_CHECK( !KIGIT::TextLooksBinary( "" ) );
168
169 // Only the head is read, so a NUL past the probe does not make the file binary.
170 BOOST_CHECK( !KIGIT::TextLooksBinary( std::string( 9000, 'x' ) + std::string( 1, '\0' ) ) );
171}
172
173
174// Past the cell cap the diff stops looking for shared lines and lists one side then the other.
175// 2001 against 2001 is the smallest square pair that exceeds it. The shared line in the middle
176// is what tells the two paths apart: the table would report it as context, the fallback cannot.
177BOOST_AUTO_TEST_CASE( OversizedPairFallsBackToWholeFileDiff )
178{
179 std::vector<wxString> oldLines;
180 std::vector<wxString> newLines;
181
182 for( int ii = 0; ii < 2001; ii++ )
183 {
184 oldLines.push_back( ii == 1000 ? wxString( wxT( "shared" ) ) : wxString::Format( wxT( "old %d" ), ii ) );
185 newLines.push_back( ii == 1000 ? wxString( wxT( "shared" ) ) : wxString::Format( wxT( "new %d" ), ii ) );
186 }
187
188 std::vector<TEXT_DIFF_LINE> diff = KIGIT::BuildTextDiff( oldLines, newLines );
189
190 BOOST_REQUIRE_EQUAL( diff.size(), oldLines.size() + newLines.size() );
191
192 for( size_t ii = 0; ii < oldLines.size(); ii++ )
193 BOOST_REQUIRE( diff[ii].kind == TEXT_DIFF_KIND::REMOVED );
194
195 for( size_t ii = oldLines.size(); ii < diff.size(); ii++ )
196 BOOST_REQUIRE( diff[ii].kind == TEXT_DIFF_KIND::ADDED );
197}
198
199
static bool empty(const wxTextEntryBase *aCtrl)
TEXT_DIFF_KIND
Definition text_diff.h:37
std::vector< wxString > SplitTextLines(const std::string &aContent)
Tolerates CRLF endings and content that is not valid UTF-8.
Definition text_diff.cpp:28
bool TextLooksBinary(const std::string &aContent)
Definition text_diff.cpp:68
std::vector< TEXT_DIFF_LINE > BuildTextDiff(const std::vector< wxString > &aOld, const std::vector< wxString > &aNew)
Every line of both sides appears once. There are no hunks and nothing is elided.
Definition text_diff.cpp:77
STL namespace.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(IdenticalFilesAreAllContext)
static wxString render(const std::vector< TEXT_DIFF_LINE > &aLines)
BOOST_CHECK_EQUAL(result, "25.4")