mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:49:43 +00:00
Add optional message argument to assert statements in the VM.
Add flag --assert-message to control the feature. BUG=#24215 R=regis@google.com Review-Url: https://codereview.chromium.org/2574003003 .
This commit is contained in:
parent
871f478c63
commit
2c0b605a96
|
@ -20,7 +20,7 @@ static RawScript* FindScript(DartFrameIterator* iterator) {
|
||||||
// the inlining meta-data so we cannot walk the inline-aware stack trace.
|
// the inlining meta-data so we cannot walk the inline-aware stack trace.
|
||||||
// Second, the script text itself is missing so whatever script is returned
|
// Second, the script text itself is missing so whatever script is returned
|
||||||
// from here will be missing the assertion expression text.
|
// from here will be missing the assertion expression text.
|
||||||
iterator->NextFrame(); // Skip _AssertionError._checkAssertion frame
|
iterator->NextFrame(); // Skip _AssertionError._evaluateAssertion frame
|
||||||
return Exceptions::GetCallerScript(iterator);
|
return Exceptions::GetCallerScript(iterator);
|
||||||
}
|
}
|
||||||
StackFrame* stack_frame = iterator->NextFrame();
|
StackFrame* stack_frame = iterator->NextFrame();
|
||||||
|
@ -62,8 +62,9 @@ static RawScript* FindScript(DartFrameIterator* iterator) {
|
||||||
// Allocate and throw a new AssertionError.
|
// Allocate and throw a new AssertionError.
|
||||||
// Arg0: index of the first token of the failed assertion.
|
// Arg0: index of the first token of the failed assertion.
|
||||||
// Arg1: index of the first token after the failed assertion.
|
// Arg1: index of the first token after the failed assertion.
|
||||||
|
// Arg2: Message object or null.
|
||||||
// Return value: none, throws an exception.
|
// Return value: none, throws an exception.
|
||||||
DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 2) {
|
DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 3) {
|
||||||
// No need to type check the arguments. This function can only be called
|
// No need to type check the arguments. This function can only be called
|
||||||
// internally from the VM.
|
// internally from the VM.
|
||||||
const TokenPosition assertion_start =
|
const TokenPosition assertion_start =
|
||||||
|
@ -71,7 +72,8 @@ DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 2) {
|
||||||
const TokenPosition assertion_end =
|
const TokenPosition assertion_end =
|
||||||
TokenPosition(Smi::CheckedHandle(arguments->NativeArgAt(1)).Value());
|
TokenPosition(Smi::CheckedHandle(arguments->NativeArgAt(1)).Value());
|
||||||
|
|
||||||
const Array& args = Array::Handle(Array::New(4));
|
const Instance& message = Instance::CheckedHandle(arguments->NativeArgAt(2));
|
||||||
|
const Array& args = Array::Handle(Array::New(5));
|
||||||
|
|
||||||
DartFrameIterator iterator;
|
DartFrameIterator iterator;
|
||||||
iterator.NextFrame(); // Skip native call.
|
iterator.NextFrame(); // Skip native call.
|
||||||
|
@ -92,6 +94,7 @@ DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 2) {
|
||||||
args.SetAt(1, String::Handle(script.url()));
|
args.SetAt(1, String::Handle(script.url()));
|
||||||
args.SetAt(2, Smi::Handle(Smi::New(from_line)));
|
args.SetAt(2, Smi::Handle(Smi::New(from_line)));
|
||||||
args.SetAt(3, Smi::Handle(Smi::New(script.HasSource() ? from_column : -1)));
|
args.SetAt(3, Smi::Handle(Smi::New(script.HasSource() ? from_column : -1)));
|
||||||
|
args.SetAt(4, message);
|
||||||
|
|
||||||
Exceptions::ThrowByType(Exceptions::kAssertion, args);
|
Exceptions::ThrowByType(Exceptions::kAssertion, args);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
|
|
@ -18,29 +18,37 @@
|
||||||
|
|
||||||
class _AssertionError extends Error implements AssertionError {
|
class _AssertionError extends Error implements AssertionError {
|
||||||
_AssertionError._create(
|
_AssertionError._create(
|
||||||
this._failedAssertion, this._url, this._line, this._column);
|
this._failedAssertion, this._url, this._line, this._column,
|
||||||
|
this.message);
|
||||||
|
|
||||||
static _throwNew(int assertionStart, int assertionEnd)
|
|
||||||
native "AssertionError_throwNew";
|
|
||||||
|
|
||||||
static void _checkAssertion(condition, int start, int end) {
|
// AssertionError_throwNew in errors.cc fishes the assertion source code
|
||||||
|
// out of the script. It expects a Dart stack frame from class
|
||||||
|
// _AssertionError. Thus we need a Dart stub that calls the native code.
|
||||||
|
static _throwNew(int assertionStart, int assertionEnd, Object message) {
|
||||||
|
_doThrowNew(assertionStart, assertionEnd, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _doThrowNew(int assertionStart, int assertionEnd, Object message)
|
||||||
|
native "AssertionError_throwNew";
|
||||||
|
|
||||||
|
static _evaluateAssertion(condition) {
|
||||||
if (condition is Function) {
|
if (condition is Function) {
|
||||||
condition = condition();
|
condition = condition();
|
||||||
}
|
}
|
||||||
if (!condition) {
|
return condition;
|
||||||
_throwNew(start, end);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _checkConstAssertion(bool condition, int start, int end) {
|
String get _messageString {
|
||||||
if (!condition) {
|
if (message == null) return "is not true.";
|
||||||
_throwNew(start, end);
|
if (message is String) return message;
|
||||||
}
|
return Error.safeToString(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
String toString() {
|
String toString() {
|
||||||
if (_url == null) {
|
if (_url == null) {
|
||||||
return _failedAssertion;
|
if (message == null) return _failedAssertion;
|
||||||
|
return "'$_failedAssertion': $_messageString";
|
||||||
}
|
}
|
||||||
var columnInfo = "";
|
var columnInfo = "";
|
||||||
if (_column > 0) {
|
if (_column > 0) {
|
||||||
|
@ -48,17 +56,18 @@ class _AssertionError extends Error implements AssertionError {
|
||||||
columnInfo = " pos $_column";
|
columnInfo = " pos $_column";
|
||||||
}
|
}
|
||||||
return "'$_url': Failed assertion: line $_line$columnInfo: "
|
return "'$_url': Failed assertion: line $_line$columnInfo: "
|
||||||
"'$_failedAssertion' is not true.";
|
"'$_failedAssertion': $_messageString";
|
||||||
}
|
}
|
||||||
final String _failedAssertion;
|
final String _failedAssertion;
|
||||||
final String _url;
|
final String _url;
|
||||||
final int _line;
|
final int _line;
|
||||||
final int _column;
|
final int _column;
|
||||||
|
final Object message;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TypeError extends _AssertionError implements TypeError {
|
class _TypeError extends _AssertionError implements TypeError {
|
||||||
_TypeError._create(String url, int line, int column, this._errorMsg)
|
_TypeError._create(String url, int line, int column, String errorMsg)
|
||||||
: super._create("is assignable", url, line, column);
|
: super._create("is assignable", url, line, column, errorMsg);
|
||||||
|
|
||||||
static _throwNew(int location,
|
static _throwNew(int location,
|
||||||
Object src_value,
|
Object src_value,
|
||||||
|
@ -78,9 +87,7 @@ class _TypeError extends _AssertionError implements TypeError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String toString() => _errorMsg;
|
String toString() => super.message;
|
||||||
|
|
||||||
final String _errorMsg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CastError extends Error implements CastError {
|
class _CastError extends Error implements CastError {
|
||||||
|
|
|
@ -159,7 +159,7 @@ namespace dart {
|
||||||
V(DateTime_timeZoneName, 1) \
|
V(DateTime_timeZoneName, 1) \
|
||||||
V(DateTime_timeZoneOffsetInSeconds, 1) \
|
V(DateTime_timeZoneOffsetInSeconds, 1) \
|
||||||
V(DateTime_localTimeZoneAdjustmentInSeconds, 0) \
|
V(DateTime_localTimeZoneAdjustmentInSeconds, 0) \
|
||||||
V(AssertionError_throwNew, 2) \
|
V(AssertionError_throwNew, 3) \
|
||||||
V(Async_rethrow, 2) \
|
V(Async_rethrow, 2) \
|
||||||
V(StackTrace_current, 0) \
|
V(StackTrace_current, 0) \
|
||||||
V(TypeError_throwNew, 5) \
|
V(TypeError_throwNew, 5) \
|
||||||
|
|
|
@ -585,7 +585,7 @@ DEFINE_RUNTIME_ENTRY(NonBoolTypeError, 1) {
|
||||||
Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
||||||
|
|
||||||
if (src_instance.IsNull()) {
|
if (src_instance.IsNull()) {
|
||||||
const Array& args = Array::Handle(zone, Array::New(4));
|
const Array& args = Array::Handle(zone, Array::New(5));
|
||||||
args.SetAt(
|
args.SetAt(
|
||||||
0, String::Handle(
|
0, String::Handle(
|
||||||
zone,
|
zone,
|
||||||
|
@ -596,6 +596,7 @@ DEFINE_RUNTIME_ENTRY(NonBoolTypeError, 1) {
|
||||||
args.SetAt(1, String::Handle(zone, String::null()));
|
args.SetAt(1, String::Handle(zone, String::null()));
|
||||||
args.SetAt(2, Smi::Handle(zone, Smi::New(0)));
|
args.SetAt(2, Smi::Handle(zone, Smi::New(0)));
|
||||||
args.SetAt(3, Smi::Handle(zone, Smi::New(0)));
|
args.SetAt(3, Smi::Handle(zone, Smi::New(0)));
|
||||||
|
args.SetAt(4, String::Handle(zone, String::null()));
|
||||||
|
|
||||||
Exceptions::ThrowByType(Exceptions::kAssertion, args);
|
Exceptions::ThrowByType(Exceptions::kAssertion, args);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
|
|
@ -70,6 +70,7 @@ DEFINE_FLAG(bool,
|
||||||
assert_initializer,
|
assert_initializer,
|
||||||
false,
|
false,
|
||||||
"Allow asserts in initializer lists.");
|
"Allow asserts in initializer lists.");
|
||||||
|
DEFINE_FLAG(bool, assert_message, false, "Allow message in assert statements");
|
||||||
|
|
||||||
DECLARE_FLAG(bool, profile_vm);
|
DECLARE_FLAG(bool, profile_vm);
|
||||||
DECLARE_FLAG(bool, trace_service);
|
DECLARE_FLAG(bool, trace_service);
|
||||||
|
@ -9164,32 +9165,91 @@ AstNode* Parser::ParseAssertStatement(bool is_const) {
|
||||||
const TokenPosition condition_pos = TokenPos();
|
const TokenPosition condition_pos = TokenPos();
|
||||||
if (!I->asserts()) {
|
if (!I->asserts()) {
|
||||||
SkipExpr();
|
SkipExpr();
|
||||||
|
if (FLAG_assert_message && (CurrentToken() == Token::kCOMMA)) {
|
||||||
|
ConsumeToken();
|
||||||
|
SkipExpr();
|
||||||
|
}
|
||||||
ExpectToken(Token::kRPAREN);
|
ExpectToken(Token::kRPAREN);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
AstNode* condition = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL);
|
|
||||||
|
BoolScope saved_seen_await(&parsed_function()->have_seen_await_expr_, false);
|
||||||
|
AstNode* condition = ParseExpr(kAllowConst, kConsumeCascades);
|
||||||
if (is_const && !condition->IsPotentiallyConst()) {
|
if (is_const && !condition->IsPotentiallyConst()) {
|
||||||
ReportError(condition_pos,
|
ReportError(condition_pos,
|
||||||
"initializer assert expression must be compile time constant.");
|
"initializer assert expression must be compile time constant.");
|
||||||
}
|
}
|
||||||
const TokenPosition condition_end = TokenPos();
|
const TokenPosition condition_end = TokenPos();
|
||||||
|
AstNode* message = NULL;
|
||||||
|
TokenPosition message_pos = TokenPosition::kNoSource;
|
||||||
|
if (FLAG_assert_message && CurrentToken() == Token::kCOMMA) {
|
||||||
|
ConsumeToken();
|
||||||
|
message_pos = TokenPos();
|
||||||
|
message = ParseExpr(kAllowConst, kConsumeCascades);
|
||||||
|
if (is_const && !message->IsPotentiallyConst()) {
|
||||||
|
ReportError(
|
||||||
|
message_pos,
|
||||||
|
"initializer assert expression must be compile time constant.");
|
||||||
|
}
|
||||||
|
}
|
||||||
ExpectToken(Token::kRPAREN);
|
ExpectToken(Token::kRPAREN);
|
||||||
|
|
||||||
|
if (!is_const) {
|
||||||
|
// Check for assertion condition being a function if not const.
|
||||||
|
ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos);
|
||||||
|
arguments->Add(condition);
|
||||||
|
condition = MakeStaticCall(
|
||||||
|
Symbols::AssertionError(),
|
||||||
|
Library::PrivateCoreLibName(Symbols::EvaluateAssertion()), arguments);
|
||||||
|
}
|
||||||
|
AstNode* not_condition =
|
||||||
|
new (Z) UnaryOpNode(condition_pos, Token::kNOT, condition);
|
||||||
|
|
||||||
|
// Build call to _AsertionError._throwNew(start, end, message)
|
||||||
ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos);
|
ArgumentListNode* arguments = new (Z) ArgumentListNode(condition_pos);
|
||||||
arguments->Add(condition);
|
|
||||||
arguments->Add(new (Z) LiteralNode(
|
arguments->Add(new (Z) LiteralNode(
|
||||||
condition_pos,
|
condition_pos,
|
||||||
Integer::ZoneHandle(Z, Integer::New(condition_pos.value(), Heap::kOld))));
|
Integer::ZoneHandle(Z, Integer::New(condition_pos.Pos()))));
|
||||||
arguments->Add(new (Z) LiteralNode(
|
arguments->Add(new (Z) LiteralNode(
|
||||||
condition_end,
|
condition_end,
|
||||||
Integer::ZoneHandle(Z, Integer::New(condition_end.value(), Heap::kOld))));
|
Integer::ZoneHandle(Z, Integer::New(condition_end.Pos()))));
|
||||||
|
if (message == NULL) {
|
||||||
|
message = new (Z) LiteralNode(condition_end, Instance::ZoneHandle(Z));
|
||||||
|
}
|
||||||
|
arguments->Add(message);
|
||||||
AstNode* assert_throw = MakeStaticCall(
|
AstNode* assert_throw = MakeStaticCall(
|
||||||
Symbols::AssertionError(),
|
Symbols::AssertionError(),
|
||||||
Library::PrivateCoreLibName(is_const ? Symbols::CheckConstAssertion()
|
Library::PrivateCoreLibName(Symbols::ThrowNew()), arguments);
|
||||||
: Symbols::CheckAssertion()),
|
|
||||||
arguments);
|
|
||||||
|
|
||||||
return assert_throw;
|
AstNode* assertion_check = NULL;
|
||||||
|
if (parsed_function()->have_seen_await()) {
|
||||||
|
// The await transformation must be done manually because assertions
|
||||||
|
// are parsed as statements, not expressions. Thus, we need to check
|
||||||
|
// explicitely whether the arguments contain await operators. (Note that
|
||||||
|
// we must not parse the arguments with ParseAwaitableExpr(). In the
|
||||||
|
// corner case of assert(await a, await b), this would create two
|
||||||
|
// sibling scopes containing the temporary values for a and b. Both
|
||||||
|
// values would be allocated in the same internal context variable.)
|
||||||
|
//
|
||||||
|
// Build !condition ? _AsertionError._throwNew(...) : null;
|
||||||
|
// We need to use a conditional expression because the await transformer
|
||||||
|
// cannot transform if statements.
|
||||||
|
assertion_check = new (Z) ConditionalExprNode(
|
||||||
|
condition_pos, not_condition, assert_throw,
|
||||||
|
new (Z) LiteralNode(condition_pos, Object::null_instance()));
|
||||||
|
OpenBlock();
|
||||||
|
AwaitTransformer at(current_block_->statements, async_temp_scope_);
|
||||||
|
AstNode* transformed_assertion = at.Transform(assertion_check);
|
||||||
|
SequenceNode* preamble = CloseBlock();
|
||||||
|
preamble->Add(transformed_assertion);
|
||||||
|
assertion_check = preamble;
|
||||||
|
} else {
|
||||||
|
// Build if (!condition) _AsertionError._throwNew(...)
|
||||||
|
assertion_check = new (Z)
|
||||||
|
IfNode(condition_pos, not_condition,
|
||||||
|
NodeAsSequenceNode(condition_pos, assert_throw, NULL), NULL);
|
||||||
|
}
|
||||||
|
return assertion_check;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,7 @@ class ObjectPointerVisitor;
|
||||||
V(_CompileTimeError, "_CompileTimeError") \
|
V(_CompileTimeError, "_CompileTimeError") \
|
||||||
V(ThrowNew, "_throwNew") \
|
V(ThrowNew, "_throwNew") \
|
||||||
V(ThrowNewIfNotLoaded, "_throwNewIfNotLoaded") \
|
V(ThrowNewIfNotLoaded, "_throwNewIfNotLoaded") \
|
||||||
V(CheckAssertion, "_checkAssertion") \
|
V(EvaluateAssertion, "_evaluateAssertion") \
|
||||||
V(CheckConstAssertion, "_checkConstAssertion") \
|
|
||||||
V(Symbol, "Symbol") \
|
V(Symbol, "Symbol") \
|
||||||
V(SymbolCtor, "Symbol.") \
|
V(SymbolCtor, "Symbol.") \
|
||||||
V(List, "List") \
|
V(List, "List") \
|
||||||
|
|
|
@ -96,7 +96,9 @@ class Error {
|
||||||
* Error thrown by the runtime system when an assert statement fails.
|
* Error thrown by the runtime system when an assert statement fails.
|
||||||
*/
|
*/
|
||||||
class AssertionError extends Error {
|
class AssertionError extends Error {
|
||||||
AssertionError();
|
/** Message describing the assertion error. */
|
||||||
|
final Object message;
|
||||||
|
AssertionError([this.message]);
|
||||||
String toString() => "Assertion failed";
|
String toString() => "Assertion failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
104
tests/language/assert_message_test.dart
Normal file
104
tests/language/assert_message_test.dart
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// SharedOptions=--assert-message
|
||||||
|
|
||||||
|
import "dart:async";
|
||||||
|
|
||||||
|
import "package:async_helper/async_helper.dart";
|
||||||
|
import "package:expect/expect.dart";
|
||||||
|
|
||||||
|
main() {
|
||||||
|
// Only run with asserts enabled mode.
|
||||||
|
bool assertsEnabled = false;
|
||||||
|
assert(assertsEnabled = true);
|
||||||
|
if (!assertsEnabled) return;
|
||||||
|
|
||||||
|
// Basics.
|
||||||
|
assert(true, "");
|
||||||
|
assert(() => true, "");
|
||||||
|
|
||||||
|
int x = null;
|
||||||
|
// Successful asserts won't execute message.
|
||||||
|
assert(true, x + 42);
|
||||||
|
assert(true, throw "unreachable");
|
||||||
|
|
||||||
|
// Can use any value as message.
|
||||||
|
try {
|
||||||
|
assert(false, 42);
|
||||||
|
} on AssertionError catch (e) {
|
||||||
|
Expect.equals(42, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(false, "");
|
||||||
|
} on AssertionError catch (e) {
|
||||||
|
Expect.equals("", e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(false, null);
|
||||||
|
} on AssertionError catch (e) {
|
||||||
|
Expect.equals(null, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test expression can throw.
|
||||||
|
try {
|
||||||
|
assert(throw "test", throw "message");
|
||||||
|
} on String catch (e) {
|
||||||
|
Expect.equals("test", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message expression can throw.
|
||||||
|
try {
|
||||||
|
assert(false, throw "message");
|
||||||
|
} on String catch (e) {
|
||||||
|
Expect.equals("message", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failing asserts evaluate message after test.
|
||||||
|
var list = [];
|
||||||
|
try {
|
||||||
|
assert((list..add(1)).isEmpty, (list..add(3)).length);
|
||||||
|
} on AssertionError catch (e) {
|
||||||
|
Expect.equals(2, e.message);
|
||||||
|
Expect.listEquals([1, 3], list);
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncStart();
|
||||||
|
asyncTests().then((_) { asyncEnd(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future asyncTests() async {
|
||||||
|
// You can await in both condition and message.
|
||||||
|
assert(true, await 0);
|
||||||
|
assert(await true, 1);
|
||||||
|
assert(await true, await 2);
|
||||||
|
|
||||||
|
// Successful asserts won't await/evaluate message.
|
||||||
|
void unreachable() => throw "unreachable";
|
||||||
|
assert(await true, await unreachable());
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(false, await 3);
|
||||||
|
} on AssertionError catch (e) {
|
||||||
|
Expect.equals(3, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
var falseFuture = new Future.value(false);
|
||||||
|
var numFuture = new Future.value(4);
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(await falseFuture, await numFuture);
|
||||||
|
} on AssertionError catch (e) {
|
||||||
|
Expect.equals(4, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(await falseFuture, await new Future.error("error"));
|
||||||
|
} on String catch (e) {
|
||||||
|
Expect.equals("error", e);
|
||||||
|
}
|
||||||
|
}
|
|
@ -139,6 +139,7 @@ multiline_newline_test/06: MissingCompileTimeError # Issue 23888
|
||||||
multiline_newline_test/none: RuntimeError # Issue 23888
|
multiline_newline_test/none: RuntimeError # Issue 23888
|
||||||
|
|
||||||
[ $compiler == dart2js && $checked ]
|
[ $compiler == dart2js && $checked ]
|
||||||
|
assert_message_test: Fail #28106
|
||||||
async_return_types_test/nestedFuture: Fail # Issue 26429
|
async_return_types_test/nestedFuture: Fail # Issue 26429
|
||||||
async_return_types_test/wrongTypeParameter: Fail # Issue 26429
|
async_return_types_test/wrongTypeParameter: Fail # Issue 26429
|
||||||
regress_26133_test: RuntimeError # Issue 26429
|
regress_26133_test: RuntimeError # Issue 26429
|
||||||
|
|
Loading…
Reference in a new issue