blob: f1e4f57760b88a455e7d482ba6319d023750beeb [file] [log] [blame]
// 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.
import 'dart:io';
import 'package:pub_semver/pub_semver.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:test/test.dart';
import 'test_utils.dart';
void main() {
group('hosted', _hostedDependency);
group('git', _gitDependency);
group('sdk', _sdkDependency);
group('path', _pathDependency);
group('errors', () {
test('List', () {
_expectThrows(
[],
r'''
line 4, column 10: Unsupported value for "dep". Not a valid dependency value.
4 │ "dep": []
│ ^^
╵''',
);
});
test('int', () {
_expectThrows(
42,
r'''
line 4, column 10: Unsupported value for "dep". Not a valid dependency value.
4 │ "dep": 42
│ ┌──────────^
5 │ │ }
│ └─^
╵''',
);
});
test('map with too many keys', () {
_expectThrows(
{'path': 'a', 'git': 'b'},
r'''
line 6, column 11: Unsupported value for "git". A dependency may only have one source.
6 │ "git": "b"
│ ^^^
╵''',
);
});
test('map with unsupported keys', () {
_expectThrows(
{'bob': 'a', 'jones': 'b'},
r'''
line 5, column 4: Unrecognized keys: [bob]; supported keys: [sdk, git, path, hosted]
5 │ "bob": "a",
│ ^^^^^
╵''',
);
});
});
}
void _hostedDependency() {
test('null', () async {
final dep = await _dependency<HostedDependency>(null);
expect(dep.version.toString(), 'any');
expect(dep.hosted, isNull);
expect(dep.toString(), 'HostedDependency: any');
});
test('empty map', () async {
final dep = await _dependency<HostedDependency>({});
expect(dep.hosted, isNull);
expect(dep.toString(), 'HostedDependency: any');
});
test('string version', () async {
final dep = await _dependency<HostedDependency>('^1.0.0');
expect(dep.version.toString(), '^1.0.0');
expect(dep.hosted, isNull);
expect(dep.toString(), 'HostedDependency: ^1.0.0');
});
test('bad string version', () {
_expectThrows(
'not a version',
r'''
line 4, column 10: Unsupported value for "dep". Could not parse version "not a version". Unknown text at "not a version".
4 │ "dep": "not a version"
│ ^^^^^^^^^^^^^^^
╵''',
);
});
test('map w/ just version', () async {
final dep = await _dependency<HostedDependency>({'version': '^1.0.0'});
expect(dep.version.toString(), '^1.0.0');
expect(dep.hosted, isNull);
expect(dep.toString(), 'HostedDependency: ^1.0.0');
});
test('map w/ version and hosted as Map', () async {
final dep = await _dependency<HostedDependency>({
'version': '^1.0.0',
'hosted': {'name': 'hosted_name', 'url': 'https://hosted_url'},
});
expect(dep.version.toString(), '^1.0.0');
expect(dep.hosted!.name, 'hosted_name');
expect(dep.hosted!.url.toString(), 'https://hosted_url');
expect(dep.toString(), 'HostedDependency: ^1.0.0');
});
test('map /w hosted as a map without name', () async {
final dep = await _dependency<HostedDependency>(
{
'version': '^1.0.0',
'hosted': {'url': 'https://hosted_url'},
},
skipTryPub: true, // todo: Unskip once pub supports this syntax
);
expect(dep.version.toString(), '^1.0.0');
expect(dep.hosted!.declaredName, isNull);
expect(dep.hosted!.name, 'dep');
expect(dep.hosted!.url.toString(), 'https://hosted_url');
expect(dep.toString(), 'HostedDependency: ^1.0.0');
});
test('map w/ bad version value', () {
_expectThrows(
{
'version': 'not a version',
'hosted': {'name': 'hosted_name', 'url': 'hosted_url'},
},
r'''
line 5, column 15: Unsupported value for "version". Could not parse version "not a version". Unknown text at "not a version".
5 │ "version": "not a version",
│ ^^^^^^^^^^^^^^^
╵''',
);
});
test('map w/ extra keys should fail', () {
_expectThrows(
{
'version': '^1.0.0',
'hosted': {'name': 'hosted_name', 'url': 'hosted_url'},
'not_supported': null,
},
r'''
line 10, column 4: Unrecognized keys: [not_supported]; supported keys: [sdk, git, path, hosted]
10 │ "not_supported": null
│ ^^^^^^^^^^^^^^^
╵''',
);
});
test('map w/ version and hosted as String', () async {
final dep = await _dependency<HostedDependency>(
{'version': '^1.0.0', 'hosted': 'hosted_url'},
skipTryPub: true, // todo: Unskip once put supports this
);
expect(dep.version.toString(), '^1.0.0');
expect(dep.hosted!.declaredName, isNull);
expect(dep.hosted!.name, 'dep');
expect(dep.hosted!.url, Uri.parse('hosted_url'));
expect(dep.toString(), 'HostedDependency: ^1.0.0');
});
test('map w/ hosted as String', () async {
final dep = await _dependency<HostedDependency>({'hosted': 'hosted_url'});
expect(dep.version, VersionConstraint.any);
expect(dep.hosted!.declaredName, isNull);
expect(dep.hosted!.name, 'dep');
expect(dep.hosted!.url, Uri.parse('hosted_url'));
expect(dep.toString(), 'HostedDependency: any');
});
test('map w/ null hosted should error', () {
_expectThrows(
{'hosted': null},
r'''
line 5, column 4: These keys had `null` values, which is not allowed: [hosted]
5 │ "hosted": null
│ ^^^^^^^^
╵''',
);
});
test('map w/ null version is fine', () async {
final dep = await _dependency<HostedDependency>({'version': null});
expect(dep.version, VersionConstraint.any);
expect(dep.hosted, isNull);
expect(dep.toString(), 'HostedDependency: any');
});
}
void _sdkDependency() {
test('without version', () async {
final dep = await _dependency<SdkDependency>({'sdk': 'flutter'});
expect(dep.sdk, 'flutter');
expect(dep.version, VersionConstraint.any);
expect(dep.toString(), 'SdkDependency: flutter');
});
test('with version', () async {
final dep = await _dependency<SdkDependency>(
{'sdk': 'flutter', 'version': '>=1.2.3 <2.0.0'},
);
expect(dep.sdk, 'flutter');
expect(dep.version.toString(), '>=1.2.3 <2.0.0');
expect(dep.toString(), 'SdkDependency: flutter');
});
test('null content', () {
_expectThrowsContaining(
{'sdk': null},
r"type 'Null' is not a subtype of type 'String'",
);
});
test('number content', () {
_expectThrowsContaining(
{'sdk': 42},
r"type 'int' is not a subtype of type 'String'",
);
});
}
void _gitDependency() {
test('string', () async {
final dep = await _dependency<GitDependency>({'git': 'url'});
expect(dep.url.toString(), 'url');
expect(dep.path, isNull);
expect(dep.ref, isNull);
expect(dep.toString(), 'GitDependency: url@url');
});
test('string with version key is ignored', () async {
// Regression test for https://github.com/dart-lang/pubspec_parse/issues/13
final dep =
await _dependency<GitDependency>({'git': 'url', 'version': '^1.2.3'});
expect(dep.url.toString(), 'url');
expect(dep.path, isNull);
expect(dep.ref, isNull);
expect(dep.toString(), 'GitDependency: url@url');
});
test('string with user@ URL', () async {
final skipTryParse = Platform.environment.containsKey('TRAVIS');
if (skipTryParse) {
print('FYI: not validating git@ URI on travis due to failure');
}
final dep = await _dependency<GitDependency>(
{'git': 'git@localhost:dep.git'},
skipTryPub: skipTryParse,
);
expect(dep.url.toString(), 'ssh://git@localhost/dep.git');
expect(dep.path, isNull);
expect(dep.ref, isNull);
expect(dep.toString(), 'GitDependency: url@ssh://git@localhost/dep.git');
});
test('string with random extra key fails', () {
_expectThrows(
{'git': 'url', 'bob': '^1.2.3'},
r'''
line 6, column 4: Unrecognized keys: [bob]; supported keys: [sdk, git, path, hosted]
6 │ "bob": "^1.2.3"
│ ^^^^^
╵''',
);
});
test('map', () async {
final dep = await _dependency<GitDependency>({
'git': {'url': 'url', 'path': 'path', 'ref': 'ref'},
});
expect(dep.url.toString(), 'url');
expect(dep.path, 'path');
expect(dep.ref, 'ref');
expect(dep.toString(), 'GitDependency: url@url');
});
test('git - null content', () {
_expectThrows(
{'git': null},
r'''
line 5, column 11: Unsupported value for "git". Must be a String or a Map.
5 │ "git": null
│ ┌───────────^
6 │ │ }
│ └──^
╵''',
);
});
test('git - int content', () {
_expectThrows(
{'git': 42},
r'''
line 5, column 11: Unsupported value for "git". Must be a String or a Map.
5 │ "git": 42
│ ┌───────────^
6 │ │ }
│ └──^
╵''',
);
});
test('git - empty map', () {
_expectThrowsContaining(
{'git': <String, dynamic>{}},
r"type 'Null' is not a subtype of type 'String'",
);
});
test('git - null url', () {
_expectThrowsContaining(
{
'git': {'url': null},
},
r"type 'Null' is not a subtype of type 'String'",
);
});
test('git - int url', () {
_expectThrowsContaining(
{
'git': {'url': 42},
},
r"type 'int' is not a subtype of type 'String'",
);
});
}
void _pathDependency() {
test('valid', () async {
final dep = await _dependency<PathDependency>({'path': '../path'});
expect(dep.path, '../path');
expect(dep.toString(), 'PathDependency: path@../path');
});
test('valid with version key is ignored', () async {
final dep = await _dependency<PathDependency>(
{'path': '../path', 'version': '^1.2.3'},
);
expect(dep.path, '../path');
expect(dep.toString(), 'PathDependency: path@../path');
});
test('valid with random extra key fails', () {
_expectThrows(
{'path': '../path', 'bob': '^1.2.3'},
r'''
line 6, column 4: Unrecognized keys: [bob]; supported keys: [sdk, git, path, hosted]
6 │ "bob": "^1.2.3"
│ ^^^^^
╵''',
);
});
test('null content', () {
_expectThrows(
{'path': null},
r'''
line 5, column 12: Unsupported value for "path". Must be a String.
5 │ "path": null
│ ┌────────────^
6 │ │ }
│ └──^
╵''',
);
});
test('int content', () {
_expectThrows(
{'path': 42},
r'''
line 5, column 12: Unsupported value for "path". Must be a String.
5 │ "path": 42
│ ┌────────────^
6 │ │ }
│ └──^
╵''',
);
});
}
void _expectThrows(Object content, String expectedError) {
expectParseThrows(
{
'name': 'sample',
'dependencies': {'dep': content},
},
expectedError,
);
}
void _expectThrowsContaining(Object content, String errorText) {
expectParseThrowsContaining(
{
'name': 'sample',
'dependencies': {'dep': content},
},
errorText,
);
}
Future<T> _dependency<T extends Dependency>(
Object? content, {
bool skipTryPub = false,
}) async {
final value = await parse(
{
...defaultPubspec,
'dependencies': {'dep': content},
},
skipTryPub: skipTryPub,
);
expect(value.name, 'sample');
expect(value.dependencies, hasLength(1));
final entry = value.dependencies.entries.single;
expect(entry.key, 'dep');
return entry.value as T;
}