Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
yangql
googletest
Commits
b54098a9
Commit
b54098a9
authored
Jul 28, 2014
by
kosak
Browse files
Expand equality failure messages with a by-line diff.
parent
bd263344
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
433 additions
and
5 deletions
+433
-5
include/gtest/internal/gtest-internal.h
include/gtest/internal/gtest-internal.h
+32
-0
src/gtest.cc
src/gtest.cc
+285
-0
test/gtest_output_test_.cc
test/gtest_output_test_.cc
+5
-0
test/gtest_output_test_golden_lin.txt
test/gtest_output_test_golden_lin.txt
+17
-5
test/gtest_unittest.cc
test/gtest_unittest.cc
+94
-0
No files found.
include/gtest/internal/gtest-internal.h
View file @
b54098a9
...
@@ -56,6 +56,8 @@
...
@@ -56,6 +56,8 @@
#include <iomanip>
#include <iomanip>
#include <limits>
#include <limits>
#include <set>
#include <set>
#include <string>
#include <vector>
#include "gtest/gtest-message.h"
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-string.h"
#include "gtest/internal/gtest-string.h"
...
@@ -171,6 +173,36 @@ class GTEST_API_ ScopedTrace {
...
@@ -171,6 +173,36 @@ class GTEST_API_ ScopedTrace {
// c'tor and d'tor. Therefore it doesn't
// c'tor and d'tor. Therefore it doesn't
// need to be used otherwise.
// need to be used otherwise.
namespace
edit_distance
{
// Returns the optimal edits to go from 'left' to 'right'.
// All edits cost the same, with replace having lower priority than
// add/remove.
// Simple implementation of the Wagner–Fischer algorithm.
// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
enum
EditType
{
kMatch
,
kAdd
,
kRemove
,
kReplace
};
GTEST_API_
std
::
vector
<
EditType
>
CalculateOptimalEdits
(
const
std
::
vector
<
size_t
>&
left
,
const
std
::
vector
<
size_t
>&
right
);
// Same as above, but the input is represented as strings.
GTEST_API_
std
::
vector
<
EditType
>
CalculateOptimalEdits
(
const
std
::
vector
<
std
::
string
>&
left
,
const
std
::
vector
<
std
::
string
>&
right
);
// Create a diff of the input strings in Unified diff format.
GTEST_API_
std
::
string
CreateUnifiedDiff
(
const
std
::
vector
<
std
::
string
>&
left
,
const
std
::
vector
<
std
::
string
>&
right
,
size_t
context
=
2
);
}
// namespace edit_distance
// Calculate the diff between 'left' and 'right' and return it in unified diff
// format.
// If not null, stores in 'total_line_count' the total number of lines found
// in left + right.
GTEST_API_
std
::
string
DiffStrings
(
const
std
::
string
&
left
,
const
std
::
string
&
right
,
size_t
*
total_line_count
);
// Constructs and returns the message for an equality assertion
// Constructs and returns the message for an equality assertion
// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
//
//
...
...
src/gtest.cc
View file @
b54098a9
...
@@ -46,6 +46,8 @@
...
@@ -46,6 +46,8 @@
#include <algorithm>
#include <algorithm>
#include <iomanip>
#include <iomanip>
#include <limits>
#include <limits>
#include <list>
#include <map>
#include <ostream> // NOLINT
#include <ostream> // NOLINT
#include <sstream>
#include <sstream>
#include <vector>
#include <vector>
...
@@ -80,6 +82,7 @@
...
@@ -80,6 +82,7 @@
#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE.
#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE.
# include <windows.h> // NOLINT
# include <windows.h> // NOLINT
# undef min
#elif GTEST_OS_WINDOWS // We are on Windows proper.
#elif GTEST_OS_WINDOWS // We are on Windows proper.
...
@@ -102,6 +105,7 @@
...
@@ -102,6 +105,7 @@
// cpplint thinks that the header is already included, so we want to
// cpplint thinks that the header is already included, so we want to
// silence it.
// silence it.
# include <windows.h> // NOLINT
# include <windows.h> // NOLINT
# undef min
#else
#else
...
@@ -981,6 +985,276 @@ AssertionResult AssertionFailure(const Message& message) {
...
@@ -981,6 +985,276 @@ AssertionResult AssertionFailure(const Message& message) {
namespace
internal
{
namespace
internal
{
namespace
edit_distance
{
std
::
vector
<
EditType
>
CalculateOptimalEdits
(
const
std
::
vector
<
size_t
>&
left
,
const
std
::
vector
<
size_t
>&
right
)
{
std
::
vector
<
std
::
vector
<
double
>
>
costs
(
left
.
size
()
+
1
,
std
::
vector
<
double
>
(
right
.
size
()
+
1
));
std
::
vector
<
std
::
vector
<
EditType
>
>
best_move
(
left
.
size
()
+
1
,
std
::
vector
<
EditType
>
(
right
.
size
()
+
1
));
// Populate for empty right.
for
(
size_t
l_i
=
0
;
l_i
<
costs
.
size
();
++
l_i
)
{
costs
[
l_i
][
0
]
=
static_cast
<
double
>
(
l_i
);
best_move
[
l_i
][
0
]
=
kRemove
;
}
// Populate for empty left.
for
(
size_t
r_i
=
1
;
r_i
<
costs
[
0
].
size
();
++
r_i
)
{
costs
[
0
][
r_i
]
=
static_cast
<
double
>
(
r_i
);
best_move
[
0
][
r_i
]
=
kAdd
;
}
for
(
size_t
l_i
=
0
;
l_i
<
left
.
size
();
++
l_i
)
{
for
(
size_t
r_i
=
0
;
r_i
<
right
.
size
();
++
r_i
)
{
if
(
left
[
l_i
]
==
right
[
r_i
])
{
// Found a match. Consume it.
costs
[
l_i
+
1
][
r_i
+
1
]
=
costs
[
l_i
][
r_i
];
best_move
[
l_i
+
1
][
r_i
+
1
]
=
kMatch
;
continue
;
}
const
double
add
=
costs
[
l_i
+
1
][
r_i
];
const
double
remove
=
costs
[
l_i
][
r_i
+
1
];
const
double
replace
=
costs
[
l_i
][
r_i
];
if
(
add
<
remove
&&
add
<
replace
)
{
costs
[
l_i
+
1
][
r_i
+
1
]
=
add
+
1
;
best_move
[
l_i
+
1
][
r_i
+
1
]
=
kAdd
;
}
else
if
(
remove
<
add
&&
remove
<
replace
)
{
costs
[
l_i
+
1
][
r_i
+
1
]
=
remove
+
1
;
best_move
[
l_i
+
1
][
r_i
+
1
]
=
kRemove
;
}
else
{
// We make replace a little more expensive than add/remove to lower
// their priority.
costs
[
l_i
+
1
][
r_i
+
1
]
=
replace
+
1.00001
;
best_move
[
l_i
+
1
][
r_i
+
1
]
=
kReplace
;
}
}
}
// Reconstruct the best path. We do it in reverse order.
std
::
vector
<
EditType
>
best_path
;
for
(
size_t
l_i
=
left
.
size
(),
r_i
=
right
.
size
();
l_i
>
0
||
r_i
>
0
;)
{
EditType
move
=
best_move
[
l_i
][
r_i
];
best_path
.
push_back
(
move
);
l_i
-=
move
!=
kAdd
;
r_i
-=
move
!=
kRemove
;
}
std
::
reverse
(
best_path
.
begin
(),
best_path
.
end
());
return
best_path
;
}
namespace
{
// Helper class to convert string into ids with deduplication.
class
InternalStrings
{
public:
size_t
GetId
(
const
std
::
string
&
str
)
{
IdMap
::
iterator
it
=
ids_
.
find
(
str
);
if
(
it
!=
ids_
.
end
())
return
it
->
second
;
size_t
id
=
ids_
.
size
();
return
ids_
[
str
]
=
id
;
}
private:
typedef
std
::
map
<
std
::
string
,
size_t
>
IdMap
;
IdMap
ids_
;
};
}
// namespace
std
::
vector
<
EditType
>
CalculateOptimalEdits
(
const
std
::
vector
<
std
::
string
>&
left
,
const
std
::
vector
<
std
::
string
>&
right
)
{
std
::
vector
<
size_t
>
left_ids
,
right_ids
;
{
InternalStrings
intern_table
;
for
(
size_t
i
=
0
;
i
<
left
.
size
();
++
i
)
{
left_ids
.
push_back
(
intern_table
.
GetId
(
left
[
i
]));
}
for
(
size_t
i
=
0
;
i
<
right
.
size
();
++
i
)
{
right_ids
.
push_back
(
intern_table
.
GetId
(
right
[
i
]));
}
}
return
CalculateOptimalEdits
(
left_ids
,
right_ids
);
}
namespace
{
// Helper class that holds the state for one hunk and prints it out to the
// stream.
// It reorders adds/removes when possible to group all removes before all
// adds. It also adds the hunk header before printint into the stream.
class
Hunk
{
public:
Hunk
(
size_t
left_start
,
size_t
right_start
)
:
left_start_
(
left_start
),
right_start_
(
right_start
),
adds_
(),
removes_
(),
common_
()
{}
void
PushLine
(
char
edit
,
const
char
*
line
)
{
switch
(
edit
)
{
case
' '
:
++
common_
;
FlushEdits
();
hunk_
.
push_back
(
std
::
make_pair
(
' '
,
line
));
break
;
case
'-'
:
++
removes_
;
hunk_removes_
.
push_back
(
std
::
make_pair
(
'-'
,
line
));
break
;
case
'+'
:
++
adds_
;
hunk_adds_
.
push_back
(
std
::
make_pair
(
'+'
,
line
));
break
;
}
}
void
PrintTo
(
std
::
ostream
*
os
)
{
PrintHeader
(
os
);
FlushEdits
();
for
(
std
::
list
<
std
::
pair
<
char
,
const
char
*>
>::
const_iterator
it
=
hunk_
.
begin
();
it
!=
hunk_
.
end
();
++
it
)
{
*
os
<<
it
->
first
<<
it
->
second
<<
"
\n
"
;
}
}
bool
has_edits
()
const
{
return
adds_
||
removes_
;
}
private:
void
FlushEdits
()
{
hunk_
.
splice
(
hunk_
.
end
(),
hunk_removes_
);
hunk_
.
splice
(
hunk_
.
end
(),
hunk_adds_
);
}
// Print a unified diff header for one hunk.
// The format is
// "@@ -<left_start>,<left_length> +<right_start>,<right_length> @@"
// where the left/right parts are ommitted if unnecessary.
void
PrintHeader
(
std
::
ostream
*
ss
)
const
{
*
ss
<<
"@@ "
;
if
(
removes_
)
{
*
ss
<<
"-"
<<
left_start_
<<
","
<<
(
removes_
+
common_
);
}
if
(
removes_
&&
adds_
)
{
*
ss
<<
" "
;
}
if
(
adds_
)
{
*
ss
<<
"+"
<<
right_start_
<<
","
<<
(
adds_
+
common_
);
}
*
ss
<<
" @@
\n
"
;
}
size_t
left_start_
,
right_start_
;
size_t
adds_
,
removes_
,
common_
;
std
::
list
<
std
::
pair
<
char
,
const
char
*>
>
hunk_
,
hunk_adds_
,
hunk_removes_
;
};
}
// namespace
// Create a list of diff hunks in Unified diff format.
// Each hunk has a header generated by PrintHeader above plus a body with
// lines prefixed with ' ' for no change, '-' for deletion and '+' for
// addition.
// 'context' represents the desired unchanged prefix/suffix around the diff.
// If two hunks are close enough that their contexts overlap, then they are
// joined into one hunk.
std
::
string
CreateUnifiedDiff
(
const
std
::
vector
<
std
::
string
>&
left
,
const
std
::
vector
<
std
::
string
>&
right
,
size_t
context
)
{
const
std
::
vector
<
EditType
>
edits
=
CalculateOptimalEdits
(
left
,
right
);
size_t
l_i
=
0
,
r_i
=
0
,
edit_i
=
0
;
std
::
stringstream
ss
;
while
(
edit_i
<
edits
.
size
())
{
// Find first edit.
while
(
edit_i
<
edits
.
size
()
&&
edits
[
edit_i
]
==
kMatch
)
{
++
l_i
;
++
r_i
;
++
edit_i
;
}
// Find the first line to include in the hunk.
const
size_t
prefix_context
=
std
::
min
(
l_i
,
context
);
Hunk
hunk
(
l_i
-
prefix_context
+
1
,
r_i
-
prefix_context
+
1
);
for
(
size_t
i
=
prefix_context
;
i
>
0
;
--
i
)
{
hunk
.
PushLine
(
' '
,
left
[
l_i
-
i
].
c_str
());
}
// Iterate the edits until we found enough suffix for the hunk or the input
// is over.
size_t
n_suffix
=
0
;
for
(;
edit_i
<
edits
.
size
();
++
edit_i
)
{
if
(
n_suffix
>=
context
)
{
// Continue only if the next hunk is very close.
std
::
vector
<
EditType
>::
const_iterator
it
=
edits
.
begin
()
+
edit_i
;
while
(
it
!=
edits
.
end
()
&&
*
it
==
kMatch
)
++
it
;
if
(
it
==
edits
.
end
()
||
(
it
-
edits
.
begin
())
-
edit_i
>=
context
)
{
// There is no next edit or it is too far away.
break
;
}
}
EditType
edit
=
edits
[
edit_i
];
// Reset count when a non match is found.
n_suffix
=
edit
==
kMatch
?
n_suffix
+
1
:
0
;
if
(
edit
==
kMatch
||
edit
==
kRemove
||
edit
==
kReplace
)
{
hunk
.
PushLine
(
edit
==
kMatch
?
' '
:
'-'
,
left
[
l_i
].
c_str
());
}
if
(
edit
==
kAdd
||
edit
==
kReplace
)
{
hunk
.
PushLine
(
'+'
,
right
[
r_i
].
c_str
());
}
// Advance indices, depending on edit type.
l_i
+=
edit
!=
kAdd
;
r_i
+=
edit
!=
kRemove
;
}
if
(
!
hunk
.
has_edits
())
{
// We are done. We don't want this hunk.
break
;
}
hunk
.
PrintTo
(
&
ss
);
}
return
ss
.
str
();
}
}
// namespace edit_distance
namespace
{
// The string representation of the values received in EqFailure() are already
// escaped. Split them on escaped '\n' boundaries. Leave all other escaped
// characters the same.
std
::
vector
<
std
::
string
>
SplitEscapedString
(
const
std
::
string
&
str
)
{
std
::
vector
<
std
::
string
>
lines
;
size_t
start
=
0
,
end
=
str
.
size
();
if
(
end
>
2
&&
str
[
0
]
==
'"'
&&
str
[
end
-
1
]
==
'"'
)
{
++
start
;
--
end
;
}
bool
escaped
=
false
;
for
(
size_t
i
=
start
;
i
+
1
<
end
;
++
i
)
{
if
(
escaped
)
{
escaped
=
false
;
if
(
str
[
i
]
==
'n'
)
{
lines
.
push_back
(
str
.
substr
(
start
,
i
-
start
-
1
));
start
=
i
+
1
;
}
}
else
{
escaped
=
str
[
i
]
==
'\\'
;
}
}
lines
.
push_back
(
str
.
substr
(
start
,
end
-
start
));
return
lines
;
}
}
// namespace
// Constructs and returns the message for an equality assertion
// Constructs and returns the message for an equality assertion
// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
//
//
...
@@ -1015,6 +1289,17 @@ AssertionResult EqFailure(const char* expected_expression,
...
@@ -1015,6 +1289,17 @@ AssertionResult EqFailure(const char* expected_expression,
msg
<<
"
\n
Which is: "
<<
expected_value
;
msg
<<
"
\n
Which is: "
<<
expected_value
;
}
}
if
(
!
expected_value
.
empty
()
&&
!
actual_value
.
empty
())
{
const
std
::
vector
<
std
::
string
>
expected_lines
=
SplitEscapedString
(
expected_value
);
const
std
::
vector
<
std
::
string
>
actual_lines
=
SplitEscapedString
(
actual_value
);
if
(
expected_lines
.
size
()
>
1
||
actual_lines
.
size
()
>
1
)
{
msg
<<
"
\n
With diff:
\n
"
<<
edit_distance
::
CreateUnifiedDiff
(
expected_lines
,
actual_lines
);
}
}
return
AssertionFailure
()
<<
msg
;
return
AssertionFailure
()
<<
msg
;
}
}
...
...
test/gtest_output_test_.cc
View file @
b54098a9
...
@@ -113,6 +113,11 @@ TEST(NonfatalFailureTest, EscapesStringOperands) {
...
@@ -113,6 +113,11 @@ TEST(NonfatalFailureTest, EscapesStringOperands) {
EXPECT_EQ
(
golden
,
actual
);
EXPECT_EQ
(
golden
,
actual
);
}
}
TEST
(
NonfatalFailureTest
,
DiffForLongStrings
)
{
std
::
string
golden_str
(
kGoldenString
,
sizeof
(
kGoldenString
)
-
1
);
EXPECT_EQ
(
golden_str
,
"Line 2"
);
}
// Tests catching a fatal failure in a subroutine.
// Tests catching a fatal failure in a subroutine.
TEST
(
FatalFailureTest
,
FatalFailureInSubroutine
)
{
TEST
(
FatalFailureTest
,
FatalFailureInSubroutine
)
{
printf
(
"(expecting a failure that x should be 1)
\n
"
);
printf
(
"(expecting a failure that x should be 1)
\n
"
);
...
...
test/gtest_output_test_golden_lin.txt
View file @
b54098a9
...
@@ -7,7 +7,7 @@ Expected: true
...
@@ -7,7 +7,7 @@ Expected: true
gtest_output_test_.cc:#: Failure
gtest_output_test_.cc:#: Failure
Value of: 3
Value of: 3
Expected: 2
Expected: 2
[0;32m[==========] [mRunning 6
3
tests from 28 test cases.
[0;32m[==========] [mRunning 6
4
tests from 28 test cases.
[0;32m[----------] [mGlobal test environment set-up.
[0;32m[----------] [mGlobal test environment set-up.
FooEnvironment::SetUp() called.
FooEnvironment::SetUp() called.
BarEnvironment::SetUp() called.
BarEnvironment::SetUp() called.
...
@@ -31,7 +31,7 @@ BarEnvironment::SetUp() called.
...
@@ -31,7 +31,7 @@ BarEnvironment::SetUp() called.
[0;32m[ OK ] [mPassingTest.PassingTest1
[0;32m[ OK ] [mPassingTest.PassingTest1
[0;32m[ RUN ] [mPassingTest.PassingTest2
[0;32m[ RUN ] [mPassingTest.PassingTest2
[0;32m[ OK ] [mPassingTest.PassingTest2
[0;32m[ OK ] [mPassingTest.PassingTest2
[0;32m[----------] [m
1
test from NonfatalFailureTest
[0;32m[----------] [m
2
test
s
from NonfatalFailureTest
[0;32m[ RUN ] [mNonfatalFailureTest.EscapesStringOperands
[0;32m[ RUN ] [mNonfatalFailureTest.EscapesStringOperands
gtest_output_test_.cc:#: Failure
gtest_output_test_.cc:#: Failure
Value of: actual
Value of: actual
...
@@ -44,6 +44,17 @@ Value of: actual
...
@@ -44,6 +44,17 @@ Value of: actual
Expected: golden
Expected: golden
Which is: "\"Line"
Which is: "\"Line"
[0;31m[ FAILED ] [mNonfatalFailureTest.EscapesStringOperands
[0;31m[ FAILED ] [mNonfatalFailureTest.EscapesStringOperands
[0;32m[ RUN ] [mNonfatalFailureTest.DiffForLongStrings
gtest_output_test_.cc:#: Failure
Value of: "Line 2"
Expected: golden_str
Which is: "\"Line\0 1\"\nLine 2"
With diff:
@@ -1,2 @@
-\"Line\0 1\"
Line 2
[0;31m[ FAILED ] [mNonfatalFailureTest.DiffForLongStrings
[0;32m[----------] [m3 tests from FatalFailureTest
[0;32m[----------] [m3 tests from FatalFailureTest
[0;32m[ RUN ] [mFatalFailureTest.FatalFailureInSubroutine
[0;32m[ RUN ] [mFatalFailureTest.FatalFailureInSubroutine
(expecting a failure that x should be 1)
(expecting a failure that x should be 1)
...
@@ -599,10 +610,11 @@ FooEnvironment::TearDown() called.
...
@@ -599,10 +610,11 @@ FooEnvironment::TearDown() called.
gtest_output_test_.cc:#: Failure
gtest_output_test_.cc:#: Failure
Failed
Failed
Expected fatal failure.
Expected fatal failure.
[0;32m[==========] [m6
3
tests from 28 test cases ran.
[0;32m[==========] [m6
4
tests from 28 test cases ran.
[0;32m[ PASSED ] [m21 tests.
[0;32m[ PASSED ] [m21 tests.
[0;31m[ FAILED ] [m4
2
tests, listed below:
[0;31m[ FAILED ] [m4
3
tests, listed below:
[0;31m[ FAILED ] [mNonfatalFailureTest.EscapesStringOperands
[0;31m[ FAILED ] [mNonfatalFailureTest.EscapesStringOperands
[0;31m[ FAILED ] [mNonfatalFailureTest.DiffForLongStrings
[0;31m[ FAILED ] [mFatalFailureTest.FatalFailureInSubroutine
[0;31m[ FAILED ] [mFatalFailureTest.FatalFailureInSubroutine
[0;31m[ FAILED ] [mFatalFailureTest.FatalFailureInNestedSubroutine
[0;31m[ FAILED ] [mFatalFailureTest.FatalFailureInNestedSubroutine
[0;31m[ FAILED ] [mFatalFailureTest.NonfatalFailureInSubroutine
[0;31m[ FAILED ] [mFatalFailureTest.NonfatalFailureInSubroutine
...
@@ -645,7 +657,7 @@ Expected fatal failure.
...
@@ -645,7 +657,7 @@ Expected fatal failure.
[0;31m[ FAILED ] [mScopedFakeTestPartResultReporterTest.InterceptOnlyCurrentThread
[0;31m[ FAILED ] [mScopedFakeTestPartResultReporterTest.InterceptOnlyCurrentThread
[0;31m[ FAILED ] [mPrintingFailingParams/FailingParamTest.Fails/0, where GetParam() = 2
[0;31m[ FAILED ] [mPrintingFailingParams/FailingParamTest.Fails/0, where GetParam() = 2
4
2
FAILED TESTS
4
3
FAILED TESTS
[0;33m YOU HAVE 1 DISABLED TEST
[0;33m YOU HAVE 1 DISABLED TEST
[mNote: Google Test filter = FatalFailureTest.*:LoggingTest.*
[mNote: Google Test filter = FatalFailureTest.*:LoggingTest.*
...
...
test/gtest_unittest.cc
View file @
b54098a9
...
@@ -282,6 +282,9 @@ using testing::internal::TestEventListenersAccessor;
...
@@ -282,6 +282,9 @@ using testing::internal::TestEventListenersAccessor;
using testing::internal::TestResultAccessor;
using testing::internal::TestResultAccessor;
using testing::internal::UInt32;
using testing::internal::UInt32;
using testing::internal::WideStringToUtf8;
using testing::internal::WideStringToUtf8;
using testing::internal::edit_distance::CalculateOptimalEdits;
using testing::internal::edit_distance::CreateUnifiedDiff;
using testing::internal::edit_distance::EditType;
using testing::internal::kMaxRandomSeed;
using testing::internal::kMaxRandomSeed;
using testing::internal::kTestTypeIdInGoogleTest;
using testing::internal::kTestTypeIdInGoogleTest;
using testing::internal::scoped_ptr;
using testing::internal::scoped_ptr;
...
@@ -3431,6 +3434,79 @@ TEST_F(NoFatalFailureTest, MessageIsStreamable) {
...
@@ -3431,6 +3434,79 @@ TEST_F(NoFatalFailureTest, MessageIsStreamable) {
// Tests non-string assertions.
// Tests non-string assertions.
std::string EditsToString(const std::vector<EditType>& edits) {
std::string out;
for (size_t i = 0; i < edits.size(); ++i) {
static const char kEdits[] = " +-/";
out.append(1, kEdits[edits[i]]);
}
return out;
}
std::vector<size_t> CharsToIndices(const std::string& str) {
std::vector<size_t> out;
for (size_t i = 0; i < str.size(); ++i) {
out.push_back(str[i]);
}
return out;
}
std::vector<std::string> CharsToLines(const std::string& str) {
std::vector<std::string> out;
for (size_t i = 0; i < str.size(); ++i) {
out.push_back(str.substr(i, 1));
}
return out;
}
TEST(EditDistance, TestCases) {
struct Case {
int line;
const char* left;
const char* right;
const char* expected_edits;
const char* expected_diff;
};
static const Case kCases[] = {
// No change.
{__LINE__, "A", "A", " ", ""},
{__LINE__, "ABCDE", "ABCDE", " ", ""},
// Simple adds.
{__LINE__, "X", "XA", " +", "@@ +1,2 @@\n X\n+A\n"},
{__LINE__, "X", "XABCD", " ++++", "@@ +1,5 @@\n X\n+A\n+B\n+C\n+D\n"},
// Simple removes.
{__LINE__, "XA", "X", " -", "@@ -1,2 @@\n X\n-A\n"},
{__LINE__, "XABCD", "X", " ----", "@@ -1,5 @@\n X\n-A\n-B\n-C\n-D\n"},
// Simple replaces.
{__LINE__, "A", "a", "/", "@@ -1,1 +1,1 @@\n-A\n+a\n"},
{__LINE__, "ABCD", "abcd", "////",
"@@ -1,4 +1,4 @@\n-A\n-B\n-C\n-D\n+a\n+b\n+c\n+d\n"},
// Path finding.
{__LINE__, "ABCDEFGH", "ABXEGH1", " -/ - +",
"@@ -1,8 +1,7 @@\n A\n B\n-C\n-D\n+X\n E\n-F\n G\n H\n+1\n"},
{__LINE__, "AAAABCCCC", "ABABCDCDC", "- / + / ",
"@@ -1,9 +1,9 @@\n-A\n A\n-A\n+B\n A\n B\n C\n+D\n C\n-C\n+D\n C\n"},
{__LINE__, "ABCDE", "BCDCD", "- +/",
"@@ -1,5 +1,5 @@\n-A\n B\n C\n D\n-E\n+C\n+D\n"},
{__LINE__, "ABCDEFGHIJKL", "BCDCDEFGJKLJK", "- ++ -- ++",
"@@ -1,4 +1,5 @@\n-A\n B\n+C\n+D\n C\n D\n"
"@@ -6,7 +7,7 @@\n F\n G\n-H\n-I\n J\n K\n L\n+J\n+K\n"},
{}};
for (const Case* c = kCases; c->left; ++c) {
EXPECT_TRUE(c->expected_edits ==
EditsToString(CalculateOptimalEdits(CharsToIndices(c->left),
CharsToIndices(c->right))))
<< "Left <" << c->left << "> Right <" << c->right << "> Edits <"
<< EditsToString(CalculateOptimalEdits(
CharsToIndices(c->left), CharsToIndices(c->right))) << ">";
EXPECT_TRUE(c->expected_diff == CreateUnifiedDiff(CharsToLines(c->left),
CharsToLines(c->right)))
<< "Left <" << c->left << "> Right <" << c->right << "> Diff <"
<< CreateUnifiedDiff(CharsToLines(c->left), CharsToLines(c->right))
<< ">";
}
}
// Tests EqFailure(), used for implementing *EQ* assertions.
// Tests EqFailure(), used for implementing *EQ* assertions.
TEST(AssertionTest, EqFailure) {
TEST(AssertionTest, EqFailure) {
const std::string foo_val("5"), bar_val("6");
const std::string foo_val("5"), bar_val("6");
...
@@ -3481,6 +3557,24 @@ TEST(AssertionTest, EqFailure) {
...
@@ -3481,6 +3557,24 @@ TEST(AssertionTest, EqFailure) {
msg5.c_str());
msg5.c_str());
}
}
TEST(AssertionTest, EqFailureWithDiff) {
const std::string left(
"1\\n2XXX\\n3\\n5\\n6\\n7\\n8\\n9\\n10\\n11\\n12XXX\\n13\\n14\\n15");
const std::string right(
"1\\n2\\n3\\n4\\n5\\n6\\n7\\n8\\n9\\n11\\n12\\n13\\n14");
const std::string msg1(
EqFailure("left", "right", left, right, false).failure_message());
EXPECT_STREQ(
"Value of: right\n"
" Actual: 1\\n2\\n3\\n4\\n5\\n6\\n7\\n8\\n9\\n11\\n12\\n13\\n14\n"
"Expected: left\n"
"Which is: "
"1\\n2XXX\\n3\\n5\\n6\\n7\\n8\\n9\\n10\\n11\\n12XXX\\n13\\n14\\n15\n"
"With diff:\n@@ -1,5 +1,6 @@\n 1\n-2XXX\n+2\n 3\n+4\n 5\n 6\n"
"@@ -7,8 +8,6 @@\n 8\n 9\n-10\n 11\n-12XXX\n+12\n 13\n 14\n-15\n",
msg1.c_str());
}
// Tests AppendUserMessage(), used for implementing the *EQ* macros.
// Tests AppendUserMessage(), used for implementing the *EQ* macros.
TEST(AssertionTest, AppendUserMessage) {
TEST(AssertionTest, AppendUserMessage) {
const std::string foo("foo");
const std::string foo("foo");
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment