Files
sdk/tests/dartdevc/no_such_method_errors_test.dart
T
William Hesse cd2c566bcf Revert "Tweak expect.dart library."
This reverts commit ff5f391c0a.

Reason for revert: The expect library is used by Flutter engine, and some of its tests use assertStatementsEnabled. There should be a migration path that doesn't require an atomic change, like adding the replacement api before removing the old one.

Original change's description:
> Tweak `expect.dart` library.
>
> Make API more consistent for a few methods.
> Reduce the number of language features used in tests:
> * Never iterating an iterable, always converting it
>   using `.toList()` first and iterating using indices
>   (fx `setEquals`).
>   Also require a `List` in places where an `Iterable`
>   wasn't necessary.
> * Avoid doing complicated computations that are also
>   used for the error message. Do simple check first,
>   then recompute to get better error messages
>   (fx `allDistinct`).
>
> Renamed some rarely used members for consistency
> (`stringContainsInOrder`->`containsInOrder`,
> where other string-contains functions just start
> with `contains`, and `containsOneOf` -> `containsAny`
> to match `Iterable.any` phrasing, and also it accepts
> if containing at least one, not precisely one.)
>
> Removed a function that wasn't used anywhere.
>
> Moved `assertStatementsEnabled` to `variations.dart` as `asserts`.
> Removed `typeAssertionsEnabled` and `checkedModeEnabled`. The former used in one place, where it was replaced with `checkedImplicitDowncasts` from `variations.dart`, the latter wasn't used anywhere.
>
> Deprecates `package:expect/minitest.dart`. It was never intended
> to be used for new tests, only as a help to convert existing tests
> written against `package:unit_test`.
> All existing imports marked as `// ignore: deprecated_member_use`.
>
> Change-Id: I07e21d4c0f3ccf11b82ee34af2668fdbb22264d2
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352360
> Reviewed-by: Slava Egorov <vegorov@google.com>
> Reviewed-by: Ömer Ağacan <omersa@google.com>
> Reviewed-by: Nate Bosch <nbosch@google.com>
> Reviewed-by: Stephen Adams <sra@google.com>
> Commit-Queue: Lasse Nielsen <lrn@google.com>

Change-Id: I360b4347470a0bb2b63c3108e2b83ee2a771bf3f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362020
Reviewed-by: Nate Bosch <nbosch@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: William Hesse <whesse@google.com>
2024-04-09 18:10:57 +00:00

321 lines
12 KiB
Dart

// Copyright (c) 2019, 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.
import 'package:expect/expect.dart';
import 'package:expect/minitest.dart';
/// This test is currently written as a white box test to ensure we don't
/// regress on any of the existing code paths that lead to NoSuchMethod errors
/// from dynamic calls. The test cases written with the knowledge of what the
/// generated code will look like.
// TODO(52190): Improve the NSM errors to make them more helpful.
expectThrowsNSMWithExactError(
void Function() computation, String expectedErrorMessage) =>
Expect.throws<NoSuchMethodError>(
computation, (error) => error.toString() == expectedErrorMessage);
class A {
String arity1(int val) {
val += 10;
return val.toString();
}
void genericArity2<T, S>() => print('$T, $S');
static String staticArity1(int val) {
val += 10;
return val.toString();
}
static void staticGenericArity2<T, S>() => print('$T, $S');
Function? nullField;
Function fieldArity1 = (int val) {
val += 10;
return val.toString();
};
}
String arity1(int val) {
val += 10;
return val.toString();
}
void genericArity2<T, S>() => print('$T, $S');
Function fieldArity1 = (int val) {
val += 10;
return val.toString();
};
String requiredNamedArity1({required bool fosse}) {
return fosse.toString();
}
int? x;
void main() {
group('Dynamic call of', () {
dynamic instanceOfA = A();
test('instance of a class with no `call()` method', () {
// Compiled as `dcall()`.
expectThrowsNSMWithExactError(
() => instanceOfA(),
"NoSuchMethodError: 'call'\n"
"Dynamic call of object has no instance method 'call'.\n"
"Receiver: ${Error.safeToString(instanceOfA)}\n"
"Arguments: []");
});
group('null', () {
// TODO(49628): These should actually throw NoSuchMethodError with a
// helpful error message.
group('value', () {
dynamic nullVal = null;
test('without type arguments', () {
// Compiled as `dcall()`.
Expect.throws(() => nullVal());
});
test('passing type arguments', () {
// Compiled as `dgcall()`.
Expect.throws(() => nullVal<String, bool>());
});
});
group('instance field', () {
test('without type arguments', () {
// Compiled as `dsend()`.
Expect.throws(() => instanceOfA.nullField());
});
test('passing type arguments', () {
// Compiled as `dgsend()`.
Expect.throws(() => instanceOfA.nullField<String, bool>());
});
});
});
group('class instance members that do not exist', () {
group('method', () {
test('without passing type arguments', () {
// Compiled as `dsend()`.
expectThrowsNSMWithExactError(
() => instanceOfA.doesNotExist(),
"NoSuchMethodError: 'doesNotExist'\n"
"Dynamic call of null.\n"
"Receiver: ${Error.safeToString(instanceOfA)}\n"
"Arguments: []");
});
test('passing type arguments', () {
// Compiled as `dgsend()`.
expectThrowsNSMWithExactError(
() => instanceOfA.doesNotExist<String, bool>(),
"NoSuchMethodError: 'doesNotExist'\n"
"Dynamic call of null.\n"
"Receiver: ${Error.safeToString(instanceOfA)}\n"
"Arguments: []");
});
});
test('setter', () {
// Compiled as `dput()`.
expectThrowsNSMWithExactError(
() => instanceOfA.doesNotExist = 10,
"NoSuchMethodError: 'doesNotExist='\n"
"method not found\n"
"Receiver: ${Error.safeToString(instanceOfA)}\n"
"Arguments: [10]");
});
test('getter', () {
// Compiled as `dload()`.
expectThrowsNSMWithExactError(
() => x = instanceOfA.doesNotExist,
"NoSuchMethodError: 'doesNotExist'\n"
"method not found\n"
"Receiver: ${Error.safeToString(instanceOfA)}\n"
"Arguments: []");
});
});
group('tearoff', () {
// The code path for throwing NoSuchMethodErrors because of the incorrect
// type arguments is shared by all forms of dynamic invocations. Simply
// using tearoffs to trigger each form of the error.
dynamic arity1Tearoff = arity1;
dynamic genericArity2Tearoff = genericArity2;
dynamic instantiatedTearoff = genericArity2<int, String>;
test('passing unexpected type arguments', () {
// Compiled as `dgcall()` and throws from `checkAndCall()`.
expectThrowsNSMWithExactError(
() => arity1Tearoff<bool>(42),
"NoSuchMethodError: 'arity1'\n"
"Dynamic call with unexpected type arguments. "
"Expected: 0 Actual: 1\n"
"Receiver: ${Error.safeToString(arity1Tearoff)}\n"
"Arguments: [42]");
});
test('passing too many type arguments', () {
// Compiled as `dgcall()` and throws from `checkAndCall()`.
expectThrowsNSMWithExactError(
() => genericArity2Tearoff<int, double, String>(),
"NoSuchMethodError: 'genericArity2'\n"
"Dynamic call with incorrect number of type arguments. "
"Expected: 2 Actual: 3\n"
"Receiver: ${Error.safeToString(genericArity2Tearoff)}\n"
"Arguments: []");
});
test('passing too few type arguments', () {
// Compiled as `dgcall()` and throws from `checkAndCall()`.
expectThrowsNSMWithExactError(
() => genericArity2Tearoff<int>(),
"NoSuchMethodError: 'genericArity2'\n"
"Dynamic call with incorrect number of type arguments. "
"Expected: 2 Actual: 1\n"
"Receiver: ${Error.safeToString(genericArity2Tearoff)}\n"
"Arguments: []");
});
test('already instantiated and passing type arguments ', () {
// Compiled as `dgcall()` and throws from `checkAndCall()`.
expectThrowsNSMWithExactError(
() => instantiatedTearoff<int, double>(),
"NoSuchMethodError: 'result'\n"
"Dynamic call with unexpected type arguments. "
"Expected: 0 Actual: 2\n"
"Receiver: ${Error.safeToString(instantiatedTearoff)}\n"
"Arguments: []");
});
});
group('`Function.apply()`', () {
Function arity1Tearoff = arity1;
Function requiredNamedArity1Tearoff = requiredNamedArity1;
// The code path for throwing NoSuchMethodErrors because of the wrong
// number of arguments or incorrect named arguments is shared by all
// forms of dynamic invocations. Function.apply used here for simplicity.
// Argument errors generated from `_argumentErrors()`.
test('passing too many arguments', () {
expectThrowsNSMWithExactError(
() => Function.apply(arity1Tearoff, [42, false]),
"NoSuchMethodError: 'arity1'\n"
"Dynamic call with too many positional arguments. "
"Expected: 1 Actual: 2\n"
"Receiver: ${Error.safeToString(arity1Tearoff)}\n"
"Arguments: [42, false]");
});
test('passing too few arguments', () {
expectThrowsNSMWithExactError(
() => Function.apply(arity1Tearoff, []),
"NoSuchMethodError: 'arity1'\n"
"Dynamic call with missing positional arguments. "
"Expected: 1 Actual: 0\n"
"Receiver: ${Error.safeToString(arity1Tearoff)}\n"
"Arguments: []");
});
test('passing unexpected named argument', () {
expectThrowsNSMWithExactError(
() => Function.apply(
requiredNamedArity1Tearoff, null, {#fosse: true, #cello: true}),
"NoSuchMethodError: 'requiredNamedArity1'\n"
"Dynamic call with unexpected named argument 'cello'.\n"
"Receiver: ${Error.safeToString(requiredNamedArity1Tearoff)}\n"
"Arguments: [fosse: true, cello: true]");
});
test('missing required named argument', () {
// Missing required named arguments are not an error when running
// without sound null safety.
if (hasUnsoundNullSafety) return;
expectThrowsNSMWithExactError(
() => Function.apply(requiredNamedArity1Tearoff, null),
"NoSuchMethodError: 'requiredNamedArity1'\n"
"Dynamic call with missing required named arguments: fosse.\n"
"Receiver: ${Error.safeToString(requiredNamedArity1Tearoff)}\n"
"Arguments: []");
});
});
});
group('Descriptors appearing in `NoSuchMethodError` message for ', () {
// Some extra tests for the names that appear in all the forms of dynamic
// calls. All of these tests pass wrong number of arguments just to trigger
// the error message.
test('class instance method', () {
dynamic instanceOfA = A();
Expect.throws<NoSuchMethodError>(() => instanceOfA.arity1(),
(error) => error.toString().contains("NoSuchMethodError: 'arity1'"));
});
test('class instance method tearoff', () {
dynamic tearoff = A().arity1;
Expect.throws<NoSuchMethodError>(
() => tearoff(),
(error) =>
error.toString().contains("NoSuchMethodError: 'bound arity1'"));
});
test('class instance generic method', () {
dynamic instanceOfA = A();
Expect.throws<NoSuchMethodError>(
() => instanceOfA.genericArity2(10),
(error) =>
error.toString().contains("NoSuchMethodError: 'genericArity2'"));
});
test('class instance generic method tearoff', () {
dynamic tearoff = A().genericArity2;
Expect.throws<NoSuchMethodError>(
() => tearoff(10),
(error) => error
.toString()
.contains("NoSuchMethodError: 'bound genericArity2'"));
});
test('class instance generic method tearoff instantiated', () {
dynamic tearoff = A().genericArity2<int, String>;
Expect.throws<NoSuchMethodError>(() => tearoff(10),
(error) => error.toString().contains("NoSuchMethodError: 'result'"));
});
test('class instance field', () {
dynamic instanceOfA = A();
Expect.throws<NoSuchMethodError>(
() => instanceOfA.fieldArity1(),
(error) =>
error.toString().contains("NoSuchMethodError: 'fieldArity1'"));
});
test('class static method tearoff', () {
dynamic tearoff = A.staticArity1;
Expect.throws<NoSuchMethodError>(
() => tearoff(),
(error) =>
error.toString().contains("NoSuchMethodError: 'staticArity1'"));
});
test('class static generic method tearoff', () {
dynamic tearoff = A.staticGenericArity2;
Expect.throws<NoSuchMethodError>(
() => tearoff(10),
(error) => error
.toString()
.contains("NoSuchMethodError: 'staticGenericArity2'"));
});
test('class static generic method tearoff instantiated', () {
dynamic tearoff = A.staticGenericArity2<int, double>;
Expect.throws<NoSuchMethodError>(() => tearoff(10),
(error) => error.toString().contains("NoSuchMethodError: 'result'"));
});
test('top level method tearoff', () {
dynamic tearoff = A.staticArity1;
Expect.throws<NoSuchMethodError>(
() => tearoff(),
(error) =>
error.toString().contains("NoSuchMethodError: 'staticArity1'"));
});
test('top level generic method tearoff', () {
dynamic tearoff = genericArity2;
Expect.throws<NoSuchMethodError>(
() => tearoff(10),
(error) =>
error.toString().contains("NoSuchMethodError: 'genericArity2'"));
});
test('top level generic method tearoff instantiated', () {
dynamic tearoff = genericArity2<int, String>;
Expect.throws<NoSuchMethodError>(() => tearoff(10),
(error) => error.toString().contains("NoSuchMethodError: 'result'"));
});
test('top level field', () {
Expect.throws<NoSuchMethodError>(() => fieldArity1(),
(error) => error.toString().contains("NoSuchMethodError: ''"));
});
});
}