[pigeon] added more errors for improper usage (#393)

This commit is contained in:
gaaclarke
2021-06-29 12:27:59 -07:00
committed by GitHub
parent 59160ba428
commit 4996352511
7 changed files with 910 additions and 287 deletions

View File

@ -2,6 +2,7 @@
* [front-end] Added a more explicit error if generic fields are used. * [front-end] Added a more explicit error if generic fields are used.
* [front-end] Added a more explicit error for static fields. * [front-end] Added a more explicit error for static fields.
* [front-end] Added more errors for incorrect usage of Pigeon (previously they were just ignored).
## 0.3.0 ## 0.3.0

View File

@ -21,7 +21,10 @@ class Method extends Node {
required this.name, required this.name,
required this.returnType, required this.returnType,
required this.argType, required this.argType,
this.isArgNullable = false,
this.isAsynchronous = false, this.isAsynchronous = false,
this.isReturnNullable = false,
this.offset,
}); });
/// The name of the method. /// The name of the method.
@ -30,12 +33,21 @@ class Method extends Node {
/// The data-type of the return value. /// The data-type of the return value.
String returnType; String returnType;
/// True if the method can return a null value.
bool isReturnNullable;
/// The data-type of the argument. /// The data-type of the argument.
String argType; String argType;
/// True if the argument has a null tag `?`.
bool isArgNullable;
/// Whether the receiver of this method is expected to return synchronously or not. /// Whether the receiver of this method is expected to return synchronously or not.
bool isAsynchronous; bool isAsynchronous;
/// The offset in the source file where the field appears.
int? offset;
@override @override
String toString() { String toString() {
return '(Api name:$name returnType:$returnType argType:$argType isAsynchronous:$isAsynchronous)'; return '(Api name:$name returnType:$returnType argType:$argType isAsynchronous:$isAsynchronous)';
@ -76,6 +88,8 @@ class Field extends Node {
Field({ Field({
required this.name, required this.name,
required this.dataType, required this.dataType,
required this.isNullable,
this.typeArguments,
this.offset, this.offset,
}); });
@ -88,6 +102,12 @@ class Field extends Node {
/// The offset in the source file where the field appears. /// The offset in the source file where the field appears.
int? offset; int? offset;
/// True if the datatype is nullable (ex `int?`).
bool isNullable;
/// Type parameters used for generics.
List<Field>? typeArguments;
@override @override
String toString() { String toString() {
return '(Field name:$name dataType:$dataType)'; return '(Field name:$name dataType:$dataType)';

View File

@ -391,7 +391,7 @@ List<Error> _validateAst(Root root, String source) {
final List<String> customEnums = root.enums.map((Enum x) => x.name).toList(); final List<String> customEnums = root.enums.map((Enum x) => x.name).toList();
for (final Class klass in root.classes) { for (final Class klass in root.classes) {
for (final Field field in klass.fields) { for (final Field field in klass.fields) {
if (field.dataType.contains('<')) { if (field.typeArguments != null) {
result.add(Error( result.add(Error(
message: message:
'Unsupported datatype:"${field.dataType}" in class "${klass.name}". Generic fields aren\'t yet supported (https://github.com/flutter/flutter/issues/63468).', 'Unsupported datatype:"${field.dataType}" in class "${klass.name}". Generic fields aren\'t yet supported (https://github.com/flutter/flutter/issues/63468).',
@ -410,15 +410,33 @@ List<Error> _validateAst(Root root, String source) {
} }
for (final Api api in root.apis) { for (final Api api in root.apis) {
for (final Method method in api.methods) { for (final Method method in api.methods) {
if (method.isReturnNullable) {
result.add(Error(
message:
'Nullable return types types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
}
if (method.isArgNullable) {
result.add(Error(
message:
'Nullable argument types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
}
if (_validTypes.contains(method.argType)) { if (_validTypes.contains(method.argType)) {
result.add(Error( result.add(Error(
message: message:
'Unsupported argument type: "${method.argType}" in API: "${api.name}" method: "${method.name}')); 'Primitive argument types aren\'t yet supported (https://github.com/flutter/flutter/issues/66467): "${method.argType}" in API: "${api.name}" method: "${method.name}',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
} }
if (_validTypes.contains(method.returnType)) { if (_validTypes.contains(method.returnType)) {
result.add(Error( result.add(Error(
message: message:
'Unsupported return type: "${method.returnType}" in API: "${api.name}" method: "${method.name}')); 'Primitive return types aren\'t yet supported (https://github.com/flutter/flutter/issues/66467): "${method.returnType}" in API: "${api.name}" method: "${method.name}',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
} }
} }
} }
@ -426,6 +444,17 @@ List<Error> _validateAst(Root root, String source) {
return result; return result;
} }
class _FindInitializer extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
dart_ast.Expression? initializer;
@override
Object? visitVariableDeclaration(dart_ast.VariableDeclaration node) {
if (node.initializer != null) {
initializer = node.initializer;
}
return null;
}
}
class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> { class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
_RootBuilder(this.source, this.ignoresInvalidImports); _RootBuilder(this.source, this.ignoresInvalidImports);
@ -472,30 +501,11 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
} }
} }
final List<Class> classesWithNullTagStripped = _classes.map((Class aClass) {
return Class(
name: aClass.name,
fields: aClass.fields.map((Field field) {
String datatype = field.dataType;
if (datatype.endsWith('?')) {
datatype = datatype.substring(0, datatype.length - 1);
} else {
// TODO(aaclarke): Provide an error when not using a nullable type.
// _errors.add(Error(
// message:
// 'Field ${aClass.name}.${field.name} must be nullable.'));
}
return Field(
name: field.name, dataType: datatype, offset: field.offset);
}).toList());
}).toList();
final List<String> classesToCheck = List<String>.from(referencedTypes); final List<String> classesToCheck = List<String>.from(referencedTypes);
while (classesToCheck.isNotEmpty) { while (classesToCheck.isNotEmpty) {
final String next = classesToCheck.last; final String next = classesToCheck.last;
classesToCheck.removeLast(); classesToCheck.removeLast();
final Class aClass = classesWithNullTagStripped.firstWhere( final Class aClass = _classes.firstWhere((Class x) => x.name == next,
(Class x) => x.name == next,
orElse: () => Class(name: '', fields: <Field>[])); orElse: () => Class(name: '', fields: <Field>[]));
for (final Field field in aClass.fields) { for (final Field field in aClass.fields) {
if (!referencedTypes.contains(field.dataType) && if (!referencedTypes.contains(field.dataType) &&
@ -510,8 +520,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
? (Class x) => !referencedTypes.contains(x.name) ? (Class x) => !referencedTypes.contains(x.name)
: (Class x) => : (Class x) =>
!referencedTypes.contains(x.name) && !typeFilter.contains(x.name); !referencedTypes.contains(x.name) && !typeFilter.contains(x.name);
final List<Class> referencedClasses = final List<Class> referencedClasses = List<Class>.from(_classes);
List<Class>.from(classesWithNullTagStripped);
referencedClasses.removeWhere(classRemover); referencedClasses.removeWhere(classRemover);
final List<Enum> referencedEnums = List<Enum>.from(_enums); final List<Enum> referencedEnums = List<Enum>.from(_enums);
@ -646,6 +655,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
Object? visitMethodDeclaration(dart_ast.MethodDeclaration node) { Object? visitMethodDeclaration(dart_ast.MethodDeclaration node) {
final dart_ast.FormalParameterList parameters = node.parameters!; final dart_ast.FormalParameterList parameters = node.parameters!;
late String argType; late String argType;
bool isNullable = false;
if (parameters.parameters.isEmpty) { if (parameters.parameters.isEmpty) {
argType = 'void'; argType = 'void';
} else { } else {
@ -655,6 +665,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
// ignore: always_specify_types // ignore: always_specify_types
.firstWhere((e) => e is dart_ast.TypeName) as dart_ast.TypeName; .firstWhere((e) => e is dart_ast.TypeName) as dart_ast.TypeName;
argType = typeName.name.name; argType = typeName.name.name;
isNullable = typeName.question != null;
} }
final bool isAsynchronous = _hasMetadata(node.metadata, 'async'); final bool isAsynchronous = _hasMetadata(node.metadata, 'async');
if (_currentApi != null) { if (_currentApi != null) {
@ -662,7 +673,15 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
name: node.name.name, name: node.name.name,
returnType: node.returnType.toString(), returnType: node.returnType.toString(),
argType: argType, argType: argType,
isAsynchronous: isAsynchronous)); isReturnNullable: node.returnType!.question != null,
isArgNullable: isNullable,
isAsynchronous: isAsynchronous,
offset: node.offset));
} else if (_currentClass != null) {
_errors.add(Error(
message:
'Methods aren\'t supported in Pigeon data classes ("${node.name.name}").',
lineNumber: _calculateLineNumber(source, node.offset)));
} }
node.visitChildren(this); node.visitChildren(this);
return null; return null;
@ -689,20 +708,59 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
'Pigeon doesn\'t support static fields ("${node.toString()}"), consider using enums.', 'Pigeon doesn\'t support static fields ("${node.toString()}"), consider using enums.',
lineNumber: _calculateLineNumber(source, node.offset))); lineNumber: _calculateLineNumber(source, node.offset)));
} else if (type is dart_ast.NamedType) { } else if (type is dart_ast.NamedType) {
_currentClass!.fields.add(Field( final _FindInitializer findInitializerVisitor = _FindInitializer();
name: node.fields.variables[0].name.name, node.visitChildren(findInitializerVisitor);
dataType: type.toString(), if (findInitializerVisitor.initializer != null) {
offset: node.offset, _errors.add(Error(
)); message:
'Initialization isn\'t supported for fields in Pigeon data classes ("$node"), just use nullable types with no initializer (example "int? x;").',
lineNumber: _calculateLineNumber(source, node.offset)));
} else {
final dart_ast.TypeArgumentList? typeArguments = type.typeArguments;
_currentClass!.fields.add(Field(
name: node.fields.variables[0].name.name,
dataType: type.name.name,
isNullable: type.question != null,
// TODO(aaclarke): This probably has to be recursive at some point.
// ignore: prefer_null_aware_operators
typeArguments: typeArguments == null
? null
: typeArguments.arguments
.map((dart_ast.TypeAnnotation e) => Field(
name: '',
dataType: (e.childEntities.first
as dart_ast.SimpleIdentifier)
.name,
isNullable: e.question != null,
offset: e.offset,
))
.toList(),
offset: node.offset,
));
}
} else { } else {
_errors.add(Error( _errors.add(Error(
message: 'Expected a named type but found "${node.toString()}".', message: 'Expected a named type but found "${node.toString()}".',
lineNumber: _calculateLineNumber(source, node.offset))); lineNumber: _calculateLineNumber(source, node.offset)));
} }
} else if (_currentApi != null) {
_errors.add(Error(
message: 'Fields aren\'t supported in Pigeon API classes ("$node").',
lineNumber: _calculateLineNumber(source, node.offset)));
} }
node.visitChildren(this); node.visitChildren(this);
return null; return null;
} }
@override
Object? visitConstructorDeclaration(dart_ast.ConstructorDeclaration node) {
final String type = _currentApi != null ? 'API classes' : 'data classes';
_errors.add(Error(
message: 'Constructors aren\'t supported in $type ("$node").',
lineNumber: _calculateLineNumber(source, node.offset)));
node.visitChildren(this);
return null;
}
} }
int? _calculateLineNumberNullable(String contents, int? offset) { int? _calculateLineNumberNullable(String contents, int? offset) {

View File

@ -15,6 +15,7 @@ void main() {
Field( Field(
name: 'field1', name: 'field1',
dataType: 'dataType1', dataType: 'dataType1',
isNullable: true,
), ),
], ],
); );
@ -57,17 +58,26 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -80,11 +90,23 @@ void main() {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(
name: 'Input', name: 'Input',
fields: <Field>[Field(name: 'input', dataType: 'String')], fields: <Field>[
Field(
name: 'input',
dataType: 'String',
isNullable: true,
)
],
), ),
Class( Class(
name: 'Nested', name: 'Nested',
fields: <Field>[Field(name: 'nested', dataType: 'Input')], fields: <Field>[
Field(
name: 'nested',
dataType: 'Input',
isNullable: true,
)
],
) )
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
@ -110,17 +132,26 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -135,14 +166,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -157,14 +193,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -183,14 +224,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -205,14 +251,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'EnumClass', argType: 'EnumClass',
isArgNullable: false,
returnType: 'EnumClass', returnType: 'EnumClass',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'EnumClass', fields: <Field>[
name: 'EnumClass', Field(
fields: <Field>[Field(name: 'enum1', dataType: 'Enum')]), name: 'enum1',
dataType: 'Enum',
isNullable: true,
)
]),
], enums: <Enum>[ ], enums: <Enum>[
Enum( Enum(
name: 'Enum', name: 'Enum',
@ -237,14 +288,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'EnumClass', argType: 'EnumClass',
isArgNullable: false,
returnType: 'EnumClass', returnType: 'EnumClass',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'EnumClass', fields: <Field>[
name: 'EnumClass', Field(
fields: <Field>[Field(name: 'enum1', dataType: 'Enum')]), name: 'enum1',
dataType: 'Enum',
isNullable: true,
)
]),
], enums: <Enum>[ ], enums: <Enum>[
Enum( Enum(
name: 'Enum', name: 'Enum',
@ -271,14 +327,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -296,23 +357,33 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
), ),
Method( Method(
name: 'voidReturner', name: 'voidReturner',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer mainCodeSink = StringBuffer(); final StringBuffer mainCodeSink = StringBuffer();
final StringBuffer testCodeSink = StringBuffer(); final StringBuffer testCodeSink = StringBuffer();
@ -342,6 +413,7 @@ void main() {
Field( Field(
name: 'field1', name: 'field1',
dataType: 'dataType1', dataType: 'dataType1',
isNullable: true,
), ),
], ],
); );
@ -362,17 +434,26 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true, isAsynchronous: true,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -389,17 +470,26 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: true, isAsynchronous: true,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -415,17 +505,26 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true, isAsynchronous: true,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);
@ -440,14 +539,19 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true, isAsynchronous: true,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink); generateDart(const DartOptions(isNullSafe: false), root, sink);

View File

@ -14,6 +14,7 @@ void main() {
Field( Field(
name: 'field1', name: 'field1',
dataType: 'int', dataType: 'int',
isNullable: true,
), ),
], ],
); );
@ -63,6 +64,7 @@ void main() {
Field( Field(
name: 'field1', name: 'field1',
dataType: 'int', dataType: 'int',
isNullable: true,
) )
], ],
); );
@ -86,17 +88,18 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: true,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(name: 'input', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'input', dataType: 'String')]), ]),
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(name: 'output', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'output', dataType: 'String')]) ])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -110,14 +113,14 @@ void main() {
test('all the simple datatypes header', () { test('all the simple datatypes header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class(name: 'Foobar', fields: <Field>[ Class(name: 'Foobar', fields: <Field>[
Field(name: 'aBool', dataType: 'bool'), Field(name: 'aBool', dataType: 'bool', isNullable: true),
Field(name: 'aInt', dataType: 'int'), Field(name: 'aInt', dataType: 'int', isNullable: true),
Field(name: 'aDouble', dataType: 'double'), Field(name: 'aDouble', dataType: 'double', isNullable: true),
Field(name: 'aString', dataType: 'String'), Field(name: 'aString', dataType: 'String', isNullable: true),
Field(name: 'aUint8List', dataType: 'Uint8List'), Field(name: 'aUint8List', dataType: 'Uint8List', isNullable: true),
Field(name: 'aInt32List', dataType: 'Int32List'), Field(name: 'aInt32List', dataType: 'Int32List', isNullable: true),
Field(name: 'aInt64List', dataType: 'Int64List'), Field(name: 'aInt64List', dataType: 'Int64List', isNullable: true),
Field(name: 'aFloat64List', dataType: 'Float64List'), Field(name: 'aFloat64List', dataType: 'Float64List', isNullable: true),
]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
@ -141,17 +144,18 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(name: 'input', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'input', dataType: 'String')]), ]),
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(name: 'output', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'output', dataType: 'String')]) ])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -167,14 +171,15 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(name: 'input', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'input', dataType: 'String')]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -190,14 +195,15 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(name: 'input', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'input', dataType: 'String')]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -214,14 +220,15 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(name: 'output', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'output', dataType: 'String')]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -237,14 +244,15 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: false, isAsynchronous: false,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(name: 'output', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'output', dataType: 'String')]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -256,9 +264,9 @@ void main() {
test('gen list', () { test('gen list', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(name: 'field1', dataType: 'List', isNullable: true)
fields: <Field>[Field(name: 'field1', dataType: 'List')]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -270,9 +278,9 @@ void main() {
test('gen map', () { test('gen map', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(name: 'field1', dataType: 'Map', isNullable: true)
fields: <Field>[Field(name: 'field1', dataType: 'Map')]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -289,6 +297,7 @@ void main() {
Field( Field(
name: 'nested', name: 'nested',
dataType: 'Nested', dataType: 'Nested',
isNullable: true,
) )
], ],
); );
@ -298,6 +307,7 @@ void main() {
Field( Field(
name: 'data', name: 'data',
dataType: 'int', dataType: 'int',
isNullable: true,
) )
], ],
); );
@ -324,17 +334,18 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true, isAsynchronous: true,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(name: 'input', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'input', dataType: 'String')]), ]),
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(name: 'output', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'output', dataType: 'String')]) ])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -357,17 +368,18 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true, isAsynchronous: true,
) )
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(name: 'input', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'input', dataType: 'String')]), ]),
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(name: 'output', dataType: 'String', isNullable: true)
fields: <Field>[Field(name: 'output', dataType: 'String')]) ])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@ -391,6 +403,7 @@ void main() {
Field( Field(
name: 'enum1', name: 'enum1',
dataType: 'Enum1', dataType: 'Enum1',
isNullable: true,
), ),
], ],
); );

View File

@ -9,9 +9,13 @@ import 'package:test/test.dart';
void main() { void main() {
test('gen one class header', () { test('gen one class header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(
fields: <Field>[Field(name: 'field1', dataType: 'String')]), name: 'field1',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(), root, sink); generateObjcHeader(const ObjcOptions(), root, sink);
@ -22,9 +26,13 @@ void main() {
test('gen one class source', () { test('gen one class source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(
fields: <Field>[Field(name: 'field1', dataType: 'String')]), name: 'field1',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink); generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@ -76,8 +84,16 @@ void main() {
Class( Class(
name: 'Foobar', name: 'Foobar',
fields: <Field>[ fields: <Field>[
Field(name: 'field1', dataType: 'String'), Field(
Field(name: 'enum1', dataType: 'Enum1'), name: 'field1',
dataType: 'String',
isNullable: true,
),
Field(
name: 'enum1',
dataType: 'Enum1',
isNullable: true,
),
], ],
), ),
], ],
@ -102,15 +118,27 @@ void main() {
test('gen one api header', () { test('gen one api header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'Output') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(), root, sink); generateObjcHeader(const ObjcOptions(), root, sink);
@ -125,15 +153,27 @@ void main() {
test('gen one api source', () { test('gen one api source', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'Output') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink); generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@ -147,14 +187,46 @@ void main() {
test('all the simple datatypes header', () { test('all the simple datatypes header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class(name: 'Foobar', fields: <Field>[ Class(name: 'Foobar', fields: <Field>[
Field(name: 'aBool', dataType: 'bool'), Field(
Field(name: 'aInt', dataType: 'int'), name: 'aBool',
Field(name: 'aDouble', dataType: 'double'), dataType: 'bool',
Field(name: 'aString', dataType: 'String'), isNullable: true,
Field(name: 'aUint8List', dataType: 'Uint8List'), ),
Field(name: 'aInt32List', dataType: 'Int32List'), Field(
Field(name: 'aInt64List', dataType: 'Int64List'), name: 'aInt',
Field(name: 'aFloat64List', dataType: 'Float64List'), dataType: 'int',
isNullable: true,
),
Field(
name: 'aDouble',
dataType: 'double',
isNullable: true,
),
Field(
name: 'aString',
dataType: 'String',
isNullable: true,
),
Field(
name: 'aUint8List',
dataType: 'Uint8List',
isNullable: true,
),
Field(
name: 'aInt32List',
dataType: 'Int32List',
isNullable: true,
),
Field(
name: 'aInt64List',
dataType: 'Int64List',
isNullable: true,
),
Field(
name: 'aFloat64List',
dataType: 'Float64List',
isNullable: true,
),
]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
@ -180,7 +252,11 @@ void main() {
test('bool source', () { test('bool source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class(name: 'Foobar', fields: <Field>[ Class(name: 'Foobar', fields: <Field>[
Field(name: 'aBool', dataType: 'bool'), Field(
name: 'aBool',
dataType: 'bool',
isNullable: true,
),
]), ]),
], enums: <Enum>[]); ], enums: <Enum>[]);
@ -193,12 +269,20 @@ void main() {
test('nested class header', () { test('nested class header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Nested', isNullable: true,
fields: <Field>[Field(name: 'nested', dataType: 'Input')]) )
]),
Class(name: 'Nested', fields: <Field>[
Field(
name: 'nested',
dataType: 'Input',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink); generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink);
@ -209,12 +293,20 @@ void main() {
test('nested class source', () { test('nested class source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Nested', isNullable: true,
fields: <Field>[Field(name: 'nested', dataType: 'Input')]) )
]),
Class(name: 'Nested', fields: <Field>[
Field(
name: 'nested',
dataType: 'Input',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink); generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@ -225,9 +317,13 @@ void main() {
test('prefix class header', () { test('prefix class header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(
fields: <Field>[Field(name: 'field1', dataType: 'String')]), name: 'field1',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink); generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink);
@ -237,9 +333,13 @@ void main() {
test('prefix class source', () { test('prefix class source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(
fields: <Field>[Field(name: 'field1', dataType: 'String')]), name: 'field1',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink); generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
@ -250,15 +350,27 @@ void main() {
test('prefix nested class header', () { test('prefix nested class header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'Nested') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'Nested')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Nested', isNullable: true,
fields: <Field>[Field(name: 'nested', dataType: 'Input')]) )
]),
Class(name: 'Nested', fields: <Field>[
Field(
name: 'nested',
dataType: 'Input',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink); generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink);
@ -271,15 +383,27 @@ void main() {
test('prefix nested class source', () { test('prefix nested class source', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'Nested') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'Nested')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Nested', isNullable: true,
fields: <Field>[Field(name: 'nested', dataType: 'Input')]) )
]),
Class(name: 'Nested', fields: <Field>[
Field(
name: 'nested',
dataType: 'Input',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink); generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
@ -292,15 +416,27 @@ void main() {
test('gen flutter api header', () { test('gen flutter api header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'Output') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink); generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink);
@ -316,15 +452,27 @@ void main() {
test('gen flutter api source', () { test('gen flutter api source', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'Output') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]) )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
])
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink); generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@ -336,12 +484,20 @@ void main() {
test('gen host void header', () { test('gen host void header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'void')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -353,12 +509,20 @@ void main() {
test('gen host void source', () { test('gen host void source', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'void')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(
@ -372,12 +536,20 @@ void main() {
test('gen flutter void return header', () { test('gen flutter void return header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'void')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -389,12 +561,20 @@ void main() {
test('gen flutter void return source', () { test('gen flutter void return source', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void') Method(
name: 'doSomething',
argType: 'Input',
isArgNullable: false,
returnType: 'void')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(
@ -407,12 +587,20 @@ void main() {
test('gen host void arg header', () { test('gen host void arg header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'void', returnType: 'Output') Method(
name: 'doSomething',
argType: 'void',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -424,12 +612,20 @@ void main() {
test('gen host void arg source', () { test('gen host void arg source', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'void', returnType: 'Output') Method(
name: 'doSomething',
argType: 'void',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(
@ -441,12 +637,20 @@ void main() {
test('gen flutter void arg header', () { test('gen flutter void arg header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'void', returnType: 'Output') Method(
name: 'doSomething',
argType: 'void',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -461,12 +665,20 @@ void main() {
test('gen flutter void arg header', () { test('gen flutter void arg header', () {
final Root root = Root(apis: <Api>[ final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'void', returnType: 'Output') Method(
name: 'doSomething',
argType: 'void',
isArgNullable: false,
returnType: 'Output')
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(
@ -481,9 +693,13 @@ void main() {
test('gen list', () { test('gen list', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(
fields: <Field>[Field(name: 'field1', dataType: 'List')]), name: 'field1',
dataType: 'List',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(), root, sink); generateObjcHeader(const ObjcOptions(), root, sink);
@ -494,9 +710,13 @@ void main() {
test('gen map', () { test('gen map', () {
final Root root = Root(apis: <Api>[], classes: <Class>[ final Root root = Root(apis: <Api>[], classes: <Class>[
Class( Class(name: 'Foobar', fields: <Field>[
name: 'Foobar', Field(
fields: <Field>[Field(name: 'field1', dataType: 'Map')]), name: 'field1',
dataType: 'Map',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader(const ObjcOptions(), root, sink); generateObjcHeader(const ObjcOptions(), root, sink);
@ -511,16 +731,25 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: true) isAsynchronous: true)
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]), )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -538,16 +767,25 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true) isAsynchronous: true)
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]), )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -565,13 +803,18 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true) isAsynchronous: true)
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcHeader( generateObjcHeader(
@ -589,6 +832,7 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: true) isAsynchronous: true)
]) ])
@ -609,16 +853,25 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true) isAsynchronous: true)
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]), )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(
@ -636,16 +889,25 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'Input', argType: 'Input',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: true) isAsynchronous: true)
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Input', fields: <Field>[
name: 'Input', Field(
fields: <Field>[Field(name: 'input', dataType: 'String')]), name: 'input',
Class( dataType: 'String',
name: 'Output', isNullable: true,
fields: <Field>[Field(name: 'output', dataType: 'String')]), )
]),
Class(name: 'Output', fields: <Field>[
Field(
name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(
@ -663,6 +925,7 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'void', returnType: 'void',
isAsynchronous: true) isAsynchronous: true)
]) ])
@ -681,13 +944,18 @@ void main() {
Method( Method(
name: 'doSomething', name: 'doSomething',
argType: 'void', argType: 'void',
isArgNullable: false,
returnType: 'Output', returnType: 'Output',
isAsynchronous: true) isAsynchronous: true)
]) ])
], classes: <Class>[ ], classes: <Class>[
Class( Class(name: 'Output', fields: <Field>[
name: 'Output', Field(
fields: <Field>[Field(name: 'output', dataType: 'String')]), name: 'output',
dataType: 'String',
isNullable: true,
)
]),
], enums: <Enum>[]); ], enums: <Enum>[]);
final StringBuffer sink = StringBuffer(); final StringBuffer sink = StringBuffer();
generateObjcSource( generateObjcSource(

View File

@ -184,10 +184,12 @@ void main() {
expect(input?.fields.length, equals(1)); expect(input?.fields.length, equals(1));
expect(input?.fields[0].name, equals('input')); expect(input?.fields[0].name, equals('input'));
expect(input?.fields[0].dataType, equals('String')); expect(input?.fields[0].dataType, equals('String'));
expect(input?.fields[0].isNullable, isTrue);
expect(output?.fields.length, equals(1)); expect(output?.fields.length, equals(1));
expect(output?.fields[0].name, equals('output')); expect(output?.fields[0].name, equals('output'));
expect(output?.fields[0].dataType, equals('String')); expect(output?.fields[0].dataType, equals('String'));
expect(output?.fields[0].isNullable, isTrue);
}); });
test('invalid datatype', () { test('invalid datatype', () {
@ -208,6 +210,7 @@ void main() {
expect(results.root.classes[0].name, equals('ClassWithEnum')); expect(results.root.classes[0].name, equals('ClassWithEnum'));
expect(results.root.classes[0].fields.length, equals(1)); expect(results.root.classes[0].fields.length, equals(1));
expect(results.root.classes[0].fields[0].dataType, equals('Enum1')); expect(results.root.classes[0].fields[0].dataType, equals('Enum1'));
expect(results.root.classes[0].fields[0].isNullable, isTrue);
expect(results.root.classes[0].fields[0].name, equals('enum1')); expect(results.root.classes[0].fields[0].name, equals('enum1'));
}); });
@ -232,6 +235,7 @@ void main() {
results.root.classes.firstWhere((Class x) => x.name == 'Nested'); results.root.classes.firstWhere((Class x) => x.name == 'Nested');
expect(nested.fields.length, equals(1)); expect(nested.fields.length, equals(1));
expect(nested.fields[0].dataType, equals('Input1')); expect(nested.fields[0].dataType, equals('Input1'));
expect(nested.fields[0].isNullable, isTrue);
}); });
test('flutter api', () { test('flutter api', () {
@ -417,6 +421,161 @@ abstract class NotificationsHostApi {
}); });
}); });
test('test method in data class error', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
class Foo {
int? x;
int? foo() { return x; }
}
@HostApi()
abstract class Api {
Foo doit(Foo foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 3);
expect(results.errors[0].message, contains('Method'));
});
});
test('test field initialization', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
class Foo {
int? x = 123;
}
@HostApi()
abstract class Api {
Foo doit(Foo foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 2);
expect(results.errors[0].message, contains('Initialization'));
});
});
test('test field in api error', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
class Foo {
int? x;
}
@HostApi()
abstract class Api {
int? x;
Foo doit(Foo foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 7);
expect(results.errors[0].message, contains('Field'));
});
});
test('constructor in data class', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
class Foo {
int? x;
Foo(this.x);
}
@HostApi()
abstract class Api {
Foo doit(Foo foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 3);
expect(results.errors[0].message, contains('Constructor'));
});
});
test('nullable api arguments', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
class Foo {
int? x;
}
@HostApi()
abstract class Api {
Foo doit(Foo? foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 7);
expect(results.errors[0].message, contains('Nullable'));
});
});
test('nullable api return', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
class Foo {
int? x;
}
@HostApi()
abstract class Api {
Foo? doit(Foo foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 7);
expect(results.errors[0].message, contains('Nullable'));
});
});
test('primitive arguments', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
@HostApi()
abstract class Api {
void doit(int foo);
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 3);
expect(results.errors[0].message, contains('Primitive'));
});
});
test('primitive return', () {
final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) {
file.writeAsStringSync('''
@HostApi()
abstract class Api {
int doit();
}
''');
final ParseResults results = dartle.parseFile(file.path);
expect(results.errors.length, 1);
expect(results.errors[0].lineNumber, 3);
expect(results.errors[0].message, contains('Primitive'));
});
});
test('test invalid import', () { test('test invalid import', () {
final Pigeon dartle = Pigeon.setup(); final Pigeon dartle = Pigeon.setup();
_withTempFile('compilationError.dart', (File file) { _withTempFile('compilationError.dart', (File file) {