blob: 53782b5829b88e5f6412a3749a696f4184b343e3 [file] [log] [blame]
// Copyright 2019 The Flutter 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 'dart:mirrors';
import 'package:cocoon_service/cocoon_service.dart';
import 'package:gcloud/db.dart';
import 'package:test/test.dart';
// Statically reference something from the Cocoon library to keep the analyzer
// happy that we're importing it (which we're doing so the mirrors system sees
// the library).
const Type libraryReference = Config;
bool isKind(InstanceMirror annotation) => annotation.reflectee.runtimeType == Kind;
bool isProperty(InstanceMirror annotation) => SymbolName(annotation.type.simpleName).toString().endsWith('Property');
const Map<Symbol, Symbol> propertyAnnotationsTypeToFieldType = <Symbol, Symbol>{
#BlobProperty: #List,
#BoolProperty: #bool,
#DateTimeProperty: #Date,
#DoubleProperty: #double,
#IntProperty: #int,
#ListProperty: #List,
#ModelKeyProperty: #Key,
#StringListProperty: #List,
#StringProperty: #String,
};
void main() {
final Iterable<LibraryMirror> libraries = currentMirrorSystem()
.libraries
.entries
.where((MapEntry<Uri, LibraryMirror> entry) => entry.key.path.contains('cocoon_service'))
.map((MapEntry<Uri, LibraryMirror> entry) => entry.value);
for (LibraryMirror library in libraries) {
final Iterable<ClassMirror> classes = library.declarations.values
.whereType<ClassMirror>()
.where((ClassMirror declaration) => declaration.hasReflectedType)
.where((ClassMirror declaration) => declaration.metadata.any(isKind));
for (ClassMirror modelClass in classes) {
group('${modelClass.reflectedType}', () {
test('extends Model', () {
final bool isStringModel = modelClass.superclass!.reflectedType.toString() == 'Model<String>';
final bool isIntModel = modelClass.superclass!.reflectedType.toString() == 'Model<int>';
expect(isStringModel || isIntModel, isTrue);
});
final Iterable<VariableMirror> propertyVariables = modelClass.declarations.values
.whereType<VariableMirror>()
.where((DeclarationMirror declaration) => declaration.metadata.any(isProperty));
for (VariableMirror variable in propertyVariables) {
final Iterable<InstanceMirror> propertyAnnotations = variable.metadata.where(isProperty);
group(SymbolName(variable.simpleName), () {
test('contains only one property annotation', () {
expect(propertyAnnotations, hasLength(1));
});
test('type matches property annotation type', () {
final Symbol propertyType = variable.type.simpleName;
expect(propertyAnnotationsTypeToFieldType[propertyAnnotations.single.type.simpleName], propertyType);
});
test('is not static', () {
expect(variable.isStatic, isFalse);
});
test('is not final', () {
expect(variable.isFinal, isFalse);
});
});
}
final Iterable<MethodMirror> propertyGetters = modelClass.declarations.values
.whereType<MethodMirror>()
.where((MethodMirror method) => method.isGetter)
.where((DeclarationMirror declaration) => declaration.metadata.any(isProperty));
for (MethodMirror getter in propertyGetters) {
final Iterable<InstanceMirror> propertyAnnotations = getter.metadata.where(isProperty);
group(SymbolName(getter.simpleName), () {
test('contains only one property annotation', () {
expect(propertyAnnotations, hasLength(1));
});
test('type matches property annotation type', () {
final Symbol propertyType = getter.returnType.simpleName;
expect(propertyAnnotationsTypeToFieldType[propertyAnnotations.single.type.simpleName], propertyType);
});
test('is not static', () {
expect(getter.isStatic, isFalse);
});
test('has corresponding setter', () {
final Iterable<MethodMirror> setter = modelClass.declarations.values
.whereType<MethodMirror>()
.where((MethodMirror method) => method.isSetter)
.where((MethodMirror setter) => setter.simpleName == SymbolName(getter.simpleName).asSetter);
expect(setter, hasLength(1));
});
});
}
});
}
}
test('SymbolName toString', () {
expect(const SymbolName(#Foo).toString(), 'Foo');
});
}
class SymbolName {
const SymbolName(this.symbol);
final Symbol symbol;
static final RegExp symbolToString = RegExp(r'^Symbol\("(.*)"\)$');
Symbol get asSetter => Symbol('$this=');
@override
String toString() {
final String raw = symbol.toString();
final RegExpMatch? match = symbolToString.firstMatch(raw);
return match == null ? raw : match.group(1)!;
}
}