An API for tracking software licenses. (#4711)
This commit is contained in:
parent
bbf31cd3b7
commit
e5fb2fb0d3
@ -13,5 +13,6 @@ export 'src/foundation/assertions.dart';
|
|||||||
export 'src/foundation/basic_types.dart';
|
export 'src/foundation/basic_types.dart';
|
||||||
export 'src/foundation/binding.dart';
|
export 'src/foundation/binding.dart';
|
||||||
export 'src/foundation/change_notifier.dart';
|
export 'src/foundation/change_notifier.dart';
|
||||||
|
export 'src/foundation/licenses.dart';
|
||||||
export 'src/foundation/print.dart';
|
export 'src/foundation/print.dart';
|
||||||
export 'src/foundation/synchronous_future.dart';
|
export 'src/foundation/synchronous_future.dart';
|
||||||
|
@ -11,6 +11,7 @@ import 'package:meta/meta.dart';
|
|||||||
|
|
||||||
import 'assertions.dart';
|
import 'assertions.dart';
|
||||||
import 'basic_types.dart';
|
import 'basic_types.dart';
|
||||||
|
import 'licenses.dart';
|
||||||
|
|
||||||
/// Signature for service extensions.
|
/// Signature for service extensions.
|
||||||
///
|
///
|
||||||
@ -74,9 +75,14 @@ abstract class BindingBase {
|
|||||||
/// `initInstances()`.
|
/// `initInstances()`.
|
||||||
void initInstances() {
|
void initInstances() {
|
||||||
assert(!_debugInitialized);
|
assert(!_debugInitialized);
|
||||||
|
LicenseRegistry.addLicense(_addLicenses);
|
||||||
assert(() { _debugInitialized = true; return true; });
|
assert(() { _debugInitialized = true; return true; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Iterable<LicenseEntry> _addLicenses() sync* {
|
||||||
|
// TODO(ianh): Populate the license registry.
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the binding is initialized, to register service
|
/// Called when the binding is initialized, to register service
|
||||||
/// extensions.
|
/// extensions.
|
||||||
///
|
///
|
||||||
|
264
packages/flutter/lib/src/foundation/licenses.dart
Normal file
264
packages/flutter/lib/src/foundation/licenses.dart
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
/// Signature for callbacks passed to [LicenseRegistry.addLicense].
|
||||||
|
typedef Iterable<LicenseEntry> LicenseEntryCollector();
|
||||||
|
|
||||||
|
/// A string that represents one paragraph in a [LicenseEntry].
|
||||||
|
///
|
||||||
|
/// See [LicenseEntry.paragraphs].
|
||||||
|
class LicenseParagraph {
|
||||||
|
/// Creates a string for a license entry paragraph.
|
||||||
|
const LicenseParagraph(this.text, this.indent);
|
||||||
|
|
||||||
|
/// The text of the paragraph. Should not have any leading or trailing whitespace.
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
/// How many steps of indentation the paragraph has.
|
||||||
|
///
|
||||||
|
/// * 0 means the paragraph is not indented.
|
||||||
|
/// * 1 means the paragraph is indented one unit of indentation.
|
||||||
|
/// * 2 means the paragraph is indented two units of indentation.
|
||||||
|
///
|
||||||
|
/// ...and so forth.
|
||||||
|
///
|
||||||
|
/// In addition, the special value [centeredIndent] can be used to indicate
|
||||||
|
/// that rather than being indented, the paragraph is centered.
|
||||||
|
final int indent; // can be set to centeredIndent
|
||||||
|
|
||||||
|
/// A constant that represents "centered" alignment for [indent].
|
||||||
|
static const int centeredIndent = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A license that covers part of the application's software or assets, to show
|
||||||
|
/// in an interface such as the [LicensePage].
|
||||||
|
///
|
||||||
|
/// For optimal performance, [LicenseEntry] objects should only be created on
|
||||||
|
/// demand in [LicenseEntryCollector] callbacks passed to
|
||||||
|
/// [LicenseRegistry.addLicense].
|
||||||
|
abstract class LicenseEntry {
|
||||||
|
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||||
|
/// const constructors so that they can be used in const expressions.
|
||||||
|
const LicenseEntry();
|
||||||
|
|
||||||
|
/// Returns each paragraph of the license as a [LicenseParagraph], which
|
||||||
|
/// consists of a string and some formatting information. Paragraphs can
|
||||||
|
/// include newline characters, but this is discouraged as it results in
|
||||||
|
/// ugliness.
|
||||||
|
Iterable<LicenseParagraph> get paragraphs;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _LicenseEntryWithLineBreaksParserState {
|
||||||
|
beforeParagraph, inParagraph
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Variant of [LicenseEntry] for licenses that separate paragraphs with blank
|
||||||
|
/// lines and that hard-wrap text within paragraphs. Lines that begin with one
|
||||||
|
/// or more space characters are also assumed to introduce new paragraphs,
|
||||||
|
/// unless they start with the same number of spaces as the previous line, in
|
||||||
|
/// which case it's assumed they are a continuation of an indented paragraph.
|
||||||
|
///
|
||||||
|
/// For example, the BSD license in this format could be encoded as follows:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// LicenseRegistry.addLicense(() {
|
||||||
|
/// yield new LicenseEntryWithLineBreaks('''
|
||||||
|
/// Copyright 2016 The Sample Authors. All rights reserved.
|
||||||
|
///
|
||||||
|
/// Redistribution and use in source and binary forms, with or without
|
||||||
|
/// modification, are permitted provided that the following conditions are
|
||||||
|
/// met:
|
||||||
|
///
|
||||||
|
/// * Redistributions of source code must retain the above copyright
|
||||||
|
/// notice, this list of conditions and the following disclaimer.
|
||||||
|
/// * Redistributions in binary form must reproduce the above
|
||||||
|
/// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
/// in the documentation and/or other materials provided with the
|
||||||
|
/// distribution.
|
||||||
|
/// * Neither the name of Example Inc. nor the names of its
|
||||||
|
/// contributors may be used to endorse or promote products derived from
|
||||||
|
/// this software without specific prior written permission.
|
||||||
|
///
|
||||||
|
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
/// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
/// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
/// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
/// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
/// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
/// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
/// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
/// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
/// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''');
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This would result in a license with six [paragraphs], the third, fourth, and
|
||||||
|
/// fifth being indented one level.
|
||||||
|
class LicenseEntryWithLineBreaks extends LicenseEntry {
|
||||||
|
/// Create a license entry for a license whose text is hard-wrapped within
|
||||||
|
/// paragraphs and has paragraph breaks denoted by blank lines or with
|
||||||
|
/// indented text.
|
||||||
|
const LicenseEntryWithLineBreaks(this.text);
|
||||||
|
|
||||||
|
/// The text of the license.
|
||||||
|
///
|
||||||
|
/// The text will be split into paragraphs according to the following
|
||||||
|
/// conventions:
|
||||||
|
///
|
||||||
|
/// * Lines starting with a different number of space characters than the
|
||||||
|
/// previous line start a new paragraph, with those spaces removed.
|
||||||
|
/// * Blank lines start a new paragraph.
|
||||||
|
/// * Other line breaks are replaced by a single space character.
|
||||||
|
/// * Leading spaces on a line are removed.
|
||||||
|
///
|
||||||
|
/// For each paragraph, the algorithm attempts (using some rough heuristics)
|
||||||
|
/// to identify how indented the paragraph is, or whether it is centered.
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<LicenseParagraph> get paragraphs sync* {
|
||||||
|
int lineStart = 0;
|
||||||
|
int currentPosition = 0;
|
||||||
|
int lastLineIndent = 0;
|
||||||
|
int currentLineIndent = 0;
|
||||||
|
int currentParagraphIndentation;
|
||||||
|
_LicenseEntryWithLineBreaksParserState state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
|
||||||
|
List<String> lines = <String>[];
|
||||||
|
|
||||||
|
void addLine() {
|
||||||
|
assert(lineStart < currentPosition);
|
||||||
|
lines.add(text.substring(lineStart, currentPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
LicenseParagraph getParagraph() {
|
||||||
|
assert(lines.isNotEmpty);
|
||||||
|
assert(currentParagraphIndentation != null);
|
||||||
|
final LicenseParagraph result = new LicenseParagraph(lines.join(' '), currentParagraphIndentation);
|
||||||
|
assert(result.text.trimLeft() == result.text);
|
||||||
|
assert(result.text.isNotEmpty);
|
||||||
|
lines.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentPosition < text.length) {
|
||||||
|
switch (state) {
|
||||||
|
case _LicenseEntryWithLineBreaksParserState.beforeParagraph:
|
||||||
|
assert(lineStart == currentPosition);
|
||||||
|
switch (text[currentPosition]) {
|
||||||
|
case ' ':
|
||||||
|
lineStart = currentPosition + 1;
|
||||||
|
currentLineIndent += 1;
|
||||||
|
state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
case '\f':
|
||||||
|
if (lines.isNotEmpty)
|
||||||
|
yield getParagraph();
|
||||||
|
lastLineIndent = 0;
|
||||||
|
currentLineIndent = 0;
|
||||||
|
currentParagraphIndentation = null;
|
||||||
|
lineStart = currentPosition + 1;
|
||||||
|
state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
// This is a bit of a hack for the LGPL 2.1, which does something like this:
|
||||||
|
//
|
||||||
|
// [this is a
|
||||||
|
// single paragraph]
|
||||||
|
//
|
||||||
|
// ...near the top.
|
||||||
|
currentLineIndent += 1;
|
||||||
|
continue startParagraph;
|
||||||
|
startParagraph:
|
||||||
|
default:
|
||||||
|
if (lines.isNotEmpty && currentLineIndent > lastLineIndent) {
|
||||||
|
yield getParagraph();
|
||||||
|
currentParagraphIndentation = null;
|
||||||
|
}
|
||||||
|
// The following is a wild heuristic for guessing the indentation level.
|
||||||
|
// It happens to work for common variants of the BSD and LGPL licenses.
|
||||||
|
if (currentParagraphIndentation == null) {
|
||||||
|
if (currentLineIndent > 10)
|
||||||
|
currentParagraphIndentation = LicenseParagraph.centeredIndent;
|
||||||
|
else
|
||||||
|
currentParagraphIndentation = currentLineIndent ~/ 3;
|
||||||
|
}
|
||||||
|
state = _LicenseEntryWithLineBreaksParserState.inParagraph;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _LicenseEntryWithLineBreaksParserState.inParagraph:
|
||||||
|
switch (text[currentPosition]) {
|
||||||
|
case '\n':
|
||||||
|
addLine();
|
||||||
|
lastLineIndent = currentLineIndent;
|
||||||
|
currentLineIndent = 0;
|
||||||
|
lineStart = currentPosition + 1;
|
||||||
|
state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
addLine();
|
||||||
|
yield getParagraph();
|
||||||
|
lastLineIndent = 0;
|
||||||
|
currentLineIndent = 0;
|
||||||
|
currentParagraphIndentation = null;
|
||||||
|
lineStart = currentPosition + 1;
|
||||||
|
state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = _LicenseEntryWithLineBreaksParserState.inParagraph;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentPosition += 1;
|
||||||
|
}
|
||||||
|
switch (state) {
|
||||||
|
case _LicenseEntryWithLineBreaksParserState.beforeParagraph:
|
||||||
|
if (lines.isNotEmpty)
|
||||||
|
yield getParagraph();
|
||||||
|
break;
|
||||||
|
case _LicenseEntryWithLineBreaksParserState.inParagraph:
|
||||||
|
addLine();
|
||||||
|
yield getParagraph();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A registry for packages to add licenses to, so that they can be displayed
|
||||||
|
/// together in an interface such as the [LicensePage].
|
||||||
|
///
|
||||||
|
/// Packages should register their licenses using [addLicense]. User interfaces
|
||||||
|
/// that wish to show all the licenses can obtain them by calling [licenses].
|
||||||
|
class LicenseRegistry {
|
||||||
|
LicenseRegistry._();
|
||||||
|
|
||||||
|
static List<LicenseEntryCollector> _collectors;
|
||||||
|
|
||||||
|
/// Adds licenses to the registry.
|
||||||
|
///
|
||||||
|
/// To avoid actually manipulating the licenses unless strictly necessary,
|
||||||
|
/// licenses are added by adding a closure that returns a list of
|
||||||
|
/// [LicenseEntry] objects. The closure is only called if [licenses] is itself
|
||||||
|
/// called; in normal operation, if the user does not request to see the
|
||||||
|
/// licenses, the closure will not be called.
|
||||||
|
static void addLicense(LicenseEntryCollector collector) {
|
||||||
|
_collectors ??= <LicenseEntryCollector>[];
|
||||||
|
_collectors.add(collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the licenses that have been registered.
|
||||||
|
///
|
||||||
|
/// Each time the iterable returned by this function is called, the callbacks
|
||||||
|
/// registered with [addLicense] are called again, which is relatively
|
||||||
|
/// expensive. For this reason, consider immediately converting the results to
|
||||||
|
/// a list with [Iterable.toList].
|
||||||
|
static Iterable<LicenseEntry> get licenses sync* {
|
||||||
|
if (_collectors == null)
|
||||||
|
return;
|
||||||
|
for (LicenseEntryCollector collector in _collectors)
|
||||||
|
yield* collector();
|
||||||
|
}
|
||||||
|
}
|
@ -301,7 +301,7 @@ class AboutDialog extends StatelessWidget {
|
|||||||
///
|
///
|
||||||
/// To show a [LicensePage], use [showLicensePage].
|
/// To show a [LicensePage], use [showLicensePage].
|
||||||
// TODO(ianh): Mention the API for registering more licenses once it exists.
|
// TODO(ianh): Mention the API for registering more licenses once it exists.
|
||||||
class LicensePage extends StatelessWidget {
|
class LicensePage extends StatefulWidget {
|
||||||
/// Creates a page that shows licenses for software used by the application.
|
/// Creates a page that shows licenses for software used by the application.
|
||||||
///
|
///
|
||||||
/// The arguments are all optional. The application name, if omitted, will be
|
/// The arguments are all optional. The application name, if omitted, will be
|
||||||
@ -337,27 +337,73 @@ class LicensePage extends StatelessWidget {
|
|||||||
/// Defaults to the empty string.
|
/// Defaults to the empty string.
|
||||||
final String applicationLegalese;
|
final String applicationLegalese;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LicensePageState createState() => new _LicensePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LicensePageState extends State<LicensePage> {
|
||||||
|
List<Widget> _licenses = _initLicenses();
|
||||||
|
|
||||||
|
static List<Widget> _initLicenses() {
|
||||||
|
List<Widget> result = <Widget>[];
|
||||||
|
for (LicenseEntry license in LicenseRegistry.licenses) {
|
||||||
|
bool haveMargin = true;
|
||||||
|
result.add(new Padding(
|
||||||
|
padding: new EdgeInsets.symmetric(vertical: 18.0),
|
||||||
|
child: new Text(
|
||||||
|
'🍀', // That's U+1F340. Could also use U+2766 (❦) if U+1F340 doesn't work everywhere.
|
||||||
|
textAlign: TextAlign.center
|
||||||
|
)
|
||||||
|
));
|
||||||
|
for (LicenseParagraph paragraph in license.paragraphs) {
|
||||||
|
if (paragraph.indent == LicenseParagraph.centeredIndent) {
|
||||||
|
result.add(new Padding(
|
||||||
|
padding: new EdgeInsets.only(top: haveMargin ? 0.0 : 16.0),
|
||||||
|
child: new Text(
|
||||||
|
paragraph.text,
|
||||||
|
style: new TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
textAlign: TextAlign.center
|
||||||
|
)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
assert(paragraph.indent >= 0);
|
||||||
|
result.add(new Padding(
|
||||||
|
padding: new EdgeInsets.only(top: haveMargin ? 0.0 : 8.0, left: 16.0 * paragraph.indent),
|
||||||
|
child: new Text(paragraph.text)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
haveMargin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final String name = applicationName ?? _defaultApplicationName(context);
|
final String name = config.applicationName ?? _defaultApplicationName(context);
|
||||||
final String version = applicationVersion ?? _defaultApplicationVersion(context);
|
final String version = config.applicationVersion ?? _defaultApplicationVersion(context);
|
||||||
|
final List<Widget> contents = <Widget>[
|
||||||
|
new Text(name, style: Theme.of(context).textTheme.headline, textAlign: TextAlign.center),
|
||||||
|
new Text(version, style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
||||||
|
new Container(height: 18.0),
|
||||||
|
new Text(config.applicationLegalese ?? '', style: Theme.of(context).textTheme.caption, textAlign: TextAlign.center),
|
||||||
|
new Container(height: 18.0),
|
||||||
|
new Text('Powered by Flutter', style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
||||||
|
new Container(height: 24.0),
|
||||||
|
];
|
||||||
|
contents.addAll(_licenses);
|
||||||
return new Scaffold(
|
return new Scaffold(
|
||||||
appBar: new AppBar(
|
appBar: new AppBar(
|
||||||
title: new Text('Licenses')
|
title: new Text('Licenses')
|
||||||
),
|
),
|
||||||
body: new Block(
|
body: new DefaultTextStyle(
|
||||||
padding: new EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
style: Theme.of(context).textTheme.caption,
|
||||||
children: <Widget>[
|
child: new LazyBlock(
|
||||||
new Text(name, style: Theme.of(context).textTheme.headline, textAlign: TextAlign.center),
|
padding: new EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
|
||||||
new Text(version, style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
delegate: new LazyBlockChildren(
|
||||||
new Container(height: 18.0),
|
children: contents
|
||||||
new Text(applicationLegalese ?? '', style: Theme.of(context).textTheme.caption, textAlign: TextAlign.center),
|
)
|
||||||
new Container(height: 18.0),
|
)
|
||||||
new Text('Powered by Flutter', style: Theme.of(context).textTheme.body1, textAlign: TextAlign.center),
|
|
||||||
new Container(height: 24.0),
|
|
||||||
// TODO(ianh): Fill in the licenses from the API for registering more licenses once it exists.
|
|
||||||
new Text('<licenses will be automatically included here>', style: Theme.of(context).textTheme.caption)
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
197
packages/flutter/test/foundation/licenses_test.dart
Normal file
197
packages/flutter/test/foundation/licenses_test.dart
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright 2016 The Chromium Authors. 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:flutter/foundation.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('LicenseEntryWithLineBreaks - most cases', () {
|
||||||
|
// There's some trailing spaces in this string.
|
||||||
|
// To avoid IDEs stripping them, I've escaped them as \u0020.
|
||||||
|
List<LicenseParagraph> paragraphs = new LicenseEntryWithLineBreaks('''
|
||||||
|
A
|
||||||
|
A
|
||||||
|
A
|
||||||
|
B
|
||||||
|
B
|
||||||
|
B
|
||||||
|
C
|
||||||
|
C
|
||||||
|
C
|
||||||
|
D
|
||||||
|
D
|
||||||
|
D
|
||||||
|
|
||||||
|
E
|
||||||
|
E
|
||||||
|
F
|
||||||
|
G
|
||||||
|
G
|
||||||
|
G
|
||||||
|
|
||||||
|
[H
|
||||||
|
H
|
||||||
|
H]
|
||||||
|
\u0020\u0020
|
||||||
|
IJ
|
||||||
|
K
|
||||||
|
K
|
||||||
|
|
||||||
|
L
|
||||||
|
L L
|
||||||
|
L L
|
||||||
|
L L
|
||||||
|
L L
|
||||||
|
L L
|
||||||
|
|
||||||
|
M
|
||||||
|
M\u0020\u0020\u0020
|
||||||
|
M\u0020\u0020\u0020\u0020
|
||||||
|
|
||||||
|
N
|
||||||
|
|
||||||
|
O
|
||||||
|
O
|
||||||
|
|
||||||
|
|
||||||
|
P
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QQQ
|
||||||
|
|
||||||
|
RR RRR RRRR RRRRR
|
||||||
|
R
|
||||||
|
|
||||||
|
S
|
||||||
|
|
||||||
|
T
|
||||||
|
|
||||||
|
U
|
||||||
|
V
|
||||||
|
|
||||||
|
W
|
||||||
|
|
||||||
|
X
|
||||||
|
\u0020\u0020\u0020\u0020\u0020\u0020
|
||||||
|
Y''').paragraphs.toList();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
expect(paragraphs[index].text, 'A A A');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'B B B');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'C C C');
|
||||||
|
expect(paragraphs[index].indent, 1);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'D D D');
|
||||||
|
expect(paragraphs[index].indent, LicenseParagraph.centeredIndent);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'E E');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'F');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'G G G');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, '[H H H]');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'I');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'J');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'K K');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'L L L L L L L L L L L');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'M M M ');
|
||||||
|
expect(paragraphs[index].indent, 1);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'N');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'O O');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'P');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'QQQ');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'RR RRR RRRR RRRRR R');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'S');
|
||||||
|
expect(paragraphs[index].indent, 0);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'T');
|
||||||
|
expect(paragraphs[index].indent, 1);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'U');
|
||||||
|
expect(paragraphs[index].indent, 2);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'V');
|
||||||
|
expect(paragraphs[index].indent, 3);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'W');
|
||||||
|
expect(paragraphs[index].indent, 2);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'X');
|
||||||
|
expect(paragraphs[index].indent, 2);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs[index].text, 'Y');
|
||||||
|
expect(paragraphs[index].indent, 2);
|
||||||
|
index += 1;
|
||||||
|
expect(paragraphs, hasLength(index));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('LicenseEntryWithLineBreaks - leading and trailing whitespace', () {
|
||||||
|
expect(new LicenseEntryWithLineBreaks(' \n\n ').paragraphs.toList(), isEmpty);
|
||||||
|
|
||||||
|
List<LicenseParagraph> paragraphs;
|
||||||
|
|
||||||
|
paragraphs = new LicenseEntryWithLineBreaks(' \nA\n ').paragraphs.toList();
|
||||||
|
expect(paragraphs[0].text, 'A');
|
||||||
|
expect(paragraphs[0].indent, 0);
|
||||||
|
expect(paragraphs, hasLength(1));
|
||||||
|
|
||||||
|
paragraphs = new LicenseEntryWithLineBreaks('\n\n\nA\n\n\n').paragraphs.toList();
|
||||||
|
expect(paragraphs[0].text, 'A');
|
||||||
|
expect(paragraphs[0].indent, 0);
|
||||||
|
expect(paragraphs, hasLength(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('LicenseRegistry', () {
|
||||||
|
expect(LicenseRegistry.licenses, isEmpty);
|
||||||
|
LicenseRegistry.addLicense(() {
|
||||||
|
return <LicenseEntry>[
|
||||||
|
new LicenseEntryWithLineBreaks('A'),
|
||||||
|
new LicenseEntryWithLineBreaks('B'),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
LicenseRegistry.addLicense(() {
|
||||||
|
return <LicenseEntry>[
|
||||||
|
new LicenseEntryWithLineBreaks('C'),
|
||||||
|
new LicenseEntryWithLineBreaks('D'),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
expect(LicenseRegistry.licenses, hasLength(4));
|
||||||
|
List<LicenseEntry> licenses = LicenseRegistry.licenses.toList();
|
||||||
|
expect(licenses, hasLength(4));
|
||||||
|
expect(licenses[0].paragraphs.single.text, 'A');
|
||||||
|
expect(licenses[1].paragraphs.single.text, 'B');
|
||||||
|
expect(licenses[2].paragraphs.single.text, 'C');
|
||||||
|
expect(licenses[3].paragraphs.single.text, 'D');
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user