Skip to content

Commit

Permalink
fix missing points and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Dogacel committed Oct 16, 2023
1 parent ca16e4f commit 094fe23
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 29 deletions.
54 changes: 40 additions & 14 deletions app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,42 @@ class CodeGenerator {
}

/**
* Generate the code for the given [Descriptors.Descriptor]. Returns a Boolean determine whether the
* descriptor has a property field or not.
* Check if the given message descriptor contains any fields or not.
*
* @param messageDescriptor [Descriptors.FileDescriptor] to generate code for.
* @return Boolean that represents whether the descriptor has a property field or not.
* This is used to avoid generating data classes with 0 properties because they are not allowed in Kotlin.
*
* @param messageDescriptor the descriptor to check if it has any fields.
* @return whether the given message descriptor contains any fields or not.
*/
private fun hasPropertyField(messageDescriptor: Descriptors.Descriptor): Boolean {
return messageDescriptor.fields.any {
it.isOptional || it.isRequired || it.isRepeated
}
fun hasNoField(messageDescriptor: Descriptors.Descriptor): Boolean {
return messageDescriptor.fields.isEmpty()
}

/**
* Get the overriden methods for a method with no fields.
*
* @param messageDescriptor descriptor of the message to generate empty override methods for.
* @return a list of [FunSpec]s that contain the overriden `toString`, `hashCode` and `equals`.
*/
private fun getEmptyOverrideFunSpecs(messageDescriptor: Descriptors.Descriptor): List<FunSpec> {
return listOf(
FunSpec.builder("toString")
.addModifiers(KModifier.OVERRIDE)
.addStatement("return %S", messageDescriptor.name)
.returns(String::class)
.build(),
FunSpec.builder("hashCode")
.addModifiers(KModifier.OVERRIDE)
.addStatement("return %L", messageDescriptor.name.hashCode())
.returns(Int::class)
.build(),
FunSpec.builder("equals")
.addModifiers(KModifier.OVERRIDE)
.addParameter("other", Any::class.asTypeName().copy(nullable = true))
.addStatement("return other is ${messageDescriptor.name}")
.returns(Boolean::class)
.build()

Check warning on line 203 in app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt#L187-L203

Added lines #L187 - L203 were not covered by tests
)
}

/**
Expand Down Expand Up @@ -208,13 +234,13 @@ class CodeGenerator {
* @return [TypeSpec.Builder] that contains the generated code.
*/
private fun generateSingleClass(messageDescriptor: Descriptors.Descriptor): TypeSpec.Builder {
val typeSpec = if (hasPropertyField(messageDescriptor)) {
TypeSpec.classBuilder(messageDescriptor.name)
.addModifiers(KModifier.DATA)
.addAnnotation(Serializable::class)
val typeSpec = TypeSpec.classBuilder(messageDescriptor.name)
.addAnnotation(Serializable::class)

if (hasNoField(messageDescriptor)) {
typeSpec.addFunctions(getEmptyOverrideFunSpecs(messageDescriptor))

Check warning on line 241 in app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt#L241

Added line #L241 was not covered by tests
} else {
TypeSpec.classBuilder(messageDescriptor.name)
.addAnnotation(Serializable::class)
typeSpec.addModifiers(KModifier.DATA)

Check warning on line 243 in app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/dogacel/kotlinx/protobuf/gen/CodeGenerator.kt#L243

Added line #L243 was not covered by tests
}

// A Data class needs a primary constructor with all the parameters.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dogacel.kotlinx.protobuf.gen

import messages.MessageNoFieldsOuterClass.MessageNoFields
import kotlin.test.Test
import kotlin.test.assertEquals

class CodeGeneratorTest {

@Test
fun hasAnyFieldShouldWork() {
val codeGenerator = CodeGenerator()

val messageNoFields = MessageNoFields.getDescriptor()
val messageSomeFields1 = MessageNoFields.SubMessageNoFields.getDescriptor()
val messageSomeFields2 = MessageNoFields.SubMessageNoFields.SubMessageNoFieldsExtend.getDescriptor()
val messageSomeFieldsOneof = MessageNoFields.SubMessageOneofFields.getDescriptor()

assertEquals(true, codeGenerator.hasNoField(messageNoFields))
assertEquals(false, codeGenerator.hasNoField(messageSomeFields1))
assertEquals(false, codeGenerator.hasNoField(messageSomeFields2))
assertEquals(false, codeGenerator.hasNoField(messageSomeFieldsOneof))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package testgen.messages

import kotlin.Any
import kotlin.Boolean
import kotlin.Int
import kotlin.String
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber

@Serializable
public class MessageNoFields() {
override fun toString(): String = "MessageNoFields"

Check warning on line 12 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L12

Added line #L12 was not covered by tests

override fun hashCode(): Int = 70_682_849

Check warning on line 14 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L14

Added line #L14 was not covered by tests

override fun equals(other: Any?): Boolean = other is MessageNoFields

Check warning on line 16 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L16

Added line #L16 was not covered by tests

@Serializable

Check warning on line 18 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L18

Added line #L18 was not covered by tests
public data class SubMessageNoFields(
@ProtoNumber(number = 1)

Check warning on line 20 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L20

Added line #L20 was not covered by tests
public val subHello: SubMessageNoFields? = null,
) {
@Serializable
public data class SubMessageNoFieldsExtend(
@ProtoNumber(number = 1)

Check warning on line 25 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L25

Added line #L25 was not covered by tests
public val type: Type = testgen.messages.MessageNoFields.Type.UNKNOWN,
)
}

@Serializable

Check warning on line 30 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L30

Added line #L30 was not covered by tests
public data class SubMessageOneofFields(
@ProtoNumber(number = 1)

Check warning on line 32 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L32

Added line #L32 was not covered by tests
public val someValue: Int? = null,
) {
init {

Check warning on line 35 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L35

Added line #L35 was not covered by tests
require(
listOfNotNull(
someValue,
).size <= 1
) { "Should only contain one of some_oneof." }
}

Check warning on line 41 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L41

Added line #L41 was not covered by tests
}

@Serializable

Check warning on line 44 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L44

Added line #L44 was not covered by tests
public enum class Type {
@ProtoNumber(number = 0)

Check warning on line 46 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L46

Added line #L46 was not covered by tests
UNKNOWN,
@ProtoNumber(number = 1)

Check warning on line 48 in generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt

View check run for this annotation

Codecov / codecov/patch

generated-code-tests/src/main/kotlin/testgen/messages/message_no_fields.proto.kt#L48

Added line #L48 was not covered by tests
KNOWN,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dogacel.kotlinx.protobuf.gen.proto
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import testgen.messages.MessageNoFields
import testgen.messages.MessagesMessage
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -36,7 +37,10 @@ class MessageTest {
assertEquals(message.id, result.id)
assertEquals(message.optionalForeignMessage.c, result.optionalForeignMessage?.c)
assertEquals(message.optionalNestedMessage.a, result.optionalNestedMessage?.a)
assertEquals(message.optionalNestedMessage.corecursive.id, result.optionalNestedMessage?.corecursive?.id)
assertEquals(
message.optionalNestedMessage.corecursive.id,
result.optionalNestedMessage?.corecursive?.id
)
assertEquals(
message.optionalNestedMessage.corecursive.optionalForeignMessage.c,
result.optionalNestedMessage?.corecursive?.optionalForeignMessage?.c
Expand All @@ -60,4 +64,16 @@ class MessageTest {
val deser = messages.Messages.MessagesMessage.parseFrom(ProtoBuf.encodeToByteArray(result))
assertEquals(message, deser)
}

@Test
fun shouldSerNoField() {
val message = messages.MessageNoFieldsOuterClass.MessageNoFields.newBuilder().build()

val messageBytes = message.toByteArray()
val result: MessageNoFields = ProtoBuf.decodeFromByteArray(messageBytes)

val deser =
messages.MessageNoFieldsOuterClass.MessageNoFields.parseFrom(ProtoBuf.encodeToByteArray(result))
assertEquals(message, deser)
}
}
14 changes: 0 additions & 14 deletions testProtos/data_class.proto

This file was deleted.

22 changes: 22 additions & 0 deletions testProtos/message_no_fields.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
syntax = "proto3";

package messages;

message MessageNoFields {
enum Type {
UNKNOWN = 0;
KNOWN = 1;
}
message SubMessageNoFields {
message SubMessageNoFieldsExtend {
Type type = 1;
}
optional SubMessageNoFields subHello = 1;
}

message SubMessageOneofFields {
oneof some_oneof {
int32 some_value = 1;
}
}
}

0 comments on commit 094fe23

Please sign in to comment.