Skip to content

Commit

Permalink
Introduce Opaquer
Browse files Browse the repository at this point in the history
  • Loading branch information
oldergod committed Oct 7, 2023
1 parent 126c5f4 commit 8a4e790
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 13 deletions.
275 changes: 273 additions & 2 deletions wire-compiler/src/test/java/com/squareup/wire/schema/LinkerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@
package com.squareup.wire.schema

import com.squareup.wire.testing.add
import kotlin.test.assertFailsWith
import okio.Path
import okio.Path.Companion.toPath
import okio.fakefilesystem.FakeFileSystem
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class LinkerTest {
private val fs = FakeFileSystem().apply {
if (Path.DIRECTORY_SEPARATOR == "\\") emulateWindows() else emulateUnix()
createDirectories("proto-path".toPath())
}

@Test
Expand Down Expand Up @@ -56,6 +59,272 @@ class LinkerTest {
)
}

@Test fun opaqueMessageDeclaredField() {
fs.add(
"source-path/cafe/cafe.proto",
"""
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
| repeated EspressoShot shots = 2;
|}
|
|message EspressoShot {
| optional Roast roast = 1;
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
""".trimMargin(),
)
val schema = loadAndLinkSchema(opaqueTypes = listOf(ProtoType.get("cafe.EspressoShot")))
assertThat((schema.getType("cafe.CafeDrink") as MessageType).field("shots")!!.type!!)
.isEqualTo(ProtoType.BYTES)
assertThat(schema.protoFile("cafe/cafe.proto")!!.toSchema()).isEqualTo(
"""
|// Proto schema formatted by Wire, do not edit.
|// Source: cafe/cafe.proto
|
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
|
| repeated bytes shots = 2;
|}
|
|message EspressoShot {
| optional Roast roast = 1;
|
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
|
""".trimMargin(),
)
}

@Test fun opaqueEnumDeclaredField() {
fs.add(
"source-path/cafe/cafe.proto",
"""
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
| repeated EspressoShot shots = 2;
|}
|
|message EspressoShot {
| optional Roast roast = 1;
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
""".trimMargin(),
)
val schema = loadAndLinkSchema(opaqueTypes = listOf(ProtoType.get("cafe.Roast")))
assertThat((schema.getType("cafe.EspressoShot") as MessageType).field("roast")!!.type!!)
.isEqualTo(ProtoType.BYTES)
assertThat(schema.protoFile("cafe/cafe.proto")!!.toSchema()).isEqualTo(
"""
|// Proto schema formatted by Wire, do not edit.
|// Source: cafe/cafe.proto
|
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
|
| repeated EspressoShot shots = 2;
|}
|
|message EspressoShot {
| optional bytes roast = 1;
|
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
|
""".trimMargin(),
)
}

@Test fun opaqueExtensionField() {
fs.add(
"source-path/cafe/cafe.proto",
"""
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
|}
|
|extend CafeDrink {
| repeated EspressoShot shots = 2;
|}
|
|message EspressoShot {
| optional Roast roast = 1;
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
""".trimMargin(),
)
val schema = loadAndLinkSchema(opaqueTypes = listOf(ProtoType.get("cafe.EspressoShot")))
assertThat((schema.getType("cafe.CafeDrink") as MessageType).extensionField("cafe.shots")!!.type!!)
.isEqualTo(ProtoType.BYTES)
assertThat(schema.protoFile("cafe/cafe.proto")!!.toSchema()).isEqualTo(
"""
|// Proto schema formatted by Wire, do not edit.
|// Source: cafe/cafe.proto
|
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
|}
|
|message EspressoShot {
| optional Roast roast = 1;
|
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
|
|extend CafeDrink {
| repeated bytes shots = 2;
|}
|
""".trimMargin(),
)
}

@Test fun opaqueMultipleFields() {
fs.add(
"source-path/cafe/cafe.proto",
"""
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
| repeated EspressoShot shots = 2;
|}
|
|message EspressoShot {
| optional Roast roast = 1;
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
""".trimMargin(),
)
val schema = loadAndLinkSchema(
opaqueTypes = listOf(
ProtoType.get("cafe.EspressoShot"),
ProtoType.get("cafe.Roast"),
),
)
assertThat((schema.getType("cafe.CafeDrink") as MessageType).field("shots")!!.type!!)
.isEqualTo(ProtoType.BYTES)
assertThat((schema.getType("cafe.EspressoShot") as MessageType).field("roast")!!.type!!)
.isEqualTo(ProtoType.BYTES)
assertThat(schema.protoFile("cafe/cafe.proto")!!.toSchema()).isEqualTo(
"""
|// Proto schema formatted by Wire, do not edit.
|// Source: cafe/cafe.proto
|
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
|
| repeated bytes shots = 2;
|}
|
|message EspressoShot {
| optional bytes roast = 1;
|
| optional bool decaf = 2;
|}
|
|enum Roast {
| MEDIUM = 1;
| DARK = 2;
|}
|
""".trimMargin(),
)
}

@Test fun opaqueScalarTypeThrows() {
fs.add(
"source-path/cafe/cafe.proto",
"""
|syntax = "proto2";
|
|package cafe;
|
|message CafeDrink {
| optional int32 size_ounces = 1;
|}
""".trimMargin(),
)

val exception = assertFailsWith<SchemaException> {
loadAndLinkSchema(opaqueTypes = listOf(ProtoType.INT32))
}
assertThat(exception).hasMessageContaining(
"""
|Scalar types like int32 cannot be opaqued
| for field size_ounces (source-path/cafe/cafe.proto:6:3)
| in message cafe.CafeDrink (source-path/cafe/cafe.proto:5:1)
""".trimMargin(),
)
}

@Test
fun unusedProtoPathFileExcludedFromSchema() {
fs.add(
Expand Down Expand Up @@ -225,15 +494,17 @@ class LinkerTest {
|}
""".trimMargin(),
)
fs.add("proto-path/b.proto", "")
val schema = loadAndLinkSchema()

val enumValueDeprecated = schema.getField(Options.ENUM_VALUE_OPTIONS, "deprecated")
assertThat(enumValueDeprecated!!.encodeMode).isNotNull()
}

private fun loadAndLinkSchema(): Schema {
private fun loadAndLinkSchema(
opaqueTypes: List<ProtoType> = listOf(),
): Schema {
val loader = SchemaLoader(fs)
loader.opaqueTypes = opaqueTypes
loader.initRoots(
sourcePath = listOf(Location.get("source-path")),
protoPath = listOf(Location.get("proto-path")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,32 @@ class WireRunTest {
.contains("public final class Red extends Message")
}

@Test
fun opaqueBeforeGeneratingKtThenJava() {
writeBlueProto()
writeTriangleProto()

val wireRun = WireRun(
sourcePath = listOf(Location.get("colors/src/main/proto")),
protoPath = listOf(Location.get("polygons/src/main/proto")),
opaqueTypes = listOf("squareup.polygons.Triangle"),
targets = listOf(
KotlinTarget(
outDirectory = "generated/kt",
),
),
)
wireRun.execute(fs, logger)

assertThat(fs.findFiles("generated")).containsExactlyInAnyOrderAsRelativePaths(
"generated/kt/squareup/colors/Blue.kt",
)
assertThat(fs.readUtf8("generated/kt/squareup/colors/Blue.kt"))
.contains("class Blue")
// The type `Triangle` has been opaqued.
.contains("public val triangle: ByteString? = null")
}

@Test
fun noSuchClassEventListener() {
assertThat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.squareup.wire

import com.squareup.wire.schema.Location
import com.squareup.wire.schema.ProtoType
import com.squareup.wire.schema.Schema
import com.squareup.wire.schema.SchemaLoader
import okio.FileSystem
Expand All @@ -29,6 +30,7 @@ class SchemaBuilder {
private val sourcePath: Path = "/sourcePath".toPath()
private val protoPath: Path = "/protoPath".toPath()
private val fileSystem: FileSystem = FakeFileSystem()
private var opaqueTypes = mutableListOf<ProtoType>()

init {
fileSystem.createDirectories(sourcePath)
Expand Down Expand Up @@ -77,8 +79,15 @@ class SchemaBuilder {
return add(name, protoFile, protoPath)
}

/** See [SchemaLoader.opaqueTypes]. */
fun addOpaqueTypes(vararg opaqueTypes: ProtoType): SchemaBuilder {
this.opaqueTypes.addAll(opaqueTypes)
return this
}

fun build(): Schema {
val schemaLoader = SchemaLoader(fileSystem)
schemaLoader.opaqueTypes = opaqueTypes.toList()
schemaLoader.initRoots(
sourcePath = listOf(Location.get(sourcePath.toString())),
protoPath = listOf(Location.get(protoPath.toString())),
Expand Down
Loading

0 comments on commit 8a4e790

Please sign in to comment.