Skip to content

Rotate formerly experimental features into regular test package #2557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 2 additions & 212 deletions test/end2end/model_special_cases_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,10 @@ import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/package_config_provider.dart';
import 'package:dartdoc/src/package_meta.dart';
import 'package:dartdoc/src/special_elements.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart';

import '../src/utils.dart' as utils;

final String _platformVersionString = Platform.version.split(' ').first;
final Version _platformVersion = Version.parse(_platformVersionString);

final _testPackageGraphExperimentsMemo = AsyncMemoizer<PackageGraph>();
Future<PackageGraph> get _testPackageGraphExperiments =>
_testPackageGraphExperimentsMemo.runOnce(() => utils.bootBasicPackage(
'testing/test_package_experiments',
pubPackageMetaProvider,
PhysicalPackageConfigProvider(),
additionalArguments: [
'--enable-experiment',
'non-nullable',
'--no-link-to-remote'
]));

final _testPackageGraphGinormousMemo = AsyncMemoizer<PackageGraph>();
Future<PackageGraph> get _testPackageGraphGinormous =>
_testPackageGraphGinormousMemo.runOnce(() => utils.bootBasicPackage(
Expand Down Expand Up @@ -74,205 +58,11 @@ void main() {
exit(1);
}

// This doesn't have the `max` because Null safety is supposed to work after
// this version, and if the `max` is placed here we'll silently pass 2.10
// stable if we haven't figured out how to switch on Null safety outside of
// dev builds as specified in #2148.
final _nullSafetyExperimentAllowed =
VersionRange(min: Version.parse('2.9.0-9.0.dev'), includeMin: true);

// Experimental features not yet enabled by default. Move tests out of this
// block when the feature is enabled by default.
group('Experiments', () {
Library lateFinalWithoutInitializer,
nullSafetyClassMemberDeclarations,
optOutOfNullSafety,
nullableElements;
Class b;
Class c;

setUpAll(() async {
lateFinalWithoutInitializer = (await _testPackageGraphExperiments)
.libraries
.firstWhere((lib) => lib.name == 'late_final_without_initializer');
nullSafetyClassMemberDeclarations = (await _testPackageGraphExperiments)
.libraries
.firstWhere((lib) => lib.name == 'nnbd_class_member_declarations');
optOutOfNullSafety = (await _testPackageGraphExperiments)
.libraries
.firstWhere((lib) => lib.name == 'opt_out_of_nnbd');
nullableElements = (await _testPackageGraphExperiments)
.libraries
.firstWhere((lib) => lib.name == 'nullable_elements');
b = nullSafetyClassMemberDeclarations.allClasses
.firstWhere((c) => c.name == 'B');
c = nullSafetyClassMemberDeclarations.allClasses
.firstWhere((c) => c.name == 'C');
});

test('isNullSafety is set correctly for libraries', () {
expect(lateFinalWithoutInitializer.isNullSafety, isTrue);
expect(optOutOfNullSafety.isNullSafety, isFalse);
});

test('method parameters with required', () {
var m1 = b.instanceMethods.firstWhere((m) => m.name == 'm1');
var p1 = m1.allParameters.firstWhere((p) => p.name == 'p1');
var p2 = m1.allParameters.firstWhere((p) => p.name == 'p2');
expect(p1.isRequiredNamed, isTrue);
expect(p2.isRequiredNamed, isFalse);
expect(p2.isNamed, isTrue);

expect(
m1.linkedParamsLines,
equals(
'<ol class="parameter-list"><li><span class="parameter" id="m1-param-some"><span class="type-annotation">int</span> <span class="parameter-name">some</span>, </span></li>\n'
'<li><span class="parameter" id="m1-param-regular"><span class="type-annotation">dynamic</span> <span class="parameter-name">regular</span>, </span></li>\n'
'<li><span class="parameter" id="m1-param-parameters"><span>covariant</span> <span class="type-annotation">dynamic</span> <span class="parameter-name">parameters</span>, </span></li>\n'
'<li><span class="parameter" id="m1-param-p1">{<span>required</span> <span class="type-annotation">dynamic</span> <span class="parameter-name">p1</span>, </span></li>\n'
'<li><span class="parameter" id="m1-param-p2"><span class="type-annotation">int</span> <span class="parameter-name">p2</span> = <span class="default-value">3</span>, </span></li>\n'
'<li><span class="parameter" id="m1-param-p3"><span>required</span> <span>covariant</span> <span class="type-annotation">dynamic</span> <span class="parameter-name">p3</span>, </span></li>\n'
'<li><span class="parameter" id="m1-param-p4"><span>required</span> <span>covariant</span> <span class="type-annotation">int</span> <span class="parameter-name">p4</span>}</span></li>\n'
'</ol>'));
});

test('verify no regression on ordinary optionals', () {
var m2 = b.instanceMethods.firstWhere((m) => m.name == 'm2');
var sometimes = m2.allParameters.firstWhere((p) => p.name == 'sometimes');
var optionals = m2.allParameters.firstWhere((p) => p.name == 'optionals');
expect(sometimes.isRequiredNamed, isFalse);
expect(sometimes.isRequiredPositional, isTrue);
expect(sometimes.isOptionalPositional, isFalse);
expect(optionals.isRequiredNamed, isFalse);
expect(optionals.isRequiredPositional, isFalse);
expect(optionals.isOptionalPositional, isTrue);

expect(
m2.linkedParamsLines,
equals(
'<ol class="parameter-list"><li><span class="parameter" id="m2-param-sometimes"><span class="type-annotation">int</span> <span class="parameter-name">sometimes</span>, </span></li>\n'
'<li><span class="parameter" id="m2-param-we"><span class="type-annotation">dynamic</span> <span class="parameter-name">we</span>, </span></li>\n'
'<li><span class="parameter" id="m2-param-have">[<span class="type-annotation">String</span> <span class="parameter-name">have</span>, </span></li>\n'
'<li><span class="parameter" id="m2-param-optionals"><span class="type-annotation">double</span> <span class="parameter-name">optionals</span>]</span></li>\n'
'</ol>'));
});

test('anonymous callback parameters are correctly marked as nullable', () {
var m3 = c.instanceMethods.firstWhere((m) => m.name == 'm3');
var listen = m3.allParameters.firstWhere((p) => p.name == 'listen');
var onDone = m3.allParameters.firstWhere((p) => p.name == 'onDone');
expect(listen.isRequiredPositional, isTrue);
expect(onDone.isNamed, isTrue);

expect(
m3.linkedParamsLines,
equals(
'<ol class="parameter-list"><li><span class="parameter" id="m3-param-listen"><span class="type-annotation">void</span> <span class="parameter-name">listen</span>(<ol class="parameter-list"><li><span class="parameter" id="param-t"><span class="type-annotation">int</span> <span class="parameter-name">t</span></span></li>\n'
'</ol>\n'
')?, </span></li>\n'
'<li><span class="parameter" id="m3-param-onDone">{<span class="type-annotation">void</span> <span class="parameter-name">onDone</span>(<ol class="parameter-list"></ol>\n'
')?}</span></li>\n'
'</ol>'));
});

test('Late final class member test', () {
var c = lateFinalWithoutInitializer.allClasses
.firstWhere((c) => c.name == 'C');
var a = c.instanceFields.firstWhere((f) => f.name == 'a');
var b = c.instanceFields.firstWhere((f) => f.name == 'b');
var cField = c.instanceFields.firstWhere((f) => f.name == 'cField');
var dField = c.instanceFields.firstWhere((f) => f.name == 'dField');

// If Null safety isn't enabled, fields named 'late' come back from the
// analyzer instead of setting up 'isLate'.
expect(c.instanceFields.any((f) => f.name == 'late'), isFalse);

expect(a.modelType.returnType.name, equals('dynamic'));
expect(a.isLate, isTrue);
expect(a.features, contains('late'));

expect(b.modelType.returnType.name, equals('int'));
expect(b.isLate, isTrue);
expect(b.features, contains('late'));

expect(cField.modelType.returnType.name, equals('dynamic'));
expect(cField.isLate, isTrue);
expect(cField.features, contains('late'));

expect(dField.modelType.returnType.name, equals('double'));
expect(dField.isLate, isTrue);
expect(dField.features, contains('late'));
});

test('Late final top level variables', () {
var initializeMe = lateFinalWithoutInitializer.publicProperties
.firstWhere((v) => v.name == 'initializeMe');
expect(initializeMe.modelType.returnType.name, equals('String'));
expect(initializeMe.isLate, isTrue);
expect(initializeMe.features, contains('late'));
});

test('Opt out of Null safety', () {
var notOptedIn = optOutOfNullSafety.publicProperties
.firstWhere((v) => v.name == 'notOptedIn');
expect(notOptedIn.isNullSafety, isFalse);
expect(notOptedIn.modelType.nullabilitySuffix, isEmpty);
});

test('complex nullable elements are detected and rendered correctly', () {
var complexNullableMembers = nullableElements.allClasses
.firstWhere((c) => c.name == 'ComplexNullableMembers');
var aComplexType = complexNullableMembers.allFields
.firstWhere((f) => f.name == 'aComplexType');
var aComplexSetterOnlyType = complexNullableMembers.allFields
.firstWhere((f) => f.name == 'aComplexSetterOnlyType');
expect(complexNullableMembers.isNullSafety, isTrue);
expect(
complexNullableMembers.nameWithGenerics,
equals(
'ComplexNullableMembers&lt;<wbr><span class=\"type-parameter\">T extends String?</span>&gt;'));
expect(
aComplexType.linkedReturnType,
equals(
'Map<span class="signature">&lt;<wbr><span class="type-parameter">T?</span>, <span class="type-parameter">String?</span>&gt;</span>'));
expect(aComplexSetterOnlyType.linkedReturnType, equals(
// TODO(jcollins-g): fix wrong span class for setter-only return type (#2226)
'<span class="parameter" id="aComplexSetterOnlyType=-param-value"><span class="type-annotation">List<span class="signature">&lt;<wbr><span class="type-parameter">Map<span class="signature">&lt;<wbr><span class="type-parameter">T?</span>, <span class="type-parameter">String?</span>&gt;</span>?</span>&gt;</span></span></span>'));
});

test('simple nullable elements are detected and rendered correctly', () {
var nullableMembers = nullableElements.allClasses
.firstWhere((c) => c.name == 'NullableMembers');
var initialized =
nullableMembers.allFields.firstWhere((f) => f.name == 'initialized');
var nullableField = nullableMembers.allFields
.firstWhere((f) => f.name == 'nullableField');
var methodWithNullables = nullableMembers.publicInstanceMethods
.firstWhere((f) => f.name == 'methodWithNullables');
var operatorStar = nullableMembers.publicInstanceOperators
.firstWhere((f) => f.name == 'operator *');
expect(nullableMembers.isNullSafety, isTrue);
expect(
nullableField.linkedReturnType,
equals(
'Iterable<span class=\"signature\">&lt;<wbr><span class=\"type-parameter\">BigInt</span>&gt;</span>?'));
expect(
methodWithNullables.linkedParams,
equals(
'<span class="parameter" id="methodWithNullables-param-foo"><span class="type-annotation">String?</span> <span class="parameter-name">foo</span></span>'));
expect(methodWithNullables.linkedReturnType, equals('int?'));
expect(
initialized.linkedReturnType,
equals(
'Map<span class="signature">&lt;<wbr><span class="type-parameter">String</span>, <span class="type-parameter">Map</span>&gt;</span>?'));
expect(
operatorStar.linkedParams,
equals(
'<span class="parameter" id="*-param-nullableOther"><span class="type-annotation"><a href="%%__HTMLBASE_dartdoc_internal__%%nullable_elements/NullableMembers-class.html">NullableMembers</a>?</span> <span class="parameter-name">nullableOther</span></span>'));
});
},
skip: (!_nullSafetyExperimentAllowed.allows(_platformVersion) &&
!_platformVersionString.contains('edge')));
setUpAll(() async {});
});

group('HTML Injection when allowed', () {
Class htmlInjection;
Expand Down
Loading