blob: cea14719e1fb5c1c28d6023948f36466eac3b14e [file] [log] [blame]
// Copyright (c) 2015, 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 'package:discoveryapis_generator/src/dart_api_library.dart';
import 'package:discoveryapis_generator/src/dart_schemas.dart';
import 'package:discoveryapis_generator/src'
'/generated_googleapis/discovery/v1.dart';
import 'package:discoveryapis_generator/src/namer.dart';
import 'package:test/test.dart';
void withImports(void Function(DartApiImports, ApiLibraryNamer) function) {
final namer = ApiLibraryNamer();
final imports = DartApiImports.fromNamer(namer);
return function(imports, namer);
}
void withParsedDB(
Map<String, dynamic> json,
void Function(DartSchemaTypeDB) function,
) =>
withImports((imports, namer) {
final description = RestDescription.fromJson(json);
final db = parseSchemas(imports, description);
namer.nameAllIdentifiers();
return function(db);
});
void main() {
group('dart-schemas', () {
test('empty', () {
withParsedDB({}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(0));
expect(db.namedSchemaTypes, hasLength(0));
expect(db.dartClassTypes, hasLength(0));
});
withParsedDB({'empty-schemas': {}}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(0));
expect(db.namedSchemaTypes, hasLength(0));
expect(db.dartClassTypes, hasLength(0));
});
});
test('invalid', () {
expect(
() => withParsedDB({
'schemas': {
'Task': {
'type': 'object',
'repeated': true,
},
}
}, (_) {}),
throwsArgumentError);
});
test('object-schema', () {
withParsedDB({
'schemas': {
'Task': {
'type': 'object',
'properties': {
'age': {'type': 'integer'},
'any': {'type': 'any'},
'icon': {'type': 'string', 'format': 'byte'},
'isMale': {'type': 'boolean'},
'labels': {
'type': 'array',
'items': {
'type': 'integer',
},
},
'name': {'type': 'string'},
'properties': {
'type': 'object',
'additionalProperties': {
'type': 'string',
},
},
'x1Date': {'type': 'string', 'format': 'date'},
'x2DateTime': {'type': 'string', 'format': 'date-time'},
'x3Int16': {'type': 'integer', 'format': 'int16'},
},
},
}
}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(3));
expect(db.namedSchemaTypes, hasLength(1));
expect(db.dartClassTypes, hasLength(1));
expect(db.namedSchemaTypes, contains('Task'));
expect(db.dartClassTypes, hasLength(1));
final ObjectType task = db.dartClassTypes.first;
expect(db.dartTypes, contains(task));
expect(db.namedSchemaTypes['Task'], equals(task));
// Do tests on `task`.
expect(task is ObjectType, isTrue);
expect(task.className.name, equals('Task'));
expect(task.superVariantType, isNull);
// Do tests on `task.properties`.
final age = task.properties[0];
expect(age, isNotNull);
expect(age.name.name, equals('age'));
expect(age.type, equals(db.integerType));
final any = task.properties[1];
expect(any, isNotNull);
expect(any.name.name, equals('any'));
expect(any.type, equals(db.anyType));
final icon = task.properties[2];
expect(icon, isNotNull);
expect(icon.name.name, equals('icon'));
expect(icon.type, equals(db.stringType));
expect(icon.byteArrayAccessor.name, equals('iconAsBytes'));
final isMale = task.properties[3];
expect(isMale, isNotNull);
expect(isMale.name.name, equals('isMale'));
expect(isMale.type, equals(db.booleanType));
final labels = task.properties[4];
expect(labels, isNotNull);
expect(labels.name.name, equals('labels'));
expect(labels.type is UnnamedArrayType, isTrue);
final UnnamedArrayType labelsTyped = labels.type;
expect(labelsTyped.className, isNull);
expect(labelsTyped.innerType, equals(db.integerType));
final name = task.properties[5];
expect(name, isNotNull);
expect(name.name.name, equals('name'));
expect(name.type, equals(db.stringType));
final properties = task.properties[6];
expect(properties, isNotNull);
expect(properties.name.name, equals('properties'));
expect(properties.type is UnnamedMapType, isTrue);
final UnnamedMapType propertiesTyped = properties.type;
expect(propertiesTyped.className, isNull);
expect(propertiesTyped.fromType, equals(db.stringType));
expect(propertiesTyped.toType, equals(db.stringType));
final date = task.properties[7];
expect(date, isNotNull);
expect(date.name.name, equals('x1Date'));
expect(date.type is DateType, isTrue);
final dateTime = task.properties[8];
expect(dateTime, isNotNull);
expect(dateTime.name.name, equals('x2DateTime'));
expect(dateTime.type is DateTimeType, isTrue);
final int16 = task.properties[9];
expect(int16, isNotNull);
expect(int16.name.name, equals('x3Int16'));
expect(int16.type is IntegerType, isTrue);
});
});
test('variant-schema-with-forward-references', () {
withParsedDB({
'schemas': {
'Geometry': {
'type': 'object',
'variant': {
'discriminant': 'my_type',
'map': [
{
'type_value': 'my_line_type',
'\$ref': 'LineGeometry',
},
{
'type_value': 'my_polygon_type',
'\$ref': 'PolygonGeometry',
},
],
},
},
'LineGeometry': {
'type': 'object',
'properties': {
'label': {'type': 'string'},
},
},
'PolygonGeometry': {
'type': 'object',
'properties': {
'points': {'type': 'integer'},
},
},
'IndirectPolygonGeometry': {
'\$ref': 'PolygonGeometry',
}
}
}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(3));
expect(db.namedSchemaTypes, hasLength(4));
expect(db.dartClassTypes, hasLength(3));
// 'Geometry' variant schema.
expect(db.namedSchemaTypes, contains('Geometry'));
final AbstractVariantType geo = db.dartClassTypes[0];
expect(db.dartTypes, contains(geo));
expect(db.namedSchemaTypes['Geometry'], equals(geo));
// 'LineGeometry' schema
expect(db.namedSchemaTypes, contains('LineGeometry'));
final ObjectType lineGeo = db.dartClassTypes[1];
expect(db.dartTypes, contains(lineGeo));
expect(db.namedSchemaTypes['LineGeometry'], equals(lineGeo));
// 'PolygonGeometry' schema
expect(db.namedSchemaTypes, contains('PolygonGeometry'));
final ObjectType polyGeo = db.dartClassTypes[2];
expect(db.dartTypes, contains(polyGeo));
expect(db.namedSchemaTypes['PolygonGeometry'], equals(polyGeo));
// 'IndirectPolygonGeometry'
expect(db.namedSchemaTypes, contains('IndirectPolygonGeometry'));
final DartSchemaForwardRef indirectPolyGeo =
db.namedSchemaTypes['IndirectPolygonGeometry'];
expect(indirectPolyGeo.forwardRefName, equals('PolygonGeometry'));
// Check variant map
expect(geo.className.name, equals('Geometry'));
expect(geo.discriminant, equals('my_type'));
expect(geo.map, contains('my_line_type'));
expect(geo.map['my_line_type'], equals(lineGeo));
expect(geo.map, contains('my_polygon_type'));
expect(geo.map['my_polygon_type'], equals(polyGeo));
expect(lineGeo.className.name, equals('LineGeometry'));
expect(lineGeo.properties.first.name.name, equals('label'));
expect(lineGeo.properties.first.type, equals(db.stringType));
expect(polyGeo.className.name, equals('PolygonGeometry'));
expect(polyGeo.properties.first.name.name, equals('points'));
expect(polyGeo.properties.first.type, equals(db.integerType));
});
});
test('object-schema-name-overlap', () {
withParsedDB({
'schemas': {
// Task, TaskName, TaskName_1
'Overlap': {
'type': 'object',
'properties': {
'array': {
'type': 'array',
'items': {
'type': 'integer',
},
},
'object': {
'type': 'object',
'properties': {
'prop': {'type': 'integer'},
},
},
'integer': {
'type': 'integer',
},
},
},
'OverlapArray': {
'type': 'object',
'properties': {
'oaprop': {'type': 'integer'},
},
},
'OverlapObject': {
'type': 'object',
'properties': {
'ooprop': {'type': 'integer'},
},
},
// INFO: Should generate the following classes:
// Overlap, OverlapArray, OverlapObject, OverlapObject_1
//
// NOTE: Since we don't generate a class for
// - [Overlap.array]
// - [Overlap.integer]
// we can assert that
// - the name 'OverlapArray' will not be allocated twice
// - the name 'OverlapInteger' will not be allocated
}
}, (DartSchemaTypeDB db) {
expect(db.dartClassTypes, hasLength(4));
expect(db.namedSchemaTypes, hasLength(3));
expect(db.namedSchemaTypes, contains('Overlap'));
expect(db.namedSchemaTypes, contains('OverlapArray'));
expect(db.namedSchemaTypes, contains('OverlapObject'));
// The order in [db.dartClassTypes] is:
// depth-first traversal with postorder node processing.
//
// Naming happens on the scope tree level-by-level, so it depends on
// the order of insertion into the scope.
expect(db.dartClassTypes, hasLength(4));
expect(db.dartClassTypes[0].className.name, equals('OverlapObject'));
expect(db.dartClassTypes[1].className.name, equals('Overlap'));
expect(db.dartClassTypes[2].className.name, equals('OverlapArray'));
expect(db.dartClassTypes[3].className.name, equals('OverlapObject_1'));
expect(db.dartClassTypes[1], equals(db.namedSchemaTypes['Overlap']));
expect(
db.dartClassTypes[2], equals(db.namedSchemaTypes['OverlapArray']));
expect(
db.dartClassTypes[3], equals(db.namedSchemaTypes['OverlapObject']));
});
});
test('named-map-schema', () {
withParsedDB({
'schemas': {
'Properties': {
'type': 'object',
'additionalProperties': {
'type': 'integer',
},
},
}
}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(1));
expect(db.namedSchemaTypes, hasLength(1));
expect(db.dartClassTypes, hasLength(1));
expect(db.namedSchemaTypes, contains('Properties'));
final NamedMapType properties = db.dartClassTypes.first;
expect(properties.className.name, equals('Properties'));
expect(properties.fromType, equals(db.stringType));
expect(properties.toType, equals(db.integerType));
});
});
test('named-array-schema', () {
withParsedDB({
'schemas': {
'NamedArray': {
'type': 'array',
'items': {
'type': 'string',
},
},
}
}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(1));
expect(db.namedSchemaTypes, hasLength(1));
expect(db.dartClassTypes, hasLength(1));
expect(db.namedSchemaTypes, contains('NamedArray'));
final NamedArrayType properties = db.dartClassTypes.first;
expect(properties.className.name, equals('NamedArray'));
expect(properties.innerType, equals(db.stringType));
});
});
test('no-enum-comments', () {
withParsedDB({
'schemas': {
'MyClass': {
'type': 'string',
'enum': ['foo', 'bar'],
},
}
}, (DartSchemaTypeDB db) {
expect(db.dartTypes, hasLength(1));
expect(db.namedSchemaTypes, hasLength(1));
expect(db.dartClassTypes, hasLength(0));
expect(db.namedSchemaTypes, contains('MyClass'));
final EnumType enumType = db.dartTypes.first;
expect(enumType.enumValues, equals(['foo', 'bar']));
expect(enumType.enumDescriptions, equals(['A foo.', 'A bar.']));
});
});
test('needs-json-encoding-decoding', () {
withImports((imports, namer) {
namer.nameAllIdentifiers();
withParsedDB({
'schemas': {
'NamedArraySimple': {
'type': 'array',
'items': {
'type': 'string',
},
},
'NamedMapSimple': {
'type': 'object',
'additionalProperties': {
'type': 'string',
},
},
'NamedArrayComplex': {
'type': 'array',
'items': {
'type': 'object',
},
},
'NamedMapComplex': {
'type': 'object',
'additionalProperties': {
'type': 'object',
},
},
'NamedObject': {
'type': 'object',
},
'C': {
'type': 'object',
'properties': {
'pArraySimple': {
'type': 'array',
'items': {
'type': 'string',
},
},
'pMapSimple': {
'type': 'object',
'additionalProperties': {
'type': 'string',
},
},
'pArrayComplex': {
'type': 'array',
'items': {
'type': 'object',
},
},
'pMapComplex': {
'type': 'object',
'additionalProperties': {
'type': 'object',
},
},
'pObject': {
'type': 'object',
},
}
}
},
}, (DartSchemaTypeDB db) {
// Primitive Types
expect(db.stringType.needsJsonEncoding, false);
expect(db.stringType.needsJsonDecoding, false);
expect(db.integerType.needsJsonEncoding, false);
expect(db.integerType.needsJsonDecoding, false);
expect(db.doubleType.needsJsonEncoding, false);
expect(db.doubleType.needsJsonDecoding, true);
expect(db.booleanType.needsJsonEncoding, false);
expect(db.booleanType.needsJsonDecoding, false);
expect(db.dateType.needsJsonEncoding, true);
expect(db.dateType.needsJsonDecoding, true);
expect(db.dateTimeType.needsJsonEncoding, true);
expect(db.dateTimeType.needsJsonDecoding, true);
expect(db.anyType.needsJsonEncoding, false);
expect(db.anyType.needsJsonDecoding, false);
// Named complex types
final namedArraySimple = db.namedSchemaTypes['NamedArraySimple'];
final namedArrayComplex = db.namedSchemaTypes['NamedArrayComplex'];
final namedMapSimple = db.namedSchemaTypes['NamedMapSimple'];
final namedMapComplex = db.namedSchemaTypes['NamedMapComplex'];
final namedObject = db.namedSchemaTypes['NamedObject'];
// Array simple/complex
expect(namedArraySimple.needsJsonEncoding, false);
expect(namedArraySimple.needsJsonDecoding, true);
expect(namedArrayComplex.needsJsonEncoding, true);
expect(namedArrayComplex.needsJsonDecoding, true);
// Map simple/complex
expect(namedMapSimple.needsJsonEncoding, false);
expect(namedMapSimple.needsJsonDecoding, true);
expect(namedMapComplex.needsJsonEncoding, true);
expect(namedMapComplex.needsJsonDecoding, true);
// Objects
expect(namedObject.needsJsonEncoding, true);
expect(namedObject.needsJsonDecoding, true);
// Unnamed complex types
final C = db.namedSchemaTypes['C'] as ObjectType;
DartSchemaType findPropertyType(String name) {
for (var property in C.properties) {
if (property.name.preferredName == name) return property.type;
}
throw StateError('not found');
}
// Unnamed complex types
final unNamedArraySimple = findPropertyType('pArraySimple');
final unNamedArrayComplex = findPropertyType('pArrayComplex');
final unNamedMapSimple = findPropertyType('pMapSimple');
final unNamedMapComplex = findPropertyType('pMapComplex');
final unNamedObject = findPropertyType('pObject');
// Array simple/complex
expect(unNamedArraySimple.needsJsonEncoding, false);
expect(unNamedArraySimple.needsJsonDecoding, true);
expect(unNamedArrayComplex.needsJsonEncoding, true);
expect(unNamedArrayComplex.needsJsonDecoding, true);
// Map simple/complex
expect(unNamedMapSimple.needsJsonEncoding, false);
expect(unNamedMapSimple.needsJsonDecoding, true);
expect(unNamedMapComplex.needsJsonEncoding, true);
expect(unNamedMapComplex.needsJsonDecoding, true);
// Objects
expect(unNamedObject.needsJsonEncoding, true);
expect(unNamedObject.needsJsonDecoding, true);
});
});
});
});
}