Unverified Commit f8fbe1c8 by Gennadiy Civil Committed by GitHub

Merge branch 'master' into unsigned-wchar

parents 837c2463 447d58b4
...@@ -72,7 +72,8 @@ build_script: ...@@ -72,7 +72,8 @@ build_script:
if ($LastExitCode -ne 0) { if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage" throw "Exec: $ErrorMessage"
} }
& cmake --build . --config $env:configuration $cmake_parallel = if ($env:generator -eq "MinGW Makefiles") {"-j2"} else {"/m"}
& cmake --build . --config $env:configuration -- $cmake_parallel
if ($LastExitCode -ne 0) { if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage" throw "Exec: $ErrorMessage"
} }
......
...@@ -178,6 +178,8 @@ divided into several categories: ...@@ -178,6 +178,8 @@ divided into several categories:
|`Ne(value)` |`argument != value`| |`Ne(value)` |`argument != value`|
|`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| |`IsNull()` |`argument` is a `NULL` pointer (raw or smart).|
|`NotNull()` |`argument` is a non-null pointer (raw or smart).| |`NotNull()` |`argument` is a non-null pointer (raw or smart).|
|`VariantWith<T>(m)` |`argument` is `variant<>` that holds the alternative of
type T with a value matching `m`.|
|`Ref(variable)` |`argument` is a reference to `variable`.| |`Ref(variable)` |`argument` is a reference to `variable`.|
|`TypedEq<type>(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| |`TypedEq<type>(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.|
......
...@@ -5655,5 +5655,69 @@ TEST(UnorderedPointwiseTest, AllowsMonomorphicInnerMatcher) { ...@@ -5655,5 +5655,69 @@ TEST(UnorderedPointwiseTest, AllowsMonomorphicInnerMatcher) {
EXPECT_THAT(lhs, UnorderedPointwise(m2, rhs)); EXPECT_THAT(lhs, UnorderedPointwise(m2, rhs));
} }
class SampleVariantIntString {
public:
SampleVariantIntString(int i) : i_(i), has_int_(true) {}
SampleVariantIntString(const std::string& s) : s_(s), has_int_(false) {}
template <typename T>
friend bool holds_alternative(const SampleVariantIntString& value) {
return value.has_int_ == internal::IsSame<T, int>::value;
}
template <typename T>
friend const T& get(const SampleVariantIntString& value) {
return value.get_impl(static_cast<T*>(NULL));
}
private:
const int& get_impl(int*) const { return i_; }
const std::string& get_impl(std::string*) const { return s_; }
int i_;
std::string s_;
bool has_int_;
};
TEST(VariantTest, DescribesSelf) {
const Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_THAT(Describe(m), ContainsRegex("is a variant<> with value of type "
"'.*' and the value is equal to 1"));
}
TEST(VariantTest, ExplainsSelf) {
const Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_THAT(Explain(m, SampleVariantIntString(1)),
ContainsRegex("whose value 1"));
EXPECT_THAT(Explain(m, SampleVariantIntString("A")),
HasSubstr("whose value is not of type '"));
EXPECT_THAT(Explain(m, SampleVariantIntString(2)),
"whose value 2 doesn't match");
}
TEST(VariantTest, FullMatch) {
Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_TRUE(m.Matches(SampleVariantIntString(1)));
m = VariantWith<std::string>(Eq("1"));
EXPECT_TRUE(m.Matches(SampleVariantIntString("1")));
}
TEST(VariantTest, TypeDoesNotMatch) {
Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_FALSE(m.Matches(SampleVariantIntString("1")));
m = VariantWith<std::string>(Eq("1"));
EXPECT_FALSE(m.Matches(SampleVariantIntString(1)));
}
TEST(VariantTest, InnerDoesNotMatch) {
Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_FALSE(m.Matches(SampleVariantIntString(2)));
m = VariantWith<std::string>(Eq("1"));
EXPECT_FALSE(m.Matches(SampleVariantIntString("2")));
}
} // namespace gmock_matchers_test } // namespace gmock_matchers_test
} // namespace testing } // namespace testing
...@@ -120,13 +120,15 @@ ...@@ -120,13 +120,15 @@
# include <errno.h> # include <errno.h>
#endif #endif
#include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h"
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "gtest/gtest.h"
#include "gtest/internal/gtest-port.h"
using testing::_; using testing::_;
using testing::A; using testing::A;
using testing::Action;
using testing::AllOf; using testing::AllOf;
using testing::AnyOf; using testing::AnyOf;
using testing::Assign; using testing::Assign;
...@@ -148,6 +150,8 @@ using testing::Invoke; ...@@ -148,6 +150,8 @@ using testing::Invoke;
using testing::InvokeArgument; using testing::InvokeArgument;
using testing::InvokeWithoutArgs; using testing::InvokeWithoutArgs;
using testing::IsNull; using testing::IsNull;
using testing::IsSubsetOf;
using testing::IsSupersetOf;
using testing::Le; using testing::Le;
using testing::Lt; using testing::Lt;
using testing::Matcher; using testing::Matcher;
...@@ -592,6 +596,22 @@ TEST(LinkTest, TestMatcherElementsAreArray) { ...@@ -592,6 +596,22 @@ TEST(LinkTest, TestMatcherElementsAreArray) {
ON_CALL(mock, VoidFromVector(ElementsAreArray(arr))).WillByDefault(Return()); ON_CALL(mock, VoidFromVector(ElementsAreArray(arr))).WillByDefault(Return());
} }
// Tests the linkage of the IsSubsetOf matcher.
TEST(LinkTest, TestMatcherIsSubsetOf) {
Mock mock;
char arr[] = {'a', 'b'};
ON_CALL(mock, VoidFromVector(IsSubsetOf(arr))).WillByDefault(Return());
}
// Tests the linkage of the IsSupersetOf matcher.
TEST(LinkTest, TestMatcherIsSupersetOf) {
Mock mock;
char arr[] = {'a', 'b'};
ON_CALL(mock, VoidFromVector(IsSupersetOf(arr))).WillByDefault(Return());
}
// Tests the linkage of the ContainerEq matcher. // Tests the linkage of the ContainerEq matcher.
TEST(LinkTest, TestMatcherContainerEq) { TEST(LinkTest, TestMatcherContainerEq) {
Mock mock; Mock mock;
......
...@@ -27,8 +27,6 @@ option( ...@@ -27,8 +27,6 @@ option(
"Build gtest with internal symbols hidden in shared libraries." "Build gtest with internal symbols hidden in shared libraries."
OFF) OFF)
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Generate debug library name with a postfix.")
# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build(). # Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build().
include(cmake/hermetic_build.cmake OPTIONAL) include(cmake/hermetic_build.cmake OPTIONAL)
...@@ -306,7 +304,9 @@ if (gtest_build_tests) ...@@ -306,7 +304,9 @@ if (gtest_build_tests)
cxx_executable(gtest_xml_outfile1_test_ test gtest_main) cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
cxx_executable(gtest_xml_outfile2_test_ test gtest_main) cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
py_test(gtest_xml_outfiles_test) py_test(gtest_xml_outfiles_test)
py_test(gtest_json_outfiles_test)
cxx_executable(gtest_xml_output_unittest_ test gtest) cxx_executable(gtest_xml_output_unittest_ test gtest)
py_test(gtest_xml_output_unittest) py_test(gtest_xml_output_unittest)
py_test(gtest_json_output_unittest)
endif() endif()
...@@ -91,7 +91,7 @@ macro(config_compiler_and_linker) ...@@ -91,7 +91,7 @@ macro(config_compiler_and_linker)
set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0") set(cxx_no_exception_flags "-EHs-c- -D_HAS_EXCEPTIONS=0")
set(cxx_no_rtti_flags "-GR-") set(cxx_no_rtti_flags "-GR-")
elseif (CMAKE_COMPILER_IS_GNUCXX) elseif (CMAKE_COMPILER_IS_GNUCXX)
set(cxx_base_flags "-Wall -Wshadow -Werror") set(cxx_base_flags "-Wall -Wshadow -Werror")
...@@ -158,6 +158,10 @@ function(cxx_library_with_type name type cxx_flags) ...@@ -158,6 +158,10 @@ function(cxx_library_with_type name type cxx_flags)
set_target_properties(${name} set_target_properties(${name}
PROPERTIES PROPERTIES
COMPILE_FLAGS "${cxx_flags}") COMPILE_FLAGS "${cxx_flags}")
# Generate debug library name with a postfix.
set_target_properties(${name}
PROPERTIES
DEBUG_POSTFIX "d")
if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED") if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED")
set_target_properties(${name} set_target_properties(${name}
PROPERTIES PROPERTIES
......
...@@ -2060,6 +2060,207 @@ Things to note: ...@@ -2060,6 +2060,207 @@ Things to note:
_Availability:_ Linux, Windows, Mac. _Availability:_ Linux, Windows, Mac.
#### Generating an JSON Report {#JsonReport}
gUnit can also emit a JSON report as an alternative format to XML. To generate
the JSON report, set the `GUNIT_OUTPUT` environment variable or the
`--gunit_output` flag to the string `"json:path_to_output_file"`, which will
create the file at the given location. You can also just use the string
`"json"`, in which case the output can be found in the `test_detail.json` file
in the current directory.
The report format conforms to the following JSON Schema:
```json
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"definitions": {
"TestCase": {
"type": "object",
"properties": {
"name": { "type": "string" },
"tests": { "type": "integer" },
"failures": { "type": "integer" },
"disabled": { "type": "integer" },
"time": { "type": "string" },
"testsuite": {
"type": "array",
"items": {
"$ref": "#/definitions/TestInfo"
}
}
}
},
"TestInfo": {
"type": "object",
"properties": {
"name": { "type": "string" },
"status": {
"type": "string",
"enum": ["RUN", "NOTRUN"]
},
"time": { "type": "string" },
"classname": { "type": "string" },
"failures": {
"type": "array",
"items": {
"$ref": "#/definitions/Failure"
}
}
}
},
"Failure": {
"type": "object",
"properties": {
"failures": { "type": "string" },
"type": { "type": "string" }
}
}
},
"properties": {
"tests": { "type": "integer" },
"failures": { "type": "integer" },
"disabled": { "type": "integer" },
"errors": { "type": "integer" },
"timestamp": {
"type": "string",
"format": "date-time"
},
"time": { "type": "string" },
"name": { "type": "string" },
"testsuites": {
"type": "array",
"items": {
"$ref": "#/definitions/TestCase"
}
}
}
}
```
The report uses the format that conforms to the following Proto3 using the
[JSON encoding](https://developers.google.com/protocol-buffers/docs/proto3#json):
```proto
syntax = "proto3";
package googletest;
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
message UnitTest {
int32 tests = 1;
int32 failures = 2;
int32 disabled = 3;
int32 errors = 4;
google.protobuf.Timestamp timestamp = 5;
google.protobuf.Duration time = 6;
string name = 7;
repeated TestCase testsuites = 8;
}
message TestCase {
string name = 1;
int32 tests = 2;
int32 failures = 3;
int32 disabled = 4;
int32 errors = 5;
google.protobuf.Duration time = 6;
repeated TestInfo testsuite = 7;
}
message TestInfo {
string name = 1;
enum Status {
RUN = 0;
NOTRUN = 1;
}
Status status = 2;
google.protobuf.Duration time = 3;
string classname = 4;
message Failure {
string failures = 1;
string type = 2;
}
repeated Failure failures = 5;
}
```
For instance, the following program
```c++
TEST(MathTest, Addition) { ... }
TEST(MathTest, Subtraction) { ... }
TEST(LogicTest, NonContradiction) { ... }
```
could generate this report:
```json
{
"tests": 3,
"failures": 1,
"errors": 0,
"time": "0.035s",
"timestamp": "2011-10-31T18:52:42Z"
"name": "AllTests",
"testsuites": [
{
"name": "MathTest",
"tests": 2,
"failures": 1,
"errors": 0,
"time": "0.015s",
"testsuite": [
{
"name": "Addition",
"status": "RUN",
"time": "0.007s",
"classname": "",
"failures": [
{
"message": "Value of: add(1, 1)\x0A Actual: 3\x0AExpected: 2",
"type": ""
},
{
"message": "Value of: add(1, -1)\x0A Actual: 1\x0AExpected: 0",
"type": ""
}
]
},
{
"name": "Subtraction",
"status": "RUN",
"time": "0.005s",
"classname": ""
}
]
}
{
"name": "LogicTest",
"tests": 1,
"failures": 0,
"errors": 0,
"time": "0.005s",
"testsuite": [
{
"name": "NonContradiction",
"status": "RUN",
"time": "0.005s",
"classname": ""
}
]
}
]
}
```
IMPORTANT: The exact format of the JSON document is subject to change.
**Availability**: Linux, Windows, Mac.
## Controlling How Failures Are Reported ## ## Controlling How Failures Are Reported ##
### Turning Assertion Failures into Break-Points ### ### Turning Assertion Failures into Break-Points ###
......
...@@ -471,8 +471,11 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; ...@@ -471,8 +471,11 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#ifndef GTEST_HAS_EXCEPTIONS #ifndef GTEST_HAS_EXCEPTIONS
// The user didn't tell us whether exceptions are enabled, so we need // The user didn't tell us whether exceptions are enabled, so we need
// to figure it out. // to figure it out.
# if defined(_MSC_VER) || defined(__BORLANDC__) # if defined(_MSC_VER) && defined(_CPPUNWIND)
// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS // MSVC defines _CPPUNWIND to 1 iff exceptions are enabled.
# define GTEST_HAS_EXCEPTIONS 1
# elif defined(__BORLANDC__)
// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS
// macro to enable exceptions, so we'll do the same. // macro to enable exceptions, so we'll do the same.
// Assumes that exceptions are enabled by default. // Assumes that exceptions are enabled by default.
# ifndef _HAS_EXCEPTIONS # ifndef _HAS_EXCEPTIONS
......
#!/usr/bin/env python
# Copyright 2018, Google 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:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
# OWNER OR 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.
"""Unit test for the gtest_json_output module."""
import json
import os
import gtest_test_utils
import gtest_json_test_utils
GTEST_OUTPUT_SUBDIR = 'json_outfiles'
GTEST_OUTPUT_1_TEST = 'gtest_xml_outfile1_test_'
GTEST_OUTPUT_2_TEST = 'gtest_xml_outfile2_test_'
EXPECTED_1 = {
u'tests': 1,
u'failures': 0,
u'disabled': 0,
u'errors': 0,
u'time': u'*',
u'timestamp': u'*',
u'name': u'AllTests',
u'testsuites': [{
u'name': u'PropertyOne',
u'tests': 1,
u'failures': 0,
u'disabled': 0,
u'errors': 0,
u'time': u'*',
u'testsuite': [{
u'name': u'TestSomeProperties',
u'status': u'RUN',
u'time': u'*',
u'classname': u'PropertyOne',
u'SetUpProp': u'1',
u'TestSomeProperty': u'1',
u'TearDownProp': u'1',
}],
}],
}
EXPECTED_2 = {
u'tests': 1,
u'failures': 0,
u'disabled': 0,
u'errors': 0,
u'time': u'*',
u'timestamp': u'*',
u'name': u'AllTests',
u'testsuites': [{
u'name': u'PropertyTwo',
u'tests': 1,
u'failures': 0,
u'disabled': 0,
u'errors': 0,
u'time': u'*',
u'testsuite': [{
u'name': u'TestSomeProperties',
u'status': u'RUN',
u'time': u'*',
u'classname': u'PropertyTwo',
u'SetUpProp': u'2',
u'TestSomeProperty': u'2',
u'TearDownProp': u'2',
}],
}],
}
class GTestJsonOutFilesTest(gtest_test_utils.TestCase):
"""Unit test for Google Test's JSON output functionality."""
def setUp(self):
# We want the trailing '/' that the last "" provides in os.path.join, for
# telling Google Test to create an output directory instead of a single file
# for xml output.
self.output_dir_ = os.path.join(gtest_test_utils.GetTempDir(),
GTEST_OUTPUT_SUBDIR, '')
self.DeleteFilesAndDir()
def tearDown(self):
self.DeleteFilesAndDir()
def DeleteFilesAndDir(self):
try:
os.remove(os.path.join(self.output_dir_, GTEST_OUTPUT_1_TEST + '.json'))
except os.error:
pass
try:
os.remove(os.path.join(self.output_dir_, GTEST_OUTPUT_2_TEST + '.json'))
except os.error:
pass
try:
os.rmdir(self.output_dir_)
except os.error:
pass
def testOutfile1(self):
self._TestOutFile(GTEST_OUTPUT_1_TEST, EXPECTED_1)
def testOutfile2(self):
self._TestOutFile(GTEST_OUTPUT_2_TEST, EXPECTED_2)
def _TestOutFile(self, test_name, expected):
gtest_prog_path = gtest_test_utils.GetTestExecutablePath(test_name)
command = [gtest_prog_path, '--gtest_output=json:%s' % self.output_dir_]
p = gtest_test_utils.Subprocess(command,
working_dir=gtest_test_utils.GetTempDir())
self.assert_(p.exited)
self.assertEquals(0, p.exit_code)
# TODO(wan@google.com): libtool causes the built test binary to be
# named lt-gtest_xml_outfiles_test_ instead of
# gtest_xml_outfiles_test_. To account for this possibility, we
# allow both names in the following code. We should remove this
# hack when Chandler Carruth's libtool replacement tool is ready.
output_file_name1 = test_name + '.json'
output_file1 = os.path.join(self.output_dir_, output_file_name1)
output_file_name2 = 'lt-' + output_file_name1
output_file2 = os.path.join(self.output_dir_, output_file_name2)
self.assert_(os.path.isfile(output_file1) or os.path.isfile(output_file2),
output_file1)
if os.path.isfile(output_file1):
with open(output_file1) as f:
actual = json.load(f)
else:
with open(output_file2) as f:
actual = json.load(f)
self.assertEqual(expected, gtest_json_test_utils.normalize(actual))
if __name__ == '__main__':
os.environ['GTEST_STACK_TRACE_DEPTH'] = '0'
gtest_test_utils.Main()
# Copyright 2018, Google 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:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
# OWNER OR 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.
"""Unit test utilities for gtest_json_output."""
import re
def normalize(obj):
"""Normalize output object.
Args:
obj: Google Test's JSON output object to normalize.
Returns:
Normalized output without any references to transient information that may
change from run to run.
"""
def _normalize(key, value):
if key == 'time':
return re.sub(r'^\d+(\.\d+)?s$', u'*', value)
elif key == 'timestamp':
return re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ$', '*', value)
elif key == 'failure':
value = re.sub(r'^.*[/\\](.*:)\d+\n', '\\1*\n', value)
return re.sub(r'Stack trace:\n(.|\n)*', 'Stack trace:\n*', value)
else:
return normalize(value)
if isinstance(obj, dict):
return {k: _normalize(k, v) for k, v in obj.items()}
if isinstance(obj, list):
return [normalize(x) for x in obj]
else:
return obj
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment