MockBuilder: Do not be concerned with method modifiers.

An overriding method and an overridden method need not have matching modifiers. For example, an async method can be overridden by a synchronous method; the modifiers are not part of the signature.

Introduce support for returning a simple dummy Iterable (an empty List) and a simple dummy Stream (an empty Stream).

Improve existing tests and introduce new tests.

PiperOrigin-RevId: 315357440
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index a543527..e1fe972 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -15,6 +15,7 @@
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart' as analyzer;
+import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer/dart/element/type_system.dart';
 import 'package:build/build.dart';
 import 'package:code_builder/code_builder.dart';
@@ -62,6 +63,7 @@
     final mockLibrary = Library((b) {
       var mockLibraryInfo = _MockLibraryInfo(classesToMock,
           sourceLibIsNonNullable: sourceLibIsNonNullable,
+          typeProvider: entryLib.typeProvider,
           typeSystem: entryLib.typeSystem);
       b.body.addAll(mockLibraryInfo.fakeClasses);
       b.body.addAll(mockLibraryInfo.mockClasses);
@@ -89,6 +91,9 @@
 class _MockLibraryInfo {
   final bool sourceLibIsNonNullable;
 
+  /// The type provider which applies to the source library.
+  final TypeProvider typeProvider;
+
   /// The type system which applies to the source library.
   final TypeSystem typeSystem;
 
@@ -108,7 +113,7 @@
   /// Build mock classes for [classesToMock], a list of classes obtained from a
   /// `@GenerateMocks` annotation.
   _MockLibraryInfo(List<DartObject> classesToMock,
-      {this.sourceLibIsNonNullable, this.typeSystem}) {
+      {this.sourceLibIsNonNullable, this.typeProvider, this.typeSystem}) {
     for (final classToMock in classesToMock) {
       final dartTypeToMock = classToMock.toTypeValue();
       if (dartTypeToMock == null) {
@@ -225,7 +230,6 @@
   // TODO(srawlins): Include widening tests for typedefs, old-style function
   // parameters, function types.
   void _buildOverridingMethod(MethodBuilder builder, MethodElement method) {
-    // TODO(srawlins): generator methods like async*, sync*.
     var name = method.displayName;
     if (method.isOperator) name = 'operator$name';
     builder
@@ -236,13 +240,6 @@
       builder.types.addAll(method.typeParameters.map(_typeParameterReference));
     }
 
-    if (method.isAsynchronous) {
-      builder.modifier =
-          method.isGenerator ? MethodModifier.asyncStar : MethodModifier.async;
-    } else if (method.isGenerator) {
-      builder.modifier = MethodModifier.syncStar;
-    }
-
     // These two variables store the arguments that will be passed to the
     // [Invocation] built for `noSuchMethod`.
     final invocationPositionalArgs = <Expression>[];
@@ -293,6 +290,8 @@
           .call([_dummyValue(typeArgument)]);
     } else if (type.isDartCoreInt) {
       return literalNum(0);
+    } else if (type.isDartCoreIterable) {
+      return literalList([]);
     } else if (type.isDartCoreList) {
       return literalList([]);
     } else if (type.isDartCoreMap) {
@@ -303,6 +302,8 @@
       // This is perhaps a dangerous hack. The code, `{}`, is parsed as a Set
       // literal if it is used in a context which explicitly expects a Set.
       return literalMap({});
+    } else if (type.element?.declaration == typeProvider.streamElement) {
+      return refer('Stream').property('empty').call([]);
     } else if (type.isDartCoreString) {
       return literalString('');
     } else {
diff --git a/test/builder_test.dart b/test/builder_test.dart
index 11273ee..fafa3a1 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -100,48 +100,63 @@
     );
   });
 
-  test('generates a mock class and overrides method parameters', () async {
-    await _testWithNonNullable(
-      {
-        ...annotationsAsset,
-        ...simpleTestAsset,
-        'foo|lib/foo.dart': dedent(r'''
-        class Foo {
-          dynamic a(int m, String n) => n + 1;
-          dynamic b(List<int> list) => list.length;
-          void c(String one, [String two, String three = ""]) => print('$one$two$three');
-          void d(String one, {String two, String three = ""}) => print('$one$two$three');
-          Future<void> e(String s) async => print(s);
-          // TODO(srawlins): Figure out async*; doesn't work yet. `isGenerator`
-          // does not appear to be working.
-          // Stream<void> f(String s) async* => print(s);
-          // Iterable<void> g(String s) sync* => print(s);
-        }
-        '''),
-      },
-      outputs: {
-        'foo|test/foo_test.mocks.dart': dedent(r'''
-        import 'package:mockito/mockito.dart' as _i1;
-        import 'package:foo/foo.dart' as _i2;
-        import 'dart:async' as _i3;
+  test('overrides methods, matching optional positional parameters', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+          void m(int a, [int b, int c = 0]) {}
+      }
+      '''),
+      _containsAllOf('void m(int? a, [int? b, int? c = 0]) =>',
+          'super.noSuchMethod(Invocation.method(#m, [a, b, c]));'),
+    );
+  });
 
-        /// A class which mocks [Foo].
-        ///
-        /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends _i1.Mock implements _i2.Foo {
-          dynamic a(int? m, String? n) =>
-              super.noSuchMethod(Invocation.method(#a, [m, n]));
-          dynamic b(List<int>? list) =>
-              super.noSuchMethod(Invocation.method(#b, [list]));
-          void c(String? one, [String? two, String? three = ""]) =>
-              super.noSuchMethod(Invocation.method(#c, [one, two, three]));
-          void d(String? one, {String? two, String? three = ""}) => super
-              .noSuchMethod(Invocation.method(#d, [one], {#two: two, #three: three}));
-          _i3.Future<void> e(String? s) async =>
-              super.noSuchMethod(Invocation.method(#e, [s]), Future.value(null));
-        }
-        '''),
-      },
+  test('overrides methods, matching named parameters', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+          void m(int a, {int b, int c = 0}) {}
+      }
+      '''),
+      _containsAllOf('void m(int? a, {int? b, int? c = 0}) =>',
+          'super.noSuchMethod(Invocation.method(#m, [a], {#b: b, #c: c}));'),
+    );
+  });
+
+  test('overrides async methods legally', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        Future<void> m() async => print(s);
+      }
+      '''),
+      _containsAllOf('_i3.Future<void> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), Future.value(null));'),
+    );
+  });
+
+  test('overrides async* methods legally', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        Stream<int> m() async* { yield 7; }
+      }
+      '''),
+      _containsAllOf('_i3.Stream<int> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), Stream.empty());'),
+    );
+  });
+
+  test('overrides sync* methods legally', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        Iterable<int> m() sync* { yield 7; }
+      }
+      '''),
+      _containsAllOf('Iterable<int> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), []);'),
     );
   });
 
@@ -558,32 +573,61 @@
     );
   });
 
-  test('does not override methods with a nullable return type', () async {
-    await _testWithNonNullable(
-      {
-        ...annotationsAsset,
-        ...simpleTestAsset,
-        'foo|lib/foo.dart': dedent(r'''
-          abstract class Foo {
-            void a();
-            b();
-            dynamic c();
-            void d();
-            int? f();
-          }
-          '''),
-      },
-      outputs: {
-        'foo|test/foo_test.mocks.dart': dedent(r'''
-          import 'package:mockito/mockito.dart' as _i1;
-          import 'package:foo/foo.dart' as _i2;
+  test('does not override methods with a void return type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          void m();
+        }
+        '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
 
-          /// A class which mocks [Foo].
-          ///
-          /// See the documentation for Mockito's code generation for more information.
-          class MockFoo extends _i1.Mock implements _i2.Foo {}
-          '''),
-      },
+  test('does not override methods with an implicit dynamic return type',
+      () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          m();
+        }
+        '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('does not override methods with an explicit dynamic return type',
+      () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          dynamic m();
+        }
+        '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('does not override methods with a nullable return type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          int? m();
+        }
+        '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('overrides methods with a non-nullable return type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          int m();
+        }
+        '''),
+      _containsAllOf(
+          'int m() => super.noSuchMethod(Invocation.method(#m, []), 0);'),
     );
   });
 
@@ -876,11 +920,11 @@
     await _expectSingleNonNullableOutput(
       dedent(r'''
       class Foo {
-        Future<bool> m1() async => false;
+        Future<bool> m() async => false;
       }
       '''),
-      _containsAllOf('_i3.Future<bool> m1() async =>',
-          'super.noSuchMethod(Invocation.method(#m1, []), Future.value(false));'),
+      _containsAllOf('_i3.Future<bool> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), Future.value(false));'),
     );
   });