blob: 84c733a26eb068d04104198a8950d62131a84f03 [file] [log] [blame]
#!/usr/bin/env dart
// Copyright (c) 2013, 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.
// @dart=2.11
import 'package:protoc_plugin/indenting_writer.dart';
import 'package:protoc_plugin/protoc.dart';
import 'package:protoc_plugin/src/generated/descriptor.pb.dart';
import 'package:protoc_plugin/src/generated/plugin.pb.dart';
import 'package:protoc_plugin/src/linker.dart';
import 'package:protoc_plugin/src/options.dart';
import 'package:test/test.dart';
import 'golden_file.dart';
FileDescriptorProto buildFileDescriptor(
{bool phoneNumber = true, bool topLevelEnum = false}) {
var fd = FileDescriptorProto()..name = 'test';
if (topLevelEnum) {
fd.enumType.add(EnumDescriptorProto()
..name = 'PhoneType'
..value.addAll([
EnumValueDescriptorProto()
..name = 'MOBILE'
..number = 0,
EnumValueDescriptorProto()
..name = 'HOME'
..number = 1,
EnumValueDescriptorProto()
..name = 'WORK'
..number = 2,
EnumValueDescriptorProto()
..name = 'BUSINESS'
..number = 2
]));
}
if (phoneNumber) {
fd.messageType.add(DescriptorProto()
..name = 'PhoneNumber'
..field.addAll([
// required string number = 1;
FieldDescriptorProto()
..name = 'number'
..jsonName = 'number'
..number = 1
..label = FieldDescriptorProto_Label.LABEL_REQUIRED
..type = FieldDescriptorProto_Type.TYPE_STRING,
// optional int32 type = 2;
// OR
// optional PhoneType type = 2;
FieldDescriptorProto()
..name = 'type'
..jsonName = 'type'
..number = 2
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = topLevelEnum
? FieldDescriptorProto_Type.TYPE_ENUM
: FieldDescriptorProto_Type.TYPE_INT32
..typeName = topLevelEnum ? '.PhoneType' : '',
// optional string name = 3 [default = "$"];
FieldDescriptorProto()
..name = 'name'
..jsonName = 'name'
..number = 3
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_STRING
..defaultValue = r'$'
]));
}
return fd;
}
FileDescriptorProto createInt64Proto() {
var fd = FileDescriptorProto()..name = 'test';
fd.messageType.add(DescriptorProto()
..name = 'Int64'
..field.add(
// optional int64 value = 1;
FieldDescriptorProto()
..name = 'value'
..jsonName = 'value'
..number = 1
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_INT64,
));
return fd;
}
void main() {
test('FileGenerator outputs a .pb.dart file for a proto with one message',
() {
var fd = buildFileDescriptor();
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(
fg.generateMainFile().toString(), 'test/goldens/oneMessage.pb');
});
test('FileGenerator outputs a .pb.dart file for an Int64 message', () {
var fd = createInt64Proto();
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(
fg.generateMainFile().toString(), 'test/goldens/int64.pb');
});
test(
'FileGenerator outputs a .pb.dart.meta file for a proto with one message',
() {
var fd = buildFileDescriptor();
var options = parseGenerationOptions(
CodeGeneratorRequest()..parameter = 'generate_kythe_info',
CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(fg.generateMainFile().sourceLocationInfo.toString(),
'test/goldens/oneMessage.pb.meta');
});
test('FileGenerator outputs a pbjson.dart file for a proto with one message',
() {
var fd = buildFileDescriptor();
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(
fg.generateJsonFile(), 'test/goldens/oneMessage.pbjson');
});
test('FileGenerator generates files for a top-level enum', () {
var fd = buildFileDescriptor(phoneNumber: false, topLevelEnum: true);
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(
fg.generateMainFile().toString(), 'test/goldens/topLevelEnum.pb');
expectMatchesGoldenFile(
fg.generateEnumFile().toString(), 'test/goldens/topLevelEnum.pbenum');
});
test('FileGenerator generates metadata files for a top-level enum', () {
var fd = buildFileDescriptor(phoneNumber: false, topLevelEnum: true);
var options = parseGenerationOptions(
CodeGeneratorRequest()..parameter = 'generate_kythe_info',
CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(fg.generateMainFile().sourceLocationInfo.toString(),
'test/goldens/topLevelEnum.pb.meta');
expectMatchesGoldenFile(fg.generateEnumFile().sourceLocationInfo.toString(),
'test/goldens/topLevelEnum.pbenum.meta');
});
test('FileGenerator generates a .pbjson.dart file for a top-level enum', () {
var fd = buildFileDescriptor(phoneNumber: false, topLevelEnum: true);
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
expectMatchesGoldenFile(
fg.generateJsonFile(), 'test/goldens/topLevelEnum.pbjson');
});
test('FileGenerator outputs library for a .proto in a package', () {
var fd = buildFileDescriptor();
fd.package = 'pb_library';
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
var writer = IndentingWriter(filename: '');
fg.writeMainHeader(writer);
expectMatchesGoldenFile(
writer.toString(), 'test/goldens/header_in_package.pb');
});
test('FileGenerator outputs a fixnum import when needed', () {
var fd = FileDescriptorProto()
..name = 'test'
..messageType.add(DescriptorProto()
..name = 'Count'
..field.addAll([
FieldDescriptorProto()
..name = 'count'
..jsonName = 'count'
..number = 1
..type = FieldDescriptorProto_Type.TYPE_INT64
]));
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
var writer = IndentingWriter(filename: '');
fg.writeMainHeader(writer);
expectMatchesGoldenFile(
writer.toString(), 'test/goldens/header_with_fixnum.pb');
});
test('FileGenerator outputs files for a service', () {
var empty = DescriptorProto()..name = 'Empty';
var sd = ServiceDescriptorProto()
..name = 'Test'
..method.add(MethodDescriptorProto()
..name = 'Ping'
..inputType = '.Empty'
..outputType = '.Empty');
var fd = FileDescriptorProto()
..name = 'test'
..messageType.add(empty)
..service.add(sd);
var options =
parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
var fg = FileGenerator(fd, options);
link(options, [fg]);
var writer = IndentingWriter(filename: '');
fg.writeMainHeader(writer);
expectMatchesGoldenFile(
fg.generateMainFile().toString(), 'test/goldens/service.pb');
expectMatchesGoldenFile(
fg.generateServerFile(), 'test/goldens/service.pbserver');
});
test('FileGenerator does not output legacy service stubs if gRPC is selected',
() {
var empty = DescriptorProto()..name = 'Empty';
var sd = ServiceDescriptorProto()
..name = 'Test'
..method.add(MethodDescriptorProto()
..name = 'Ping'
..inputType = '.Empty'
..outputType = '.Empty');
var fd = FileDescriptorProto()
..name = 'test'
..messageType.add(empty)
..service.add(sd);
var options = GenerationOptions(useGrpc: true);
var fg = FileGenerator(fd, options);
link(options, [fg]);
var writer = IndentingWriter(filename: '');
fg.writeMainHeader(writer);
expectMatchesGoldenFile(
fg.generateMainFile().toString(), 'test/goldens/grpc_service.pb');
});
test('FileGenerator outputs gRPC stubs if gRPC is selected', () {
final input = DescriptorProto()..name = 'Input';
final output = DescriptorProto()..name = 'Output';
final unary = MethodDescriptorProto()
..name = 'Unary'
..inputType = '.Input'
..outputType = '.Output'
..clientStreaming = false
..serverStreaming = false;
final clientStreaming = MethodDescriptorProto()
..name = 'ClientStreaming'
..inputType = '.Input'
..outputType = '.Output'
..clientStreaming = true
..serverStreaming = false;
final serverStreaming = MethodDescriptorProto()
..name = 'ServerStreaming'
..inputType = '.Input'
..outputType = '.Output'
..clientStreaming = false
..serverStreaming = true;
final bidirectional = MethodDescriptorProto()
..name = 'Bidirectional'
..inputType = '.Input'
..outputType = '.Output'
..clientStreaming = true
..serverStreaming = true;
var sd = ServiceDescriptorProto()
..name = 'Test'
..method.addAll([unary, clientStreaming, serverStreaming, bidirectional]);
var fd = FileDescriptorProto()
..name = 'test'
..messageType.addAll([input, output])
..service.add(sd);
var options = GenerationOptions(useGrpc: true);
var fg = FileGenerator(fd, options);
link(options, [fg]);
var writer = IndentingWriter(filename: '');
fg.writeMainHeader(writer);
expectMatchesGoldenFile(
fg.generateGrpcFile(), 'test/goldens/grpc_service.pbgrpc');
});
test('FileGenerator generates imports for .pb.dart files', () {
// This defines three .proto files package1.proto, package2.proto and
// test.proto with the following content:
//
// package1.proto:
// ---------------
// package p1;
// message M {
// optional M m = 2;
// }
//
// package2.proto:
// ---------------
// package p2;
// message M {
// optional M m = 2;
// }
//
// test.proto:
// ---------------
// package test;
// import "package1.proto";
// import "package2.proto";
// message M {
// optional M m = 1;
// optional p1.M m1 = 2;
// optional p2.M m2 = 3;
// }
// Description of package1.proto.
var md1 = DescriptorProto()
..name = 'M'
..field.addAll([
// optional M m = 1;
FieldDescriptorProto()
..name = 'm'
..jsonName = 'm'
..number = 1
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_MESSAGE
..typeName = '.p1.M',
]);
var fd1 = FileDescriptorProto()
..package = 'p1'
..name = 'package1.proto'
..messageType.add(md1);
// Description of package1.proto.
var md2 = DescriptorProto()
..name = 'M'
..field.addAll([
// optional M m = 1;
FieldDescriptorProto()
..name = 'x'
..jsonName = 'x'
..number = 1
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_MESSAGE
..typeName = '.p2.M',
]);
var fd2 = FileDescriptorProto()
..package = 'p2'
..name = 'package2.proto'
..messageType.add(md2);
// Description of test.proto.
var md = DescriptorProto()
..name = 'M'
..field.addAll([
// optional M m = 1;
FieldDescriptorProto()
..name = 'm'
..jsonName = 'm'
..number = 1
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_MESSAGE
..typeName = '.M',
// optional p1.M m1 = 2;
FieldDescriptorProto()
..name = 'm1'
..jsonName = 'm1'
..number = 2
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_MESSAGE
..typeName = '.p1.M',
// optional p2.M m2 = 3;
FieldDescriptorProto()
..name = 'm2'
..jsonName = 'm2'
..number = 3
..label = FieldDescriptorProto_Label.LABEL_OPTIONAL
..type = FieldDescriptorProto_Type.TYPE_MESSAGE
..typeName = '.p2.M',
]);
var fd = FileDescriptorProto()
..name = 'test.proto'
..messageType.add(md);
fd.dependency.addAll(['package1.proto', 'package2.proto']);
var request = CodeGeneratorRequest();
var response = CodeGeneratorResponse();
var options = parseGenerationOptions(request, response);
var fg = FileGenerator(fd, options);
link(options,
[fg, FileGenerator(fd1, options), FileGenerator(fd2, options)]);
expectMatchesGoldenFile(
fg.generateMainFile().toString(), 'test/goldens/imports.pb');
expectMatchesGoldenFile(
fg.generateEnumFile().toString(), 'test/goldens/imports.pbjson');
});
}