blob: 0b2c93c39dd717c7dcfa9b2aee233eb19115e644 [file] [log] [blame]
// Copyright 2013 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 'package:pigeon/ast.dart';
import 'package:pigeon/cpp_generator.dart';
import 'package:pigeon/pigeon.dart' show Error;
import 'package:test/test.dart';
void main() {
test('gen one api', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'Input',
isNullable: false,
),
name: 'input',
offset: null)
],
returnType:
const TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
Class(name: 'Input', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'input',
offset: null)
]),
Class(name: 'Output', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'output',
offset: null)
])
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Input'));
expect(code, contains('class Output'));
expect(code, contains('class Api'));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Input::Input()'));
expect(code, contains('Output::Output'));
expect(
code,
contains(
'void Api::SetUp(flutter::BinaryMessenger* binary_messenger, Api* api)'));
}
});
test('naming follows style', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'Input',
isNullable: false,
),
name: 'someInput',
offset: null)
],
returnType:
const TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
Class(name: 'Input', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'bool',
isNullable: false,
),
name: 'inputField',
offset: null)
]),
Class(name: 'Output', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'bool',
isNullable: false,
),
name: 'outputField',
offset: null)
])
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(), root, sink);
final String code = sink.toString();
// Method name and argument names should be adjusted.
expect(code, contains(' DoSomething(const Input& some_input)'));
// Getters and setters should use optional getter/setter style.
expect(code, contains('bool input_field()'));
expect(code, contains('void set_input_field(bool value_arg)'));
expect(code, contains('bool output_field()'));
expect(code, contains('void set_output_field(bool value_arg)'));
// Instance variables should be adjusted.
expect(code, contains('bool input_field_'));
expect(code, contains('bool output_field_'));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('pointer_input_field'));
expect(code, contains('Output::output_field()'));
expect(code, contains('Output::set_output_field(bool value_arg)'));
expect(code, contains('encodable_output_field'));
}
});
test('Spaces before {', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'Input',
isNullable: false,
),
name: 'input',
offset: null)
],
returnType:
const TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
Class(name: 'Input', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'input',
offset: null)
]),
Class(name: 'Output', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'output',
offset: null)
])
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(), root, sink);
final String code = sink.toString();
expect(code, isNot(contains('){')));
expect(code, isNot(contains('const{')));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(), root, sink);
final String code = sink.toString();
expect(code, isNot(contains('){')));
expect(code, isNot(contains('const{')));
}
});
test('include blocks follow style', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'input',
offset: null)
],
returnType: const TypeDeclaration(baseName: 'int', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('''
#include <flutter/basic_message_channel.h>
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
#include <flutter/standard_message_codec.h>
#include <map>
#include <optional>
#include <string>
'''));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(header: 'a_header.h'), root, sink);
final String code = sink.toString();
expect(code, contains('''
#include "a_header.h"
#include <flutter/basic_message_channel.h>
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
#include <flutter/standard_message_codec.h>
#include <map>
#include <optional>
#include <string>
'''));
}
});
test('namespaces follows style', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'input',
offset: null)
],
returnType: const TypeDeclaration(baseName: 'int', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(namespace: 'foo'), root, sink);
final String code = sink.toString();
expect(code, contains('namespace foo {'));
expect(code, contains('} // namespace foo'));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(namespace: 'foo'), root, sink);
final String code = sink.toString();
expect(code, contains('namespace foo {'));
expect(code, contains('} // namespace foo'));
}
});
test('data classes handle nullable fields', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'Input',
isNullable: false,
),
name: 'someInput',
offset: null)
],
returnType: const TypeDeclaration.voidDeclaration(),
isAsynchronous: false,
)
])
], classes: <Class>[
Class(name: 'Nested', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'bool',
isNullable: true,
),
name: 'nestedValue',
offset: null),
]),
Class(name: 'Input', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'bool',
isNullable: true,
),
name: 'nullableBool',
offset: null),
NamedType(
type: const TypeDeclaration(
baseName: 'int',
isNullable: true,
),
name: 'nullableInt',
offset: null),
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: true,
),
name: 'nullableString',
offset: null),
NamedType(
type: const TypeDeclaration(
baseName: 'Nested',
isNullable: true,
),
name: 'nullableNested',
offset: null),
]),
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(), root, sink);
final String code = sink.toString();
// Getters should return const pointers.
expect(code, contains('const bool* nullable_bool()'));
expect(code, contains('const int64_t* nullable_int()'));
expect(code, contains('const std::string* nullable_string()'));
expect(code, contains('const Nested* nullable_nested()'));
// Setters should take const pointers.
expect(code, contains('void set_nullable_bool(const bool* value_arg)'));
expect(code, contains('void set_nullable_int(const int64_t* value_arg)'));
// Strings should be string_view rather than string as an argument.
expect(
code,
contains(
'void set_nullable_string(const std::string_view* value_arg)'));
expect(
code, contains('void set_nullable_nested(const Nested* value_arg)'));
// Setters should have non-null-style variants.
expect(code, contains('void set_nullable_bool(bool value_arg)'));
expect(code, contains('void set_nullable_int(int64_t value_arg)'));
expect(code,
contains('void set_nullable_string(std::string_view value_arg)'));
expect(
code, contains('void set_nullable_nested(const Nested& value_arg)'));
// Instance variables should be std::optionals.
expect(code, contains('std::optional<bool> nullable_bool_'));
expect(code, contains('std::optional<int64_t> nullable_int_'));
expect(code, contains('std::optional<std::string> nullable_string_'));
expect(code, contains('std::optional<Nested> nullable_nested_'));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(), root, sink);
final String code = sink.toString();
// Getters extract optionals.
expect(code,
contains('return nullable_bool_ ? &(*nullable_bool_) : nullptr;'));
expect(code,
contains('return nullable_int_ ? &(*nullable_int_) : nullptr;'));
expect(
code,
contains(
'return nullable_string_ ? &(*nullable_string_) : nullptr;'));
expect(
code,
contains(
'return nullable_nested_ ? &(*nullable_nested_) : nullptr;'));
// Setters convert to optionals.
expect(
code,
contains('nullable_bool_ = value_arg ? '
'std::optional<bool>(*value_arg) : std::nullopt;'));
expect(
code,
contains('nullable_int_ = value_arg ? '
'std::optional<int64_t>(*value_arg) : std::nullopt;'));
expect(
code,
contains('nullable_string_ = value_arg ? '
'std::optional<std::string>(*value_arg) : std::nullopt;'));
expect(
code,
contains('nullable_nested_ = value_arg ? '
'std::optional<Nested>(*value_arg) : std::nullopt;'));
// Serialization handles optionals.
expect(
code,
contains('{flutter::EncodableValue("nullableBool"), '
'nullable_bool_ ? flutter::EncodableValue(*nullable_bool_) '
': flutter::EncodableValue()}'));
expect(
code,
contains('{flutter::EncodableValue("nullableNested"), '
'nullable_nested_ ? nullable_nested_->ToEncodableMap() '
': flutter::EncodableValue()}'));
}
});
test('data classes handle non-nullable fields', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
arguments: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'Input',
isNullable: false,
),
name: 'someInput',
offset: null)
],
returnType: const TypeDeclaration.voidDeclaration(),
isAsynchronous: false,
)
])
], classes: <Class>[
Class(name: 'Nested', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'bool',
isNullable: false,
),
name: 'nestedValue',
offset: null),
]),
Class(name: 'Input', fields: <NamedType>[
NamedType(
type: const TypeDeclaration(
baseName: 'bool',
isNullable: false,
),
name: 'nonNullableBool',
offset: null),
NamedType(
type: const TypeDeclaration(
baseName: 'int',
isNullable: false,
),
name: 'nonNullableInt',
offset: null),
NamedType(
type: const TypeDeclaration(
baseName: 'String',
isNullable: false,
),
name: 'nonNullableString',
offset: null),
NamedType(
type: const TypeDeclaration(
baseName: 'Nested',
isNullable: false,
),
name: 'nonNullableNested',
offset: null),
]),
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
generateCppHeader('', const CppOptions(), root, sink);
final String code = sink.toString();
// POD getters should return copies references.
expect(code, contains('bool non_nullable_bool()'));
expect(code, contains('int64_t non_nullable_int()'));
// Non-POD getters should return const references.
expect(code, contains('const std::string& non_nullable_string()'));
expect(code, contains('const Nested& non_nullable_nested()'));
// POD setters should take values.
expect(code, contains('void set_non_nullable_bool(bool value_arg)'));
expect(code, contains('void set_non_nullable_int(int64_t value_arg)'));
// Strings should be string_view as an argument.
expect(code,
contains('void set_non_nullable_string(std::string_view value_arg)'));
// Other non-POD setters should take const references.
expect(code,
contains('void set_non_nullable_nested(const Nested& value_arg)'));
// Instance variables should be plain types.
expect(code, contains('bool non_nullable_bool_;'));
expect(code, contains('int64_t non_nullable_int_;'));
expect(code, contains('std::string non_nullable_string_;'));
expect(code, contains('Nested non_nullable_nested_;'));
}
{
final StringBuffer sink = StringBuffer();
generateCppSource(const CppOptions(), root, sink);
final String code = sink.toString();
// Getters just return the value.
expect(code, contains('return non_nullable_bool_;'));
expect(code, contains('return non_nullable_int_;'));
expect(code, contains('return non_nullable_string_;'));
expect(code, contains('return non_nullable_nested_;'));
// Setters just assign the value.
expect(code, contains('non_nullable_bool_ = value_arg;'));
expect(code, contains('non_nullable_int_ = value_arg;'));
expect(code, contains('non_nullable_string_ = value_arg;'));
expect(code, contains('non_nullable_nested_ = value_arg;'));
// Serialization uses the value directly.
expect(
code,
contains('{flutter::EncodableValue("nonNullableBool"), '
'flutter::EncodableValue(non_nullable_bool_)}'));
expect(
code,
contains('{flutter::EncodableValue("nonNullableNested"), '
'non_nullable_nested_.ToEncodableMap()}'));
}
});
test('enum argument', () {
final Root root = Root(
apis: <Api>[
Api(name: 'Bar', location: ApiLocation.host, methods: <Method>[
Method(
name: 'bar',
returnType: const TypeDeclaration.voidDeclaration(),
arguments: <NamedType>[
NamedType(
name: 'foo',
type: const TypeDeclaration(
baseName: 'Foo', isNullable: false))
])
])
],
classes: <Class>[],
enums: <Enum>[
Enum(name: 'Foo', members: <String>['one', 'two'])
],
);
final List<Error> errors = validateCpp(const CppOptions(), root);
expect(errors.length, 1);
});
}