From 4d37a9601291e953858b18f650ec262624e7332f Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Thu, 11 Apr 2024 17:27:23 -0700 Subject: [PATCH 1/8] Adds class template changes for read API generation * Adds import statement for IonReader and IonException * Adds read API for `AbstractDataType::Value`, `AbstractDataType::Sequence` and `AbstractDataType::Structure` --- .../beta/generate/templates/java/class.templ | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/bin/ion/commands/beta/generate/templates/java/class.templ b/src/bin/ion/commands/beta/generate/templates/java/class.templ index 968040f..197a775 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -1,5 +1,7 @@ package {{ namespace }}; import java.util.ArrayList; +import com.amazon.ion.IonReader; +import com.amazon.ion.IonException; public final class {{ target_kind_name }} { {% for field in fields -%} @@ -16,4 +18,69 @@ public final class {{ target_kind_name }} { return this.{{ field.name | camel }}; } {% endfor %} + + public static {{ target_kind_name }} readFrom(IonReader reader) { + {% for field in fields -%} + {{ field.value }} {{ field.name | camel }} = + {% if field.value == "boolean" %} + false + {% elif field.value == "int" or field.value == "double" %} + 0 + {% else %} + null + {% endif %}; + {% endfor %} + {% if abstract_data_type == "Value"%} + value = {% if fields[0].value | is_built_in_type %} + {% if fields[0].value == "bytes[]" %} + reader.newBytes(); + {% else %} + reader.{{ fields[0].value | camel }}Value(); + {% endif %} + {% else %} + {{ fields[0].value }}.readFrom(reader); + {% endif %} + {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} + reader.stepIn(); + while (reader.hasNext()) { + reader.next(); + String fieldName = reader.getFieldName(); + switch(fieldName) { + {% for field in fields %} + case "{{ field.name }}": + {{ field.name | camel }} = {% if field.value | is_built_in_type %} + {% if field.value == "bytes[]" %} + reader.newBytes(); + {% else %} + reader.{{ field.value | camel }}Value(); + {% endif %} + {% else %} + {{ field.value }}.readFrom(reader); + {% endif %} + break; + {% endfor %} + default: + throw new IonException("Can not read field name:" + fieldName + " for {{ target_kind_name }} as it doesn't exist in the given schema type definition."); + } + } + reader.stepOut(); + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + reader.stepIn(); + value = new {{ fields[0].value }}(); + while (reader.hasNext()) { + reader.next(); + {% if abstract_data_type["Sequence"] | is_built_in_type == false %} + value.add({{ abstract_data_type["Sequence"] }}.readFrom(reader)); + {% else %} + {% if abstract_data_type["Sequence"] == "bytes[]" %} + value.add(reader.newBytes()); + {% else %} + value.add(reader.{{ abstract_data_type["Sequence"] | camel }}Value()); + {% endif %} + {% endif %} + } + reader.stepOut(); + {% endif %} + return new {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}); + } } From 595096b1a72d1bd74a133f3d7ccc3a54c0562b8a Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Thu, 11 Apr 2024 17:29:04 -0700 Subject: [PATCH 2/8] Adds tests for read API generation * Adds new tests for read APi generation * Modifies current `is_built_in_type` filter for `JavaLanguage` to change float to double * Modifies test input and schemas to use Ion float values --- .../input/struct_with_fields.ion | 1 + .../test/java/org/example/CodeGenTest.java | 46 +++++++++++++++++-- .../schema/struct_with_fields.isl | 3 +- src/bin/ion/commands/beta/generate/utils.rs | 2 +- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/code-gen-projects/input/struct_with_fields.ion b/code-gen-projects/input/struct_with_fields.ion index c94337a..a429892 100644 --- a/code-gen-projects/input/struct_with_fields.ion +++ b/code-gen-projects/input/struct_with_fields.ion @@ -2,4 +2,5 @@ A: "hello", B: 12, C: ["foo", "bar", "baz"], + D: 10e2 } \ No newline at end of file diff --git a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java index 75951fd..af7e683 100644 --- a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java +++ b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java @@ -3,6 +3,12 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; +import com.amazon.ion.system.IonReaderBuilder; +import com.amazon.ion.IonReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.File; class CodeGenTest { // TODO: Add roundtrip tests after read-write APIs are supported for Java code generation @@ -11,16 +17,46 @@ class CodeGenTest { a.add("foo"); a.add("bar"); a.add("baz"); - StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a)); + StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a), 10e2); assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); assertEquals(12, s.getB(), "s.getB() should return `12`"); assertEquals(3, s.getC().getValue().size(), "s.getC().getValue() should return ArrayList fo size 3"); + assertEquals(10e2, s.getD(), "s.getD() should return `10e2`"); } @Test void getterAndSetterTestForNestedStruct() { - NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); - assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); - assertEquals(12, n.getB(), "n.getB() should return `12`"); - assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); + NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); + assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); + assertEquals(12, n.getB(), "n.getB() should return `12`"); + assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); + } + + @Test void readFromForStructWithFields() throws IOException { + File f = new File("./../../input/struct_with_fields.ion"); + InputStream inputStream = new FileInputStream(f); + + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + StructWithFields s = StructWithFields.readFrom(reader); + assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); + assertEquals(12, s.getB(), "s.getB() should return `12`"); + assertEquals(3, s.getC().getValue().size(), "s.getC().getValue() should return ArrayList fo size 3"); + assertEquals(10e2, s.getD(), "s.getD() should return `10e2`"); + } + } + + @Test void readFromForNestedStruct() throws IOException { + File f = new File("./../../input/nested_struct.ion"); + InputStream inputStream = new FileInputStream(f); + + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + NestedStruct n = NestedStruct.readFrom(reader); + assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); + assertEquals(12, n.getB(), "n.getB() should return `12`"); + assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); } + } } diff --git a/code-gen-projects/schema/struct_with_fields.isl b/code-gen-projects/schema/struct_with_fields.isl index dc70102..f03fbb0 100644 --- a/code-gen-projects/schema/struct_with_fields.isl +++ b/code-gen-projects/schema/struct_with_fields.isl @@ -3,7 +3,8 @@ type::{ fields: { A: string, B: int, - C: { element: string } + C: { element: string }, + D: float, } } diff --git a/src/bin/ion/commands/beta/generate/utils.rs b/src/bin/ion/commands/beta/generate/utils.rs index a1f1b71..4883b4d 100644 --- a/src/bin/ion/commands/beta/generate/utils.rs +++ b/src/bin/ion/commands/beta/generate/utils.rs @@ -88,7 +88,7 @@ impl Language for JavaLanguage { } fn is_built_in_type(name: &str) -> bool { - matches!(name, "int" | "String" | "boolean" | "byte[]" | "float") + matches!(name, "int" | "String" | "boolean" | "byte[]" | "double") } fn template_name(template: &Template) -> String { From f3353491b6f3e585eddbda310563541242bac969 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Mon, 15 Apr 2024 12:04:12 -0700 Subject: [PATCH 3/8] Adds write API generation support * Modifies class template toa dd writer API support * Adds roundtrip tests in CodeGenTest.java --- .../test/java/org/example/CodeGenTest.java | 35 ++++++++++----- .../beta/generate/templates/java/class.templ | 44 +++++++++++++++++++ 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java index af7e683..a060646 100644 --- a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java +++ b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java @@ -5,13 +5,21 @@ import java.util.ArrayList; import com.amazon.ion.system.IonReaderBuilder; import com.amazon.ion.IonReader; +import com.amazon.ion.system.IonTextWriterBuilder; +import com.amazon.ion.IonWriter; +import com.amazon.ion.IonSystem; +import com.amazon.ion.system.IonSystemBuilder; +import com.amazon.ion.IonLoader; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.FileInputStream; import java.io.File; class CodeGenTest { - // TODO: Add roundtrip tests after read-write APIs are supported for Java code generation + private static final IonSystem ionSystem = IonSystemBuilder.standard().build(); + private static final IonLoader ionLoader = ionSystem.getLoader(); + @Test void getterAndSetterTestForStructWithFields() { ArrayList a = new ArrayList(); a.add("foo"); @@ -31,32 +39,35 @@ class CodeGenTest { assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); } - @Test void readFromForStructWithFields() throws IOException { + @Test void roundtripTestForStructWithFields() throws IOException { File f = new File("./../../input/struct_with_fields.ion"); InputStream inputStream = new FileInputStream(f); - + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); try (IonReader reader = readerBuilder.build(inputStream)) { reader.next(); StructWithFields s = StructWithFields.readFrom(reader); - assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); - assertEquals(12, s.getB(), "s.getB() should return `12`"); - assertEquals(3, s.getC().getValue().size(), "s.getC().getValue() should return ArrayList fo size 3"); - assertEquals(10e2, s.getD(), "s.getD() should return `10e2`"); + IonWriter writer = b.build(out); + s.writeTo(writer); + writer.close(); } + assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); } - @Test void readFromForNestedStruct() throws IOException { + @Test void roundtripTestForNestedStruct() throws IOException { File f = new File("./../../input/nested_struct.ion"); InputStream inputStream = new FileInputStream(f); - + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); try (IonReader reader = readerBuilder.build(inputStream)) { reader.next(); NestedStruct n = NestedStruct.readFrom(reader); - assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); - assertEquals(12, n.getB(), "n.getB() should return `12`"); - assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); + IonWriter writer = b.build(out); + n.writeTo(writer); + writer.close(); } + assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); } } diff --git a/src/bin/ion/commands/beta/generate/templates/java/class.templ b/src/bin/ion/commands/beta/generate/templates/java/class.templ index 197a775..7396dae 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -2,6 +2,9 @@ package {{ namespace }}; import java.util.ArrayList; import com.amazon.ion.IonReader; import com.amazon.ion.IonException; +import com.amazon.ion.IonWriter; +import com.amazon.ion.IonType; +import java.io.IOException; public final class {{ target_kind_name }} { {% for field in fields -%} @@ -20,6 +23,7 @@ public final class {{ target_kind_name }} { {% endfor %} public static {{ target_kind_name }} readFrom(IonReader reader) { + {# Intializes all the fields of this class #} {% for field in fields -%} {{ field.value }} {{ field.name | camel }} = {% if field.value == "boolean" %} @@ -31,6 +35,7 @@ public final class {{ target_kind_name }} { {% endif %}; {% endfor %} {% if abstract_data_type == "Value"%} + {# Reads `Value` class with a signle field `value` #} value = {% if fields[0].value | is_built_in_type %} {% if fields[0].value == "bytes[]" %} reader.newBytes(); @@ -41,6 +46,7 @@ public final class {{ target_kind_name }} { {{ fields[0].value }}.readFrom(reader); {% endif %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} + {# Reads `Structure` class with multiple fields based on `field.name` #} reader.stepIn(); while (reader.hasNext()) { reader.next(); @@ -65,8 +71,10 @@ public final class {{ target_kind_name }} { } reader.stepOut(); {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {# Reads `Sequence` class with a single field `value` that is an `ArraList` #} reader.stepIn(); value = new {{ fields[0].value }}(); + {# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #} while (reader.hasNext()) { reader.next(); {% if abstract_data_type["Sequence"] | is_built_in_type == false %} @@ -83,4 +91,40 @@ public final class {{ target_kind_name }} { {% endif %} return new {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}); } + + public void writeTo(IonWriter writer) throws IOException { + {% if abstract_data_type == "Value" %} + {# Writes `Value` class with a signle field `value` as an Ion value #} + {% for field in fields %} + {% if field.value | is_built_in_type == false %} + this.{{ field.name | camel }}.writeTo(writer)?; + {% else %} + writer.write{% if field.isl_type_name == "symbol" %}Symbol{% else %}{{ field.isl_type_name | upper_camel }}{% endif %}(this.value); + {% endif %} + {% endfor %} + {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} + {# Writes `Structure` class with multiple fields based on `field.name` as an Ion struct #} + writer.stepIn(IonType.STRUCT); + {% for field in fields %} + writer.setFieldName("{{ field.name }}"); + {% if field.value | is_built_in_type == false %} + this.{{ field.name | camel }}.writeTo(writer); + {% else %} + writer.write{% if field.isl_type_name == "symbol" %}Symbol{% else %}{{ field.isl_type_name | upper_camel }}{% endif %}(this.{{ field.name | camel }}); + {% endif %} + {% endfor %} + writer.stepOut(); + {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} + {# Writes `Sequence` class with a single field `value` that is an `ArraList` as an Ion sequence #} + writer.stepIn(IonType.LIST); + for ({{ abstract_data_type["Sequence"] }} value: this.value) { + {% if abstract_data_type["Sequence"] | is_built_in_type == false %} + value.writeTo(writer); + {% else %} + writer.write{% if fields[0].isl_type_name == "symbol" %}Symbol{% else %}{{ abstract_data_type["Sequence"] | upper_camel }}{% endif %}(value); + {% endif %} + } + writer.stepOut(); + {% endif %} + } } From 9f4171b0fa4367967feae0c9041d955a0a7f0b7f Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Thu, 18 Apr 2024 15:58:43 -0700 Subject: [PATCH 4/8] Adds suggested changes * Modified indentation for template files * Adds doc comment * Minor typo fixes --- .../beta/generate/templates/java/class.templ | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/bin/ion/commands/beta/generate/templates/java/class.templ b/src/bin/ion/commands/beta/generate/templates/java/class.templ index 7396dae..a13330b 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -22,10 +22,17 @@ public final class {{ target_kind_name }} { } {% endfor %} + + /** + * Reads a {{ target_kind_name }} from an {@link IonReader}. + * + * This method does not advance the reader at the current level. + * The caller is responsible for positioning the reader on the value to read. + */ public static {{ target_kind_name }} readFrom(IonReader reader) { {# Intializes all the fields of this class #} {% for field in fields -%} - {{ field.value }} {{ field.name | camel }} = + {{ field.value }} {{ field.name | camel }} = {% if field.value == "boolean" %} false {% elif field.value == "int" or field.value == "double" %} @@ -35,12 +42,12 @@ public final class {{ target_kind_name }} { {% endif %}; {% endfor %} {% if abstract_data_type == "Value"%} - {# Reads `Value` class with a signle field `value` #} + {# Reads `Value` class with a single field `value` #} value = {% if fields[0].value | is_built_in_type %} {% if fields[0].value == "bytes[]" %} - reader.newBytes(); + reader.newBytes(); {% else %} - reader.{{ fields[0].value | camel }}Value(); + reader.{{ fields[0].value | camel }}Value(); {% endif %} {% else %} {{ fields[0].value }}.readFrom(reader); @@ -56,12 +63,12 @@ public final class {{ target_kind_name }} { case "{{ field.name }}": {{ field.name | camel }} = {% if field.value | is_built_in_type %} {% if field.value == "bytes[]" %} - reader.newBytes(); + reader.newBytes(); {% else %} reader.{{ field.value | camel }}Value(); {% endif %} {% else %} - {{ field.value }}.readFrom(reader); + {{ field.value }}.readFrom(reader); {% endif %} break; {% endfor %} @@ -71,7 +78,7 @@ public final class {{ target_kind_name }} { } reader.stepOut(); {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} - {# Reads `Sequence` class with a single field `value` that is an `ArraList` #} + {# Reads `Sequence` class with a single field `value` that is an `ArrayList` #} reader.stepIn(); value = new {{ fields[0].value }}(); {# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #} @@ -92,36 +99,43 @@ public final class {{ target_kind_name }} { return new {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}); } + + /** + * Writes a {{ target_kind_name }} as Ion from an {@link IonWriter}. + * + * This method does not close the writer after writing is complete. + * The caller is responsible for closing the stream associated with the writer. + */ public void writeTo(IonWriter writer) throws IOException { {% if abstract_data_type == "Value" %} - {# Writes `Value` class with a signle field `value` as an Ion value #} + {# Writes `Value` class with a single field `value` as an Ion value #} {% for field in fields %} {% if field.value | is_built_in_type == false %} this.{{ field.name | camel }}.writeTo(writer)?; {% else %} - writer.write{% if field.isl_type_name == "symbol" %}Symbol{% else %}{{ field.isl_type_name | upper_camel }}{% endif %}(this.value); + writer.write{{ field.isl_type_name | upper_camel }}(this.value); {% endif %} {% endfor %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} - {# Writes `Structure` class with multiple fields based on `field.name` as an Ion struct #} + {# Writes `Structure` class with multiple fields based on `field.name` as an Ion struct #} writer.stepIn(IonType.STRUCT); {% for field in fields %} - writer.setFieldName("{{ field.name }}"); + writer.setFieldName("{{ field.name }}"); {% if field.value | is_built_in_type == false %} this.{{ field.name | camel }}.writeTo(writer); {% else %} - writer.write{% if field.isl_type_name == "symbol" %}Symbol{% else %}{{ field.isl_type_name | upper_camel }}{% endif %}(this.{{ field.name | camel }}); + writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }}); {% endif %} {% endfor %} writer.stepOut(); {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} - {# Writes `Sequence` class with a single field `value` that is an `ArraList` as an Ion sequence #} + {# Writes `Sequence` class with a single field `value` that is an `ArrayList` as an Ion sequence #} writer.stepIn(IonType.LIST); for ({{ abstract_data_type["Sequence"] }} value: this.value) { {% if abstract_data_type["Sequence"] | is_built_in_type == false %} - value.writeTo(writer); + value.writeTo(writer); {% else %} - writer.write{% if fields[0].isl_type_name == "symbol" %}Symbol{% else %}{{ abstract_data_type["Sequence"] | upper_camel }}{% endif %}(value); + writer.write{{ abstract_data_type["Sequence"] | upper_camel }}(value); {% endif %} } writer.stepOut(); From 475bebcca8f8ac2ba73a7894e5765e02c9cd3d8f Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Thu, 18 Apr 2024 17:22:38 -0700 Subject: [PATCH 5/8] Add setter support for Java classes --- .../test/java/org/example/CodeGenTest.java | 21 +++++++++++++++++++ .../beta/generate/templates/java/class.templ | 7 ++++++- tests/cli.rs | 10 ++++----- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java index a060646..054d744 100644 --- a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java +++ b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java @@ -26,17 +26,38 @@ class CodeGenTest { a.add("bar"); a.add("baz"); StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a), 10e2); + + // getter tests for `StructWithFields` assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); assertEquals(12, s.getB(), "s.getB() should return `12`"); assertEquals(3, s.getC().getValue().size(), "s.getC().getValue() should return ArrayList fo size 3"); assertEquals(10e2, s.getD(), "s.getD() should return `10e2`"); + + // setter tests for `StructWithFields` + s.setA("hi"); + assertEquals("hi", s.getA(), "s.getA() should return \"hi\""); + s.setB(6); + assertEquals(6, s.getB(), "s.getB() should return `6`"); + s.setC(new AnonymousType2(new ArrayList())); + assertEquals(true, s.getC().getValue().isEmpty(), "s.getC().isEmpty() should return `true`"); + s.setD(11e3); + assertEquals(11e3 ,s.getD(), "s.getD() should return `11e3`"); } @Test void getterAndSetterTestForNestedStruct() { + // getter tests for `NestedStruct` NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); assertEquals(12, n.getB(), "n.getB() should return `12`"); assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); + + // setter tests for `NestedStruct` + n.setA("hi"); + assertEquals("hi", n.getA(), "s.getA() should return \"hi\""); + n.setB(6); + assertEquals(6, n.getB(), "s.getB() should return `6`"); + n.getC().setD(true); + assertEquals(true, n.getC().getD(), "s.getC().getD() should return `true`"); } @Test void roundtripTestForStructWithFields() throws IOException { diff --git a/src/bin/ion/commands/beta/generate/templates/java/class.templ b/src/bin/ion/commands/beta/generate/templates/java/class.templ index a13330b..2216b43 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -8,7 +8,7 @@ import java.io.IOException; public final class {{ target_kind_name }} { {% for field in fields -%} - private final {{ field.value }} {{ field.name | camel }}; + private {{ field.value }} {{ field.name | camel }}; {% endfor %} public {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.value }} {{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}) { @@ -22,6 +22,11 @@ public final class {{ target_kind_name }} { } {% endfor %} + {% for field in fields -%}public {{ field.value }} set{% filter upper_camel %}{{ field.name }}{% endfilter %}({{ field.value }} {{ field.name | camel }}) { + return this.{{ field.name | camel }} = {{ field.name | camel }}; + } + {% endfor %} + /** * Reads a {{ target_kind_name }} from an {@link IonReader}. diff --git a/tests/cli.rs b/tests/cli.rs index df51c9a..1beb817 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -336,7 +336,7 @@ fn test_code_generation_in_rust( } } "#, - &["private final int id;", "private final String name;"], + &["private int id;", "private String name;"], &["public String getName() {", "public int getId() {"] )] #[case( @@ -347,7 +347,7 @@ fn test_code_generation_in_rust( type: int // this will be a field in struct } "#, - &["private final int value;"], + &["private int value;"], &["public int getValue() {"] )] #[case( @@ -358,7 +358,7 @@ fn test_code_generation_in_rust( element: string // this will be a sequence field in struct } "#, - &["private final ArrayList value;"], + &["private ArrayList value;"], &["public ArrayList getValue() {"] )] #[case( @@ -376,7 +376,7 @@ fn test_code_generation_in_rust( type: int } "#, - &["private final OtherType reference;"], + &["private OtherType reference;"], &["public OtherType getReference() {"] )] #[case( @@ -389,7 +389,7 @@ fn test_code_generation_in_rust( } } "#, - &["private final AnonymousType1 anonymousType;"], + &["private AnonymousType1 anonymousType;"], &["public AnonymousType1 getAnonymousType() {"] )] /// Calls ion-cli beta generate with different schema file. Pass the test if the return value contains the expected properties and accessors. From 98db61508fa7795cae27a3d89304742016be4f37 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Fri, 19 Apr 2024 15:49:46 -0700 Subject: [PATCH 6/8] Renamed `value` property of field to `value_type` --- .../rust/code-gen-demo/src/lib.rs | 44 ++++++++++++++----- .../ion/commands/beta/generate/generator.rs | 2 +- .../beta/generate/templates/java/class.templ | 36 +++++++-------- .../beta/generate/templates/rust/struct.templ | 26 +++++------ src/bin/ion/commands/beta/generate/utils.rs | 2 +- 5 files changed, 65 insertions(+), 45 deletions(-) diff --git a/code-gen-projects/rust/code-gen-demo/src/lib.rs b/code-gen-projects/rust/code-gen-demo/src/lib.rs index 5e50879..8741095 100644 --- a/code-gen-projects/rust/code-gen-demo/src/lib.rs +++ b/code-gen-projects/rust/code-gen-demo/src/lib.rs @@ -10,6 +10,8 @@ mod tests { use ion_rs::ReaderBuilder; use ion_rs::TextWriterBuilder; use std::fs; + use test_generator::test_resources; + include!(concat!(env!("OUT_DIR"), "/ion_generated_code.rs")); #[test] @@ -18,12 +20,9 @@ mod tests { assert_eq!(result, 4); } - #[test] - fn test_roundtrip_generated_code_structs_with_fields() -> IonResult<()> { - let ion_string = fs::read_to_string(&format!( - "{}/../../input/struct_with_fields.ion", - env!("CARGO_MANIFEST_DIR") - ))?; + #[test_resources("../../input/good/struct_with_fields/**/*.ion")] + fn roundtrip_good_test_generated_code_structs_with_fields(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; let mut reader = ReaderBuilder::new().build(ion_string.clone())?; let mut buffer = Vec::new(); let mut text_writer = TextWriterBuilder::default().build(&mut buffer)?; @@ -42,12 +41,21 @@ mod tests { Ok(()) } - #[test] - fn test_roundtrip_generated_code_nested_structs() -> IonResult<()> { - let ion_string = fs::read_to_string(&format!( - "{}/../../input/nested_struct.ion", - env!("CARGO_MANIFEST_DIR") - ))?; + #[test_resources("../../input/bad/struct_with_fields/**/*.ion")] + fn roundtrip_bad_test_generated_code_structs_with_fields(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; + let mut reader = ReaderBuilder::new().build(ion_string.clone())?; + // read given Ion value using Ion reader + reader.next()?; + let result = StructWithFields::read_from(&mut reader); + assert!(result.is_err()); + + Ok(()) + } + + #[test_resources("../../input/good/nested_struct/**/*.ion")] + fn roundtrip_good_test_generated_code_nested_structs(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; let mut reader = ReaderBuilder::new().build(ion_string.clone())?; let mut buffer = Vec::new(); let mut text_writer = TextWriterBuilder::default().build(&mut buffer)?; @@ -65,4 +73,16 @@ mod tests { Ok(()) } + + #[test_resources("../../input/bad/nested_struct/**/*.ion")] + fn roundtrip_bad_test_generated_code_nested_structs(file_name: &str) -> IonResult<()> { + let ion_string = fs::read_to_string(file_name)?; + let mut reader = ReaderBuilder::new().build(ion_string.clone())?; + // read given Ion value using Ion reader + reader.next()?; + let result = NestedStruct::read_from(&mut reader); + assert!(result.is_err()); + + Ok(()) + } } diff --git a/src/bin/ion/commands/beta/generate/generator.rs b/src/bin/ion/commands/beta/generate/generator.rs index 9e64bb2..6695be4 100644 --- a/src/bin/ion/commands/beta/generate/generator.rs +++ b/src/bin/ion/commands/beta/generate/generator.rs @@ -364,7 +364,7 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> { ) -> CodeGenResult<()> { tera_fields.push(Field { name: field_name.to_string(), - value: abstract_data_type_name, + value_type: abstract_data_type_name, isl_type_name, }); Ok(()) diff --git a/src/bin/ion/commands/beta/generate/templates/java/class.templ b/src/bin/ion/commands/beta/generate/templates/java/class.templ index 2216b43..4a457c0 100644 --- a/src/bin/ion/commands/beta/generate/templates/java/class.templ +++ b/src/bin/ion/commands/beta/generate/templates/java/class.templ @@ -8,21 +8,21 @@ import java.io.IOException; public final class {{ target_kind_name }} { {% for field in fields -%} - private {{ field.value }} {{ field.name | camel }}; + private {{ field.value_type }} {{ field.name | camel }}; {% endfor %} - public {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.value }} {{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}) { + public {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.value_type }} {{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}) { {% for field in fields -%} this.{{ field.name | camel }} = {{ field.name | camel }}; {% endfor %} } - {% for field in fields -%}public {{ field.value }} get{% filter upper_camel %}{{ field.name }}{% endfilter %}() { + {% for field in fields -%}public {{ field.value_type }} get{% filter upper_camel %}{{ field.name }}{% endfilter %}() { return this.{{ field.name | camel }}; } {% endfor %} - {% for field in fields -%}public {{ field.value }} set{% filter upper_camel %}{{ field.name }}{% endfilter %}({{ field.value }} {{ field.name | camel }}) { + {% for field in fields -%}public {{ field.value_type }} set{% filter upper_camel %}{{ field.name }}{% endfilter %}({{ field.value_type }} {{ field.name | camel }}) { return this.{{ field.name | camel }} = {{ field.name | camel }}; } {% endfor %} @@ -37,10 +37,10 @@ public final class {{ target_kind_name }} { public static {{ target_kind_name }} readFrom(IonReader reader) { {# Intializes all the fields of this class #} {% for field in fields -%} - {{ field.value }} {{ field.name | camel }} = - {% if field.value == "boolean" %} + {{ field.value_type }} {{ field.name | camel }} = + {% if field.value_type == "boolean" %} false - {% elif field.value == "int" or field.value == "double" %} + {% elif field.value_type == "int" or field.value_type == "double" %} 0 {% else %} null @@ -48,14 +48,14 @@ public final class {{ target_kind_name }} { {% endfor %} {% if abstract_data_type == "Value"%} {# Reads `Value` class with a single field `value` #} - value = {% if fields[0].value | is_built_in_type %} - {% if fields[0].value == "bytes[]" %} + value = {% if fields[0].value_type | is_built_in_type %} + {% if fields[0].value_type == "bytes[]" %} reader.newBytes(); {% else %} - reader.{{ fields[0].value | camel }}Value(); + reader.{{ fields[0].value_type | camel }}Value(); {% endif %} {% else %} - {{ fields[0].value }}.readFrom(reader); + {{ fields[0].value_type }}.readFrom(reader); {% endif %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} {# Reads `Structure` class with multiple fields based on `field.name` #} @@ -66,14 +66,14 @@ public final class {{ target_kind_name }} { switch(fieldName) { {% for field in fields %} case "{{ field.name }}": - {{ field.name | camel }} = {% if field.value | is_built_in_type %} - {% if field.value == "bytes[]" %} + {{ field.name | camel }} = {% if field.value_type | is_built_in_type %} + {% if field.value_type == "bytes[]" %} reader.newBytes(); {% else %} - reader.{{ field.value | camel }}Value(); + reader.{{ field.value_type | camel }}Value(); {% endif %} {% else %} - {{ field.value }}.readFrom(reader); + {{ field.value_type }}.readFrom(reader); {% endif %} break; {% endfor %} @@ -85,7 +85,7 @@ public final class {{ target_kind_name }} { {% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} {# Reads `Sequence` class with a single field `value` that is an `ArrayList` #} reader.stepIn(); - value = new {{ fields[0].value }}(); + value = new {{ fields[0].value_type }}(); {# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #} while (reader.hasNext()) { reader.next(); @@ -115,7 +115,7 @@ public final class {{ target_kind_name }} { {% if abstract_data_type == "Value" %} {# Writes `Value` class with a single field `value` as an Ion value #} {% for field in fields %} - {% if field.value | is_built_in_type == false %} + {% if field.value_type | is_built_in_type == false %} this.{{ field.name | camel }}.writeTo(writer)?; {% else %} writer.write{{ field.isl_type_name | upper_camel }}(this.value); @@ -126,7 +126,7 @@ public final class {{ target_kind_name }} { writer.stepIn(IonType.STRUCT); {% for field in fields %} writer.setFieldName("{{ field.name }}"); - {% if field.value | is_built_in_type == false %} + {% if field.value_type | is_built_in_type == false %} this.{{ field.name | camel }}.writeTo(writer); {% else %} writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }}); diff --git a/src/bin/ion/commands/beta/generate/templates/rust/struct.templ b/src/bin/ion/commands/beta/generate/templates/rust/struct.templ index e331076..6e1e265 100644 --- a/src/bin/ion/commands/beta/generate/templates/rust/struct.templ +++ b/src/bin/ion/commands/beta/generate/templates/rust/struct.templ @@ -1,12 +1,12 @@ #[derive(Debug, Clone, Default)] pub struct {{ target_kind_name }} { {% for field in fields -%} - {{ field.name | snake | indent(first = true) }}: {{ field.value }}, + {{ field.name | snake | indent(first = true) }}: {{ field.value_type }}, {% endfor %} } impl {{ target_kind_name }} { - pub fn new({% for field in fields | sort(attribute="name") -%}{{ field.name | snake }}: {{ field.value }},{% endfor %}) -> Self { + pub fn new({% for field in fields | sort(attribute="name") -%}{{ field.name | snake }}: {{ field.value_type }},{% endfor %}) -> Self { Self { {% for field in fields -%} {{ field.name | snake }}, @@ -15,7 +15,7 @@ impl {{ target_kind_name }} { } - {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value }} { + {% for field in fields -%}pub fn {{ field.name | snake }}(&self) -> &{{ field.value_type }} { &self.{{ field.name | snake }} } {% endfor %} @@ -24,10 +24,10 @@ impl {{ target_kind_name }} { pub fn read_from(reader: &mut Reader) -> IonResult { let mut abstract_data_type = {{ target_kind_name }}::default(); {% if abstract_data_type == "Value"%} - abstract_data_type.value = {% if fields[0].value | is_built_in_type == false %} - {{ fields[0].value }}::read_from(reader)?; + abstract_data_type.value = {% if fields[0].value_type | is_built_in_type == false %} + {{ fields[0].value_type }}::read_from(reader)?; {% else %} - reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ fields[0].value | replace(from="string", to ="str") }}()?{% endif %}{% if fields[0].value | lower == "string" %} .to_string() {% endif %}; + reader.read_{% if fields[0].isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ fields[0].value_type | replace(from="string", to ="str") }}()?{% endif %}{% if fields[0].value_type | lower == "string" %} .to_string() {% endif %}; {% endif %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} reader.step_in()?; @@ -35,10 +35,10 @@ impl {{ target_kind_name }} { if let Some(field_name) = reader.field_name()?.text() { match field_name { {% for field in fields -%} - {% if field.value | is_built_in_type == false %} - "{{ field.name }}" => { abstract_data_type.{{ field.name | snake }} = {{ field.value }}::read_from(reader)?; } + {% if field.value_type | is_built_in_type == false %} + "{{ field.name }}" => { abstract_data_type.{{ field.name | snake }} = {{ field.value_type }}::read_from(reader)?; } {% else %} - "{{ field.name }}" => { abstract_data_type.{{ field.name | snake}} = reader.read_{% if field.isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ field.value | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value | lower== "string" %} .to_string() {% endif %}; } + "{{ field.name }}" => { abstract_data_type.{{ field.name | snake}} = reader.read_{% if field.isl_type_name == "symbol" %}symbol()?.text().unwrap(){% else %}{{ field.value_type | lower | replace(from="string", to ="str") }}()?{% endif %}{% if field.value_type | lower== "string" %} .to_string() {% endif %}; } {% endif %} {% endfor %} _ => { @@ -76,21 +76,21 @@ impl {{ target_kind_name }} { pub fn write_to(&self, writer: &mut W) -> IonResult<()> { {% if abstract_data_type == "Value" %} {% for field in fields %} - {% if field.value | is_built_in_type == false %} + {% if field.value_type | is_built_in_type == false %} self.{{ field.name | snake }}.write_to(writer)?; {% else %} - writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value | lower }}{% endif %}(self.value)?; + writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value_type | lower }}{% endif %}(self.value)?; {% endif %} {% endfor %} {% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} writer.step_in(IonType::Struct)?; {% for field in fields %} writer.set_field_name("{{ field.name }}"); - {% if field.value | is_built_in_type == false %} + {% if field.value_type | is_built_in_type == false %} self.{{ field.name | snake }}.write_to(writer)?; {% else %} {# TODO: Change the following `to_owned` to only be used when writing i64,f32,f64,bool which require owned value as input #} - writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; + writer.write_{% if field.isl_type_name == "symbol" %}symbol{% else %}{{ field.value_type | lower }}{% endif %}(self.{{ field.name | snake }}.to_owned())?; {% endif %} {% endfor %} writer.step_out()?; diff --git a/src/bin/ion/commands/beta/generate/utils.rs b/src/bin/ion/commands/beta/generate/utils.rs index 4883b4d..d66594c 100644 --- a/src/bin/ion/commands/beta/generate/utils.rs +++ b/src/bin/ion/commands/beta/generate/utils.rs @@ -9,7 +9,7 @@ use std::fmt::{Display, Formatter}; #[derive(Serialize)] pub struct Field { pub(crate) name: String, - pub(crate) value: String, + pub(crate) value_type: String, pub(crate) isl_type_name: String, } From 4c4de58ca8b564eb39d33b84b40d12cee62b9181 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Tue, 23 Apr 2024 14:43:09 -0700 Subject: [PATCH 7/8] Adds valid-invalid inputs for roundtrip tests * Changes Rust and Java demo project tests to run throughbad/invalid input Ion files and assert Exception is thrown * Changes Rust and java dmeo projects tests to run through each good/valid input Ion files and assert equivelnce for roundtrip test * Adds valid and invalid inpu Ion files for tests --- .../bad/nested_struct/mismatched_type.ion | 8 ++ .../mismatched_sequence_element.ion | 8 ++ .../struct_with_fields/mismatched_type.ion | 8 ++ .../input/good/nested_struct/empty_values.ion | 8 ++ .../nested_struct/valid_fields.ion} | 3 +- .../nested_struct/valid_unordered_fields.ion | 9 ++ .../good/struct_with_fields/empty_values.ion | 7 ++ .../struct_with_fields/valid_fields.ion} | 3 +- .../valid_unordered_fields.ion | 8 ++ .../java/code-gen-demo/build.gradle.kts | 4 + .../test/java/org/example/CodeGenTest.java | 93 +++++++++++++----- .../rust/code-gen-demo/Cargo.lock | 96 +++++++++++++++---- .../rust/code-gen-demo/Cargo.toml | 5 +- 13 files changed, 209 insertions(+), 51 deletions(-) create mode 100644 code-gen-projects/input/bad/nested_struct/mismatched_type.ion create mode 100644 code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion create mode 100644 code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion create mode 100644 code-gen-projects/input/good/nested_struct/empty_values.ion rename code-gen-projects/input/{nested_struct.ion => good/nested_struct/valid_fields.ion} (52%) create mode 100644 code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion create mode 100644 code-gen-projects/input/good/struct_with_fields/empty_values.ion rename code-gen-projects/input/{struct_with_fields.ion => good/struct_with_fields/valid_fields.ion} (59%) create mode 100644 code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion diff --git a/code-gen-projects/input/bad/nested_struct/mismatched_type.ion b/code-gen-projects/input/bad/nested_struct/mismatched_type.ion new file mode 100644 index 0000000..e8c48db --- /dev/null +++ b/code-gen-projects/input/bad/nested_struct/mismatched_type.ion @@ -0,0 +1,8 @@ +// nested struct with type mismatched fields +{ + A: "hello", + B: 12, + C: { + D: 1e0, // expected type: bool + } +} diff --git a/code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion b/code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion new file mode 100644 index 0000000..9d60dd2 --- /dev/null +++ b/code-gen-projects/input/bad/struct_with_fields/mismatched_sequence_element.ion @@ -0,0 +1,8 @@ +// struct with mismatched sequence element +{ + A: "hello", + B: 12, + C: [1, 2, 3], + D: 10e2 +} + diff --git a/code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion b/code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion new file mode 100644 index 0000000..675df97 --- /dev/null +++ b/code-gen-projects/input/bad/struct_with_fields/mismatched_type.ion @@ -0,0 +1,8 @@ +// simple struct with type mismatched fields +{ + A: "hello", + B: false, // expected field type: int + C: ["foo", "bar", "baz"], + D: 10e2 +} + diff --git a/code-gen-projects/input/good/nested_struct/empty_values.ion b/code-gen-projects/input/good/nested_struct/empty_values.ion new file mode 100644 index 0000000..545e6b2 --- /dev/null +++ b/code-gen-projects/input/good/nested_struct/empty_values.ion @@ -0,0 +1,8 @@ +// nested struct with empty string and zeros +{ + C: { + D: false, + }, + A: "", + B: 0, +} diff --git a/code-gen-projects/input/nested_struct.ion b/code-gen-projects/input/good/nested_struct/valid_fields.ion similarity index 52% rename from code-gen-projects/input/nested_struct.ion rename to code-gen-projects/input/good/nested_struct/valid_fields.ion index e0b35eb..391c8cd 100644 --- a/code-gen-projects/input/nested_struct.ion +++ b/code-gen-projects/input/good/nested_struct/valid_fields.ion @@ -1,7 +1,8 @@ +// nested struct with all valid fields { A: "hello", B: 12, C: { D: false, } -} \ No newline at end of file +} diff --git a/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion b/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion new file mode 100644 index 0000000..73f2774 --- /dev/null +++ b/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion @@ -0,0 +1,9 @@ +// nested struct with unordered fields +{ + A: "hello", + B: 12, + C: { + D: false, + } +} + diff --git a/code-gen-projects/input/good/struct_with_fields/empty_values.ion b/code-gen-projects/input/good/struct_with_fields/empty_values.ion new file mode 100644 index 0000000..67986dc --- /dev/null +++ b/code-gen-projects/input/good/struct_with_fields/empty_values.ion @@ -0,0 +1,7 @@ +// struct with empty list, empty string and zeros +{ + C: [], + A: "", + B: 0, + D: 0e0, +} diff --git a/code-gen-projects/input/struct_with_fields.ion b/code-gen-projects/input/good/struct_with_fields/valid_fields.ion similarity index 59% rename from code-gen-projects/input/struct_with_fields.ion rename to code-gen-projects/input/good/struct_with_fields/valid_fields.ion index a429892..f306089 100644 --- a/code-gen-projects/input/struct_with_fields.ion +++ b/code-gen-projects/input/good/struct_with_fields/valid_fields.ion @@ -1,6 +1,7 @@ +// simple struct with all valid fields { A: "hello", B: 12, C: ["foo", "bar", "baz"], D: 10e2 -} \ No newline at end of file +} diff --git a/code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion b/code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion new file mode 100644 index 0000000..80b9a4e --- /dev/null +++ b/code-gen-projects/input/good/struct_with_fields/valid_unordered_fields.ion @@ -0,0 +1,8 @@ + +// struct with unordered fields +{ + C: ["foo", "bar", "baz"], + A: "hello", + B: 12, + D: 10e2, +} diff --git a/code-gen-projects/java/code-gen-demo/build.gradle.kts b/code-gen-projects/java/code-gen-demo/build.gradle.kts index fb52d99..cfb1817 100644 --- a/code-gen-projects/java/code-gen-demo/build.gradle.kts +++ b/code-gen-projects/java/code-gen-demo/build.gradle.kts @@ -65,4 +65,8 @@ tasks { tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() + testLogging { + showStandardStreams = true + events("skipped", "failed") + } } diff --git a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java index 054d744..3e093bc 100644 --- a/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java +++ b/code-gen-projects/java/code-gen-demo/src/test/java/org/example/CodeGenTest.java @@ -10,6 +10,7 @@ import com.amazon.ion.IonSystem; import com.amazon.ion.system.IonSystemBuilder; import com.amazon.ion.IonLoader; +import com.amazon.ion.IonException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -60,35 +61,75 @@ class CodeGenTest { assertEquals(true, n.getC().getD(), "s.getC().getD() should return `true`"); } - @Test void roundtripTestForStructWithFields() throws IOException { - File f = new File("./../../input/struct_with_fields.ion"); - InputStream inputStream = new FileInputStream(f); - IonTextWriterBuilder b = IonTextWriterBuilder.standard(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); - try (IonReader reader = readerBuilder.build(inputStream)) { - reader.next(); - StructWithFields s = StructWithFields.readFrom(reader); - IonWriter writer = b.build(out); - s.writeTo(writer); - writer.close(); + @Test void roundtripGoodTestForStructWithFields() throws IOException { + File dir = new File("./../../input/good/struct_with_fields"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + StructWithFields s = StructWithFields.readFrom(reader); + IonWriter writer = b.build(out); + s.writeTo(writer); + writer.close(); + assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); + } } - assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); } - @Test void roundtripTestForNestedStruct() throws IOException { - File f = new File("./../../input/nested_struct.ion"); - InputStream inputStream = new FileInputStream(f); - IonTextWriterBuilder b = IonTextWriterBuilder.standard(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); - try (IonReader reader = readerBuilder.build(inputStream)) { - reader.next(); - NestedStruct n = NestedStruct.readFrom(reader); - IonWriter writer = b.build(out); - n.writeTo(writer); - writer.close(); + @Test void roundtripBadTestForStructWithFields() throws IOException { + File dir = new File("./../../input/bad/struct_with_fields"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + assertThrows(Throwable.class, () -> { StructWithFields s = StructWithFields.readFrom(reader); }); + } + } + } + + @Test void roundtripGoodTestForNestedStruct() throws IOException { + File dir = new File("./../../input/good/nested_struct"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + NestedStruct n = NestedStruct.readFrom(reader); + IonWriter writer = b.build(out); + n.writeTo(writer); + writer.close(); + assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); + } + } + } + + @Test void roundtripBadTestForNestedStruct() throws IOException { + File dir = new File("./../../input/bad/nested_struct"); + String[] fileNames = dir.list(); + for (String fileName : fileNames) { + File f = new File(dir, fileName); + InputStream inputStream = new FileInputStream(f); + IonTextWriterBuilder b = IonTextWriterBuilder.standard(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); + try (IonReader reader = readerBuilder.build(inputStream)) { + reader.next(); + assertThrows(Throwable.class, () -> { NestedStruct n = NestedStruct.readFrom(reader); }); + } } - assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); } } diff --git a/code-gen-projects/rust/code-gen-demo/Cargo.lock b/code-gen-projects/rust/code-gen-demo/Cargo.lock index 8b0484b..e4a1fb7 100644 --- a/code-gen-projects/rust/code-gen-demo/Cargo.lock +++ b/code-gen-projects/rust/code-gen-demo/Cargo.lock @@ -95,6 +95,7 @@ name = "code-gen-demo" version = "0.1.0" dependencies = [ "ion-rs", + "test-generator", ] [[package]] @@ -121,8 +122,8 @@ checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "strsim", "syn 2.0.55", ] @@ -134,7 +135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", - "quote", + "quote 1.0.35", "syn 2.0.55", ] @@ -144,8 +145,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 1.0.109", ] @@ -165,6 +166,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.12.3" @@ -344,6 +351,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -353,13 +369,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.79", ] [[package]] @@ -383,8 +408,8 @@ version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", ] @@ -422,8 +447,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", ] @@ -439,14 +464,25 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid", +] + [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "unicode-ident", ] @@ -456,11 +492,23 @@ version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "unicode-ident", ] +[[package]] +name = "test-generator" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b23be2add79223226e1cb6446cb3e37506a5927089870687a0f1149bb7a073a" +dependencies = [ + "glob", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "thiserror" version = "1.0.58" @@ -476,8 +524,8 @@ version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", ] @@ -518,6 +566,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -537,8 +591,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", "wasm-bindgen-shared", ] @@ -549,7 +603,7 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote", + "quote 1.0.35", "wasm-bindgen-macro-support", ] @@ -559,8 +613,8 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.79", + "quote 1.0.35", "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", diff --git a/code-gen-projects/rust/code-gen-demo/Cargo.toml b/code-gen-projects/rust/code-gen-demo/Cargo.toml index b93b5d2..22741f2 100644 --- a/code-gen-projects/rust/code-gen-demo/Cargo.toml +++ b/code-gen-projects/rust/code-gen-demo/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -ion-rs = {version = "1.0.0-rc.2", features = ["experimental"]} \ No newline at end of file +[dev-dependencies] +ion-rs = { version = "1.0.0-rc.2", features = ["experimental"] } +test-generator = "0.3" \ No newline at end of file From ba4e440bf1e8512dfa0a31838e2a0a043de94cca Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Thu, 25 Apr 2024 15:17:10 -0700 Subject: [PATCH 8/8] Modifies unordered field test case --- .../input/good/nested_struct/valid_unordered_fields.ion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion b/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion index 73f2774..da21667 100644 --- a/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion +++ b/code-gen-projects/input/good/nested_struct/valid_unordered_fields.ion @@ -1,7 +1,7 @@ // nested struct with unordered fields { - A: "hello", B: 12, + A: "hello", C: { D: false, }