From 3c058a8a205ac456684f2f968a23c4b4fe760f6f Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 2 Jan 2019 17:29:09 -0800 Subject: [PATCH 1/5] Add basic support for experiment flags --- bin/dartdoc.dart | 1 + lib/src/dartdoc_options.dart | 32 ++++++++---- lib/src/experiment_options.dart | 70 ++++++++++++++++++++++++++ lib/src/logging.dart | 2 +- test/experiment_options_test.dart | 83 +++++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 lib/src/experiment_options.dart create mode 100644 test/experiment_options_test.dart diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index 3626038747..840eef6865 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -120,6 +120,7 @@ Future main(List arguments) async { /// Print help if we are passed the help option. void _printHelp(ArgParser parser) { print('Generate HTML documentation for Dart libraries.\n'); + print(parser.usage); } /// Print usage information on invalid command lines. diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart index 15d7a47504..6d91029af9 100644 --- a/lib/src/dartdoc_options.dart +++ b/lib/src/dartdoc_options.dart @@ -20,6 +20,7 @@ import 'dart:io'; import 'package:analyzer/dart/element/element.dart'; import 'package:args/args.dart'; import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/src/experiment_options.dart'; import 'package:dartdoc/src/io_utils.dart'; import 'package:dartdoc/src/tool_runner.dart'; import 'package:dartdoc/src/tuple.dart'; @@ -489,7 +490,7 @@ abstract class DartdocOption { /// and requires that one of [isDir] or [isFile] is set. final bool mustExist; - DartdocOption._(this.name, this.defaultsTo, this.help, this.isDir, + DartdocOption(this.name, this.defaultsTo, this.help, this.isDir, this.isFile, this.mustExist, this._convertYamlToType) { assert(!(isDir && isFile)); if (isDir || isFile) assert(_isString || _isListString || _isMapString); @@ -674,7 +675,7 @@ class DartdocOptionFileSynth extends DartdocOption bool isFile = false, bool parentDirOverridesChild, T Function(YamlMap, pathLib.Context) convertYamlToType}) - : super._(name, null, help, isDir, isFile, mustExist, convertYamlToType) { + : super(name, null, help, isDir, isFile, mustExist, convertYamlToType) { _parentDirOverridesChild = parentDirOverridesChild; } @@ -721,7 +722,7 @@ class DartdocOptionArgSynth extends DartdocOption bool isFile = false, bool negatable = false, bool splitCommas}) - : super._(name, null, help, isDir, isFile, mustExist, null) { + : super(name, null, help, isDir, isFile, mustExist, null) { _hide = hide; _negatable = negatable; _splitCommas = splitCommas; @@ -767,7 +768,7 @@ class DartdocOptionSyntheticOnly extends DartdocOption String help = '', bool isDir = false, bool isFile = false}) - : super._(name, null, help, isDir, isFile, mustExist, null); + : super(name, null, help, isDir, isFile, mustExist, null); } abstract class DartdocSyntheticOption implements DartdocOption { @@ -801,7 +802,7 @@ typedef Future> OptionGenerator(); /// A [DartdocOption] that only contains other [DartdocOption]s and is not an option itself. class DartdocOptionSet extends DartdocOption { DartdocOptionSet(String name) - : super._(name, null, null, false, false, false, null); + : super(name, null, null, false, false, false, null); /// Asynchronous factory that is the main entry point to initialize Dartdoc /// options for use. @@ -852,7 +853,7 @@ class DartdocOptionArgOnly extends DartdocOption bool isFile = false, bool negatable = false, bool splitCommas}) - : super._(name, defaultsTo, help, isDir, isFile, mustExist, null) { + : super(name, defaultsTo, help, isDir, isFile, mustExist, null) { _hide = hide; _negatable = negatable; _splitCommas = splitCommas; @@ -888,7 +889,7 @@ class DartdocOptionArgFile extends DartdocOption bool negatable = false, bool parentDirOverridesChild: false, bool splitCommas}) - : super._(name, defaultsTo, help, isDir, isFile, mustExist, null) { + : super(name, defaultsTo, help, isDir, isFile, mustExist, null) { _abbr = abbr; _hide = hide; _negatable = negatable; @@ -938,7 +939,7 @@ class DartdocOptionFileOnly extends DartdocOption bool isFile = false, bool parentDirOverridesChild: false, T Function(YamlMap, pathLib.Context) convertYamlToType}) - : super._(name, defaultsTo, help, isDir, isFile, mustExist, + : super(name, defaultsTo, help, isDir, isFile, mustExist, convertYamlToType) { _parentDirOverridesChild = parentDirOverridesChild; } @@ -1258,12 +1259,21 @@ abstract class _DartdocArgOption implements DartdocOption { } } +/// All DartdocOptionContext mixins should implement this, as well as any other +/// DartdocOptionContext mixins they use for calculating synthetic options. +abstract class DartdocOptionContextBase { + DartdocOptionSet get optionSet; + Directory get context; +} + /// An [DartdocOptionSet] wrapped in nice accessors specific to Dartdoc, which /// automatically passes in the right directory for a given context. Usually, /// a single [ModelElement], [Package], [Category] and so forth has a single context /// and so this can be made a member variable of those structures. -class DartdocOptionContext { +class DartdocOptionContext extends DartdocOptionContextBase with DartdocExperimentOptionContext { + @override final DartdocOptionSet optionSet; + @override Directory context; // TODO(jcollins-g): Allow passing in structured data to initialize a @@ -1561,5 +1571,7 @@ Future> createDartdocOptions() async { 'exist. Executables for different platforms are specified by ' 'giving the platform name as a key, and a list of strings as the ' 'command.'), - ]; + // TODO(jcollins-g): refactor so there is a single static "create" for + // each DartdocOptionContext that traverses the inheritance tree itself. + ]..addAll(await createExperimentOptions()); } diff --git a/lib/src/experiment_options.dart b/lib/src/experiment_options.dart new file mode 100644 index 0000000000..13b610c29e --- /dev/null +++ b/lib/src/experiment_options.dart @@ -0,0 +1,70 @@ +// Copyright (c) 2018, 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. + +/// +/// Implementation of Dart language experiment option handling for dartdoc. +/// See https://github.com/dart-lang/sdk/blob/master/docs/process/experimental-flags.md. +/// +library dartdoc.experiment_options; + +import 'dart:io'; + +import 'package:dartdoc/src/dartdoc_options.dart'; + +/// Assumes the immediate parent of this option returns a list of strings that +/// we can use to calculate the value of this experiment. +class DartdocExperimentOption extends DartdocOption { + /// Whether overriding the default is allowed for this experiment. + final bool expired; + + /// Whether this option should be hidden in help text. + final bool hide; + + @override + DartdocOption> get parent => super.parent; + + DartdocExperimentOption(String name, bool defaultsTo, + {String help = '', + bool expired = false, + bool hide = false}) + : this.expired = expired, this.hide = hide, super(name, defaultsTo, help, false, false, false, null); + + @override + bool valueAt(Directory dir) { + bool value = defaultsTo; + for (String option in parent.valueAt(dir)) { + if (option == name) { + value = true; + } else if (option == 'no-${name}') { + value = false; + } + } + if (expired && value != defaultsTo) { + throw new DartdocOptionError('Experiment ${name} can not be set to ${value} -- experiment has expired'); + } + return value; + } +} + +abstract class DartdocExperimentOptionContext implements DartdocOptionContextBase { + bool get experimentConstantUpdate2018 => optionSet['enable-experiment']['constant-update-2018'].valueAt(context); + bool get experimentNonNullable => optionSet['enable-experiment']['non-nullable'].valueAt(context); + bool get experimentSetLiterals => optionSet['enable-experiment']['set-literals'].valueAt(context); +} + +// TODO(jcollins-g): Implement YAML parsing for these flags and generation +// of [DartdocExperimentOptionContext], once a YAML file is available. +Future> createExperimentOptions() async { + List experiments = [ + new DartdocExperimentOption('constant-update-2018', false, help: 'Q4 2018 Constant Update'), + new DartdocExperimentOption('non-nullable', false, help: 'Non Nullable'), + new DartdocExperimentOption('set-literals', false, help: 'Set Literals'), + ]; + + return [ + new DartdocOptionArgFile>('enable-experiment', [], + help: 'Enable or disable listed experiments.\n' + + experiments.where((e) => !e.hide).map((e) => ' [no-]${e.name}: ${e.help} (default: ${e.defaultsTo})').join('\n'))..addAll(experiments), + ]; +} diff --git a/lib/src/logging.dart b/lib/src/logging.dart index 103be7e85c..c52b05f12b 100644 --- a/lib/src/logging.dart +++ b/lib/src/logging.dart @@ -111,7 +111,7 @@ void startLogging(LoggingContext config) { } } -abstract class LoggingContext implements DartdocOptionContext { +abstract class LoggingContext implements DartdocOptionContextBase { bool get json => optionSet['json'].valueAt(context); bool get showProgress => optionSet['showProgress'].valueAt(context); } diff --git a/test/experiment_options_test.dart b/test/experiment_options_test.dart new file mode 100644 index 0000000000..c55a55d7fa --- /dev/null +++ b/test/experiment_options_test.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2018, 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. + +/// Unit tests for lib/src/experiment_options.dart. +library dartdoc.experiment_options_test; + +import 'dart:io'; + +import 'package:dartdoc/src/dartdoc_options.dart'; +import 'package:dartdoc/src/experiment_options.dart'; +import 'package:path/path.dart' as pathLib; +import 'package:test/test.dart'; + +class DartdocExperimentOptionContextTester extends DartdocOptionContext { + DartdocExperimentOptionContextTester(DartdocOptionSet optionSet, FileSystemEntity entity) : super(optionSet, entity); + + bool get experimentFakeExperiment => optionSet['enable-experiment']['fake-experiment'].valueAt(context); + bool get experimentFakeExperimentOn => optionSet['enable-experiment']['fake-experiment-on'].valueAt(context); +} + +void main() { + DartdocOptionSet experimentOptions; + Directory tempDir; + File optionsFile; + + setUp(() async { + experimentOptions = await DartdocOptionSet.fromOptionGenerators('dartdoc', [createExperimentOptions]); + List testOnlyOptions = [ + new DartdocExperimentOption('fake-experiment', false), + new DartdocExperimentOption('fake-experiment-on', true), + ]; + experimentOptions['enable-experiment']..addAll(testOnlyOptions); + }); + + setUpAll(() { + tempDir = Directory.systemTemp.createTempSync('experiment_options_test'); + optionsFile = new File(pathLib.join(tempDir.path, 'dartdoc_options.yaml'))..createSync(); + optionsFile.writeAsStringSync(''' +dartdoc: + enable-experiment: + - constant-update-2018 + - fake-experiment + - no-fake-experiment-on +'''); + }); + + tearDownAll(() { + tempDir.deleteSync(recursive: true); + }); + + group('Experimental options test', () { + test('Defaults work for all options', () { + experimentOptions.parseArguments([]); + DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester(experimentOptions, Directory.current); + expect(tester.experimentConstantUpdate2018, isFalse); + expect(tester.experimentNonNullable, isFalse); + expect(tester.experimentSetLiterals, isFalse); + expect(tester.experimentFakeExperiment, isFalse); + expect(tester.experimentFakeExperimentOn, isTrue); + }); + + test('Overriding defaults works via args', () { + experimentOptions.parseArguments(['--enable-experiment', 'non-nullable,set-literals,no-fake-experiment-on']); + DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester(experimentOptions, Directory.current); + expect(tester.experimentConstantUpdate2018, isFalse); + expect(tester.experimentNonNullable, isTrue); + expect(tester.experimentSetLiterals, isTrue); + expect(tester.experimentFakeExperiment, isFalse); + expect(tester.experimentFakeExperimentOn, isFalse); + }); + + test('Overriding defaults works via dartdoc_options.yaml', () { + experimentOptions.parseArguments([]); + DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester(experimentOptions, tempDir); + expect(tester.experimentConstantUpdate2018, isTrue); + expect(tester.experimentNonNullable, isFalse); + expect(tester.experimentSetLiterals, isFalse); + expect(tester.experimentFakeExperiment, isTrue); + expect(tester.experimentFakeExperimentOn, isFalse); + }); + }); +} \ No newline at end of file From 06178e84c383ba9caa6375999942e1a9b3694cdb Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 2 Jan 2019 17:29:22 -0800 Subject: [PATCH 2/5] dartfmt --- lib/src/dartdoc_options.dart | 7 ++++--- lib/src/experiment_options.dart | 33 ++++++++++++++++++++---------- test/experiment_options_test.dart | 34 ++++++++++++++++++++++--------- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/lib/src/dartdoc_options.dart b/lib/src/dartdoc_options.dart index 6d91029af9..bb95737670 100644 --- a/lib/src/dartdoc_options.dart +++ b/lib/src/dartdoc_options.dart @@ -490,8 +490,8 @@ abstract class DartdocOption { /// and requires that one of [isDir] or [isFile] is set. final bool mustExist; - DartdocOption(this.name, this.defaultsTo, this.help, this.isDir, - this.isFile, this.mustExist, this._convertYamlToType) { + DartdocOption(this.name, this.defaultsTo, this.help, this.isDir, this.isFile, + this.mustExist, this._convertYamlToType) { assert(!(isDir && isFile)); if (isDir || isFile) assert(_isString || _isListString || _isMapString); if (mustExist) { @@ -1270,7 +1270,8 @@ abstract class DartdocOptionContextBase { /// automatically passes in the right directory for a given context. Usually, /// a single [ModelElement], [Package], [Category] and so forth has a single context /// and so this can be made a member variable of those structures. -class DartdocOptionContext extends DartdocOptionContextBase with DartdocExperimentOptionContext { +class DartdocOptionContext extends DartdocOptionContextBase + with DartdocExperimentOptionContext { @override final DartdocOptionSet optionSet; @override diff --git a/lib/src/experiment_options.dart b/lib/src/experiment_options.dart index 13b610c29e..04a39cae73 100644 --- a/lib/src/experiment_options.dart +++ b/lib/src/experiment_options.dart @@ -25,10 +25,10 @@ class DartdocExperimentOption extends DartdocOption { DartdocOption> get parent => super.parent; DartdocExperimentOption(String name, bool defaultsTo, - {String help = '', - bool expired = false, - bool hide = false}) - : this.expired = expired, this.hide = hide, super(name, defaultsTo, help, false, false, false, null); + {String help = '', bool expired = false, bool hide = false}) + : this.expired = expired, + this.hide = hide, + super(name, defaultsTo, help, false, false, false, null); @override bool valueAt(Directory dir) { @@ -41,23 +41,29 @@ class DartdocExperimentOption extends DartdocOption { } } if (expired && value != defaultsTo) { - throw new DartdocOptionError('Experiment ${name} can not be set to ${value} -- experiment has expired'); + throw new DartdocOptionError( + 'Experiment ${name} can not be set to ${value} -- experiment has expired'); } return value; } } -abstract class DartdocExperimentOptionContext implements DartdocOptionContextBase { - bool get experimentConstantUpdate2018 => optionSet['enable-experiment']['constant-update-2018'].valueAt(context); - bool get experimentNonNullable => optionSet['enable-experiment']['non-nullable'].valueAt(context); - bool get experimentSetLiterals => optionSet['enable-experiment']['set-literals'].valueAt(context); +abstract class DartdocExperimentOptionContext + implements DartdocOptionContextBase { + bool get experimentConstantUpdate2018 => + optionSet['enable-experiment']['constant-update-2018'].valueAt(context); + bool get experimentNonNullable => + optionSet['enable-experiment']['non-nullable'].valueAt(context); + bool get experimentSetLiterals => + optionSet['enable-experiment']['set-literals'].valueAt(context); } // TODO(jcollins-g): Implement YAML parsing for these flags and generation // of [DartdocExperimentOptionContext], once a YAML file is available. Future> createExperimentOptions() async { List experiments = [ - new DartdocExperimentOption('constant-update-2018', false, help: 'Q4 2018 Constant Update'), + new DartdocExperimentOption('constant-update-2018', false, + help: 'Q4 2018 Constant Update'), new DartdocExperimentOption('non-nullable', false, help: 'Non Nullable'), new DartdocExperimentOption('set-literals', false, help: 'Set Literals'), ]; @@ -65,6 +71,11 @@ Future> createExperimentOptions() async { return [ new DartdocOptionArgFile>('enable-experiment', [], help: 'Enable or disable listed experiments.\n' + - experiments.where((e) => !e.hide).map((e) => ' [no-]${e.name}: ${e.help} (default: ${e.defaultsTo})').join('\n'))..addAll(experiments), + experiments + .where((e) => !e.hide) + .map((e) => + ' [no-]${e.name}: ${e.help} (default: ${e.defaultsTo})') + .join('\n')) + ..addAll(experiments), ]; } diff --git a/test/experiment_options_test.dart b/test/experiment_options_test.dart index c55a55d7fa..fb6ad03068 100644 --- a/test/experiment_options_test.dart +++ b/test/experiment_options_test.dart @@ -13,10 +13,14 @@ import 'package:path/path.dart' as pathLib; import 'package:test/test.dart'; class DartdocExperimentOptionContextTester extends DartdocOptionContext { - DartdocExperimentOptionContextTester(DartdocOptionSet optionSet, FileSystemEntity entity) : super(optionSet, entity); + DartdocExperimentOptionContextTester( + DartdocOptionSet optionSet, FileSystemEntity entity) + : super(optionSet, entity); - bool get experimentFakeExperiment => optionSet['enable-experiment']['fake-experiment'].valueAt(context); - bool get experimentFakeExperimentOn => optionSet['enable-experiment']['fake-experiment-on'].valueAt(context); + bool get experimentFakeExperiment => + optionSet['enable-experiment']['fake-experiment'].valueAt(context); + bool get experimentFakeExperimentOn => + optionSet['enable-experiment']['fake-experiment-on'].valueAt(context); } void main() { @@ -25,7 +29,8 @@ void main() { File optionsFile; setUp(() async { - experimentOptions = await DartdocOptionSet.fromOptionGenerators('dartdoc', [createExperimentOptions]); + experimentOptions = await DartdocOptionSet.fromOptionGenerators( + 'dartdoc', [createExperimentOptions]); List testOnlyOptions = [ new DartdocExperimentOption('fake-experiment', false), new DartdocExperimentOption('fake-experiment-on', true), @@ -35,7 +40,8 @@ void main() { setUpAll(() { tempDir = Directory.systemTemp.createTempSync('experiment_options_test'); - optionsFile = new File(pathLib.join(tempDir.path, 'dartdoc_options.yaml'))..createSync(); + optionsFile = new File(pathLib.join(tempDir.path, 'dartdoc_options.yaml')) + ..createSync(); optionsFile.writeAsStringSync(''' dartdoc: enable-experiment: @@ -52,7 +58,9 @@ dartdoc: group('Experimental options test', () { test('Defaults work for all options', () { experimentOptions.parseArguments([]); - DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester(experimentOptions, Directory.current); + DartdocExperimentOptionContextTester tester = + new DartdocExperimentOptionContextTester( + experimentOptions, Directory.current); expect(tester.experimentConstantUpdate2018, isFalse); expect(tester.experimentNonNullable, isFalse); expect(tester.experimentSetLiterals, isFalse); @@ -61,8 +69,13 @@ dartdoc: }); test('Overriding defaults works via args', () { - experimentOptions.parseArguments(['--enable-experiment', 'non-nullable,set-literals,no-fake-experiment-on']); - DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester(experimentOptions, Directory.current); + experimentOptions.parseArguments([ + '--enable-experiment', + 'non-nullable,set-literals,no-fake-experiment-on' + ]); + DartdocExperimentOptionContextTester tester = + new DartdocExperimentOptionContextTester( + experimentOptions, Directory.current); expect(tester.experimentConstantUpdate2018, isFalse); expect(tester.experimentNonNullable, isTrue); expect(tester.experimentSetLiterals, isTrue); @@ -72,7 +85,8 @@ dartdoc: test('Overriding defaults works via dartdoc_options.yaml', () { experimentOptions.parseArguments([]); - DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester(experimentOptions, tempDir); + DartdocExperimentOptionContextTester tester = + new DartdocExperimentOptionContextTester(experimentOptions, tempDir); expect(tester.experimentConstantUpdate2018, isTrue); expect(tester.experimentNonNullable, isFalse); expect(tester.experimentSetLiterals, isFalse); @@ -80,4 +94,4 @@ dartdoc: expect(tester.experimentFakeExperimentOn, isFalse); }); }); -} \ No newline at end of file +} From 274fcd4a4741db69deac4065389933024585e354 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 3 Jan 2019 10:36:01 -0800 Subject: [PATCH 3/5] Add test for throw --- test/experiment_options_test.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/experiment_options_test.dart b/test/experiment_options_test.dart index fb6ad03068..1edd3d7b75 100644 --- a/test/experiment_options_test.dart +++ b/test/experiment_options_test.dart @@ -17,6 +17,8 @@ class DartdocExperimentOptionContextTester extends DartdocOptionContext { DartdocOptionSet optionSet, FileSystemEntity entity) : super(optionSet, entity); + bool get experimentExpired => + optionSet['enable-experiment']['expired'].valueAt(context); bool get experimentFakeExperiment => optionSet['enable-experiment']['fake-experiment'].valueAt(context); bool get experimentFakeExperimentOn => @@ -34,6 +36,7 @@ void main() { List testOnlyOptions = [ new DartdocExperimentOption('fake-experiment', false), new DartdocExperimentOption('fake-experiment-on', true), + new DartdocExperimentOption('expired', true, expired: true), ]; experimentOptions['enable-experiment']..addAll(testOnlyOptions); }); @@ -64,6 +67,7 @@ dartdoc: expect(tester.experimentConstantUpdate2018, isFalse); expect(tester.experimentNonNullable, isFalse); expect(tester.experimentSetLiterals, isFalse); + expect(tester.experimentExpired, isTrue); expect(tester.experimentFakeExperiment, isFalse); expect(tester.experimentFakeExperimentOn, isTrue); }); @@ -79,6 +83,7 @@ dartdoc: expect(tester.experimentConstantUpdate2018, isFalse); expect(tester.experimentNonNullable, isTrue); expect(tester.experimentSetLiterals, isTrue); + expect(tester.experimentExpired, isTrue); expect(tester.experimentFakeExperiment, isFalse); expect(tester.experimentFakeExperimentOn, isFalse); }); @@ -90,8 +95,21 @@ dartdoc: expect(tester.experimentConstantUpdate2018, isTrue); expect(tester.experimentNonNullable, isFalse); expect(tester.experimentSetLiterals, isFalse); + expect(tester.experimentExpired, isTrue); expect(tester.experimentFakeExperiment, isTrue); expect(tester.experimentFakeExperimentOn, isFalse); }); + + test('Overriding default of an expired experiment throws', () { + experimentOptions.parseArguments([ + '--enable-experiment', + 'no-expired', + ]); + DartdocExperimentOptionContextTester tester = + new DartdocExperimentOptionContextTester( + experimentOptions, Directory.current); + expect(() => tester.experimentExpired, + throwsA(const TypeMatcher())); + }); }); } From d0357c4cab43732aeb7eb561e2bc29d4a76b0152 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 7 Jan 2019 10:05:04 -0800 Subject: [PATCH 4/5] Synchronize analyzer and dartdoc experiment implementations --- lib/src/experiment_options.dart | 68 +++++-------------------------- lib/src/model.dart | 3 ++ test/experiment_options_test.dart | 55 +++---------------------- 3 files changed, 19 insertions(+), 107 deletions(-) diff --git a/lib/src/experiment_options.dart b/lib/src/experiment_options.dart index 04a39cae73..86b0b813e2 100644 --- a/lib/src/experiment_options.dart +++ b/lib/src/experiment_options.dart @@ -8,74 +8,28 @@ /// library dartdoc.experiment_options; -import 'dart:io'; - +import 'package:analyzer/src/dart/analysis/experiments.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; -/// Assumes the immediate parent of this option returns a list of strings that -/// we can use to calculate the value of this experiment. -class DartdocExperimentOption extends DartdocOption { - /// Whether overriding the default is allowed for this experiment. - final bool expired; - - /// Whether this option should be hidden in help text. - final bool hide; - - @override - DartdocOption> get parent => super.parent; - - DartdocExperimentOption(String name, bool defaultsTo, - {String help = '', bool expired = false, bool hide = false}) - : this.expired = expired, - this.hide = hide, - super(name, defaultsTo, help, false, false, false, null); - - @override - bool valueAt(Directory dir) { - bool value = defaultsTo; - for (String option in parent.valueAt(dir)) { - if (option == name) { - value = true; - } else if (option == 'no-${name}') { - value = false; - } - } - if (expired && value != defaultsTo) { - throw new DartdocOptionError( - 'Experiment ${name} can not be set to ${value} -- experiment has expired'); - } - return value; - } -} - abstract class DartdocExperimentOptionContext implements DartdocOptionContextBase { - bool get experimentConstantUpdate2018 => - optionSet['enable-experiment']['constant-update-2018'].valueAt(context); - bool get experimentNonNullable => - optionSet['enable-experiment']['non-nullable'].valueAt(context); - bool get experimentSetLiterals => - optionSet['enable-experiment']['set-literals'].valueAt(context); + List get enableExperiment => optionSet['enable-experiment'].valueAt(context); + ExperimentStatus get experimentStatus => optionSet['experimentStatus'].valueAt(context); } // TODO(jcollins-g): Implement YAML parsing for these flags and generation // of [DartdocExperimentOptionContext], once a YAML file is available. Future> createExperimentOptions() async { - List experiments = [ - new DartdocExperimentOption('constant-update-2018', false, - help: 'Q4 2018 Constant Update'), - new DartdocExperimentOption('non-nullable', false, help: 'Non Nullable'), - new DartdocExperimentOption('set-literals', false, help: 'Set Literals'), - ]; - return [ - new DartdocOptionArgFile>('enable-experiment', [], + // TODO(jcollins-g): Consider loading experiment values from dartdoc_options.yaml? + new DartdocOptionArgOnly>('enable-experiment', [], help: 'Enable or disable listed experiments.\n' + - experiments - .where((e) => !e.hide) + ExperimentStatus.knownFeatures.values + .where((e) => e.documentation != null) .map((e) => - ' [no-]${e.name}: ${e.help} (default: ${e.defaultsTo})') - .join('\n')) - ..addAll(experiments), + ' [no-]${e.enableString}: ${e.documentation} (default: ${e.isEnabledByDefault})') + .join('\n')), + new DartdocOptionSyntheticOnly('experimentStatus', + (option, dir) => new ExperimentStatus.fromStrings(option.parent['enable-experiment'].valueAt(dir))), ]; } diff --git a/lib/src/model.dart b/lib/src/model.dart index 13bace5752..b72ee5b525 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -6458,6 +6458,9 @@ class PackageBuilder { AnalysisDriverScheduler scheduler = new AnalysisDriverScheduler(log); AnalysisOptionsImpl options = new AnalysisOptionsImpl(); + // TODO(jcollins-g): pass in an ExperimentStatus instead? + options.enabledExperiments = config.enableExperiment; + // TODO(jcollins-g): Make use of currently not existing API for managing // many AnalysisDrivers // TODO(jcollins-g): make use of DartProject isApi() diff --git a/test/experiment_options_test.dart b/test/experiment_options_test.dart index 1edd3d7b75..811112e7b8 100644 --- a/test/experiment_options_test.dart +++ b/test/experiment_options_test.dart @@ -16,13 +16,6 @@ class DartdocExperimentOptionContextTester extends DartdocOptionContext { DartdocExperimentOptionContextTester( DartdocOptionSet optionSet, FileSystemEntity entity) : super(optionSet, entity); - - bool get experimentExpired => - optionSet['enable-experiment']['expired'].valueAt(context); - bool get experimentFakeExperiment => - optionSet['enable-experiment']['fake-experiment'].valueAt(context); - bool get experimentFakeExperimentOn => - optionSet['enable-experiment']['fake-experiment-on'].valueAt(context); } void main() { @@ -33,12 +26,6 @@ void main() { setUp(() async { experimentOptions = await DartdocOptionSet.fromOptionGenerators( 'dartdoc', [createExperimentOptions]); - List testOnlyOptions = [ - new DartdocExperimentOption('fake-experiment', false), - new DartdocExperimentOption('fake-experiment-on', true), - new DartdocExperimentOption('expired', true, expired: true), - ]; - experimentOptions['enable-experiment']..addAll(testOnlyOptions); }); setUpAll(() { @@ -64,52 +51,20 @@ dartdoc: DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester( experimentOptions, Directory.current); - expect(tester.experimentConstantUpdate2018, isFalse); - expect(tester.experimentNonNullable, isFalse); - expect(tester.experimentSetLiterals, isFalse); - expect(tester.experimentExpired, isTrue); - expect(tester.experimentFakeExperiment, isFalse); - expect(tester.experimentFakeExperimentOn, isTrue); + expect(tester.experimentStatus.constant_update_2018, isFalse); + expect(tester.experimentStatus.set_literals, isFalse); }); test('Overriding defaults works via args', () { experimentOptions.parseArguments([ '--enable-experiment', - 'non-nullable,set-literals,no-fake-experiment-on' - ]); - DartdocExperimentOptionContextTester tester = - new DartdocExperimentOptionContextTester( - experimentOptions, Directory.current); - expect(tester.experimentConstantUpdate2018, isFalse); - expect(tester.experimentNonNullable, isTrue); - expect(tester.experimentSetLiterals, isTrue); - expect(tester.experimentExpired, isTrue); - expect(tester.experimentFakeExperiment, isFalse); - expect(tester.experimentFakeExperimentOn, isFalse); - }); - - test('Overriding defaults works via dartdoc_options.yaml', () { - experimentOptions.parseArguments([]); - DartdocExperimentOptionContextTester tester = - new DartdocExperimentOptionContextTester(experimentOptions, tempDir); - expect(tester.experimentConstantUpdate2018, isTrue); - expect(tester.experimentNonNullable, isFalse); - expect(tester.experimentSetLiterals, isFalse); - expect(tester.experimentExpired, isTrue); - expect(tester.experimentFakeExperiment, isTrue); - expect(tester.experimentFakeExperimentOn, isFalse); - }); - - test('Overriding default of an expired experiment throws', () { - experimentOptions.parseArguments([ - '--enable-experiment', - 'no-expired', + 'constant-update-2018,set-literals' ]); DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester( experimentOptions, Directory.current); - expect(() => tester.experimentExpired, - throwsA(const TypeMatcher())); + expect(tester.experimentStatus.constant_update_2018, isTrue); + expect(tester.experimentStatus.set_literals, isTrue); }); }); } From ec85e3dcd11e986cce03b76408c1ffd9c0f2387b Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 7 Jan 2019 10:06:09 -0800 Subject: [PATCH 5/5] dartfmt --- lib/src/experiment_options.dart | 12 ++++++++---- test/dartdoc_test.dart | 12 +++++++++--- test/experiment_options_test.dart | 6 ++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/src/experiment_options.dart b/lib/src/experiment_options.dart index 86b0b813e2..0d9fa2f410 100644 --- a/lib/src/experiment_options.dart +++ b/lib/src/experiment_options.dart @@ -13,8 +13,10 @@ import 'package:dartdoc/src/dartdoc_options.dart'; abstract class DartdocExperimentOptionContext implements DartdocOptionContextBase { - List get enableExperiment => optionSet['enable-experiment'].valueAt(context); - ExperimentStatus get experimentStatus => optionSet['experimentStatus'].valueAt(context); + List get enableExperiment => + optionSet['enable-experiment'].valueAt(context); + ExperimentStatus get experimentStatus => + optionSet['experimentStatus'].valueAt(context); } // TODO(jcollins-g): Implement YAML parsing for these flags and generation @@ -29,7 +31,9 @@ Future> createExperimentOptions() async { .map((e) => ' [no-]${e.enableString}: ${e.documentation} (default: ${e.isEnabledByDefault})') .join('\n')), - new DartdocOptionSyntheticOnly('experimentStatus', - (option, dir) => new ExperimentStatus.fromStrings(option.parent['enable-experiment'].valueAt(dir))), + new DartdocOptionSyntheticOnly( + 'experimentStatus', + (option, dir) => new ExperimentStatus.fromStrings( + option.parent['enable-experiment'].valueAt(dir))), ]; } diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart index 0e405e8b79..ab02d48789 100644 --- a/test/dartdoc_test.dart +++ b/test/dartdoc_test.dart @@ -154,9 +154,15 @@ void main() { test('help prints command line args', () async { List outputLines = []; - await subprocessLauncher.runStreamed(Platform.resolvedExecutable, [dartdocPath, '--help'], perLine: outputLines.add); - expect(outputLines, contains('Generate HTML documentation for Dart libraries.')); - expect(outputLines.join('\n'), contains(new RegExp('^-h, --help[ ]+Show command help.', multiLine: true))) ; + await subprocessLauncher.runStreamed( + Platform.resolvedExecutable, [dartdocPath, '--help'], + perLine: outputLines.add); + expect(outputLines, + contains('Generate HTML documentation for Dart libraries.')); + expect( + outputLines.join('\n'), + contains(new RegExp('^-h, --help[ ]+Show command help.', + multiLine: true))); }); test('Validate missing FLUTTER_ROOT exception is clean', () async { diff --git a/test/experiment_options_test.dart b/test/experiment_options_test.dart index 811112e7b8..6772bfdbce 100644 --- a/test/experiment_options_test.dart +++ b/test/experiment_options_test.dart @@ -56,10 +56,8 @@ dartdoc: }); test('Overriding defaults works via args', () { - experimentOptions.parseArguments([ - '--enable-experiment', - 'constant-update-2018,set-literals' - ]); + experimentOptions.parseArguments( + ['--enable-experiment', 'constant-update-2018,set-literals']); DartdocExperimentOptionContextTester tester = new DartdocExperimentOptionContextTester( experimentOptions, Directory.current);