From 44dc982a910112e2e98dcd7656bb31099d319aa7 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Tue, 24 Dec 2024 12:08:27 +0800 Subject: [PATCH 1/4] generate long for BigDecimal --- .../http/client/generator/core/util/ModelTestCaseUtil.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java index 02edce96138..5928f6cd53a 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java @@ -98,6 +98,8 @@ public static Object jsonFromType(int depth, IType type) { return RANDOM.nextInt() & Integer.MAX_VALUE; } else if (type.asNullable() == ClassType.LONG) { return RANDOM.nextLong() & Long.MAX_VALUE; + } else if (type.asNullable() == ClassType.BIG_DECIMAL) { + return RANDOM.nextLong() & Long.MAX_VALUE; } else if (type.asNullable() == ClassType.FLOAT) { return RANDOM.nextFloat() * 100; } else if (type.asNullable() == ClassType.DOUBLE) { From 1c22dea79887f3c098bf0b76cdd6941502271027 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Tue, 24 Dec 2024 14:27:18 +0800 Subject: [PATCH 2/4] break string, if too long --- .../core/template/ModelTestTemplate.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java index d7cd6e74f3e..d1652b56ec2 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java @@ -35,6 +35,9 @@ public static ModelTestTemplate getInstance() { @Override public void write(ClientModel model, JavaFile javaFile) { + // TODO: there is still possible "code too large" error + // example: https://github.com/Azure/azure-rest-api-specs/tree/main/specification/logic/resource-manager + final boolean immutableOutputModel = JavaSettings.getInstance().isOutputModelImmutable() && model.getImplementationDetails() != null && !model.getImplementationDetails().isInput(); @@ -66,7 +69,7 @@ public void write(ClientModel model, JavaFile javaFile) { classBlock.annotation("org.junit.jupiter.api.Test"); classBlock.publicMethod("void testDeserialize() throws Exception", methodBlock -> { methodBlock.line(String.format("%1$s model = BinaryData.fromString(%2$s).toObject(%1$s.class);", - model.getName(), ClassType.STRING.defaultValueExpression(jsonStr))); + model.getName(), getJavaStringExpression(jsonStr))); writer.writeAssertion(methodBlock); }); @@ -88,4 +91,25 @@ public void write(ClientModel model, JavaFile javaFile) { } }); } + + private static String getJavaStringExpression(String jsonStr) { + // 2^16, but half the size, for there maybe lots of `\"` in JSON + final int maxConstantStringLength = 65536 / 2; + if (jsonStr.length() < maxConstantStringLength) { + return ClassType.STRING.defaultValueExpression(jsonStr); + } else { + // avoid "constant string too long" + StringBuilder sb = new StringBuilder("String.join(\"\""); + String remains = jsonStr; + String.join("", "", ""); + while (remains.length() >= maxConstantStringLength) { + String current = remains.substring(0, maxConstantStringLength - 1); + remains = remains.substring(maxConstantStringLength - 1); + + sb.append(", ").append(ClassType.STRING.defaultValueExpression(current)); + } + sb.append(")"); + return sb.toString(); + } + } } From 23b3694481790d4078bd27721162624c875fa8c9 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Fri, 3 Jan 2025 14:45:46 +0800 Subject: [PATCH 3/4] Remove redundant String.join statement --- .../http/client/generator/core/template/ModelTestTemplate.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java index d1652b56ec2..f2238ba4ae0 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java @@ -101,7 +101,6 @@ private static String getJavaStringExpression(String jsonStr) { // avoid "constant string too long" StringBuilder sb = new StringBuilder("String.join(\"\""); String remains = jsonStr; - String.join("", "", ""); while (remains.length() >= maxConstantStringLength) { String current = remains.substring(0, maxConstantStringLength - 1); remains = remains.substring(maxConstantStringLength - 1); From 7fa13d67478636d500cdb284c6ab3da327826623 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Fri, 3 Jan 2025 15:35:44 +0800 Subject: [PATCH 4/4] skip the test case when constant string too long --- .../core/model/javamodel/JavaPackage.java | 4 ++ .../core/template/ModelTestTemplate.java | 46 +++++++++---------- .../util/ConstantStringTooLongException.java | 11 +++++ 3 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java index 349f24462e4..c88a72f7c6e 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java @@ -39,6 +39,7 @@ import com.microsoft.typespec.http.client.generator.core.template.Templates; import com.microsoft.typespec.http.client.generator.core.template.TestProxyAssetsTemplate; import com.microsoft.typespec.http.client.generator.core.util.ClientModelUtil; +import com.microsoft.typespec.http.client.generator.core.util.ConstantStringTooLongException; import com.microsoft.typespec.http.client.generator.core.util.PossibleCredentialException; import java.io.BufferedReader; import java.io.IOException; @@ -299,6 +300,9 @@ public void addModelUnitTest(ClientModel model) { } catch (PossibleCredentialException e) { // skip this test file logger.warn("Skip unit test for model '{}', caused by key '{}'", model.getName(), e.getKeyName()); + } catch (ConstantStringTooLongException e) { + // skip this test file + logger.warn("Skip unit test for model '{}', JSON string is too long.", model.getName()); } } diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java index f2238ba4ae0..f932d5e8304 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java @@ -12,6 +12,7 @@ import com.microsoft.typespec.http.client.generator.core.model.clientmodel.examplemodel.ExampleNode; import com.microsoft.typespec.http.client.generator.core.model.javamodel.JavaFile; import com.microsoft.typespec.http.client.generator.core.template.example.ModelExampleWriter; +import com.microsoft.typespec.http.client.generator.core.util.ConstantStringTooLongException; import com.microsoft.typespec.http.client.generator.core.util.ModelExampleUtil; import com.microsoft.typespec.http.client.generator.core.util.ModelTestCaseUtil; import java.io.ByteArrayOutputStream; @@ -32,12 +33,21 @@ public static ModelTestTemplate getInstance() { return INSTANCE; } + /** + * Write the JSON serialization / de-serialization unit test for the model. + * + * @param model the client model to test. + * @param javaFile the java file. + * @throws com.microsoft.typespec.http.client.generator.core.util.PossibleCredentialException + * thrown when there is possible mock value to a secret property. + * Even when the value is mocked, it could be flagged by CI as issue. Therefore, the case is skipped. + * @throws com.microsoft.typespec.http.client.generator.core.util.ConstantStringTooLongException + * thrown when the String representation of the JSON is too long (>= 2^16). + * Constant string of that size would cause compiler "constant string too long" error. + */ @Override public void write(ClientModel model, JavaFile javaFile) { - // TODO: there is still possible "code too large" error - // example: https://github.com/Azure/azure-rest-api-specs/tree/main/specification/logic/resource-manager - final boolean immutableOutputModel = JavaSettings.getInstance().isOutputModelImmutable() && model.getImplementationDetails() != null && !model.getImplementationDetails().isInput(); @@ -64,12 +74,20 @@ public void write(ClientModel model, JavaFile javaFile) { javaFile.declareImport(imports); + String jsonStringExpression = ClassType.STRING.defaultValueExpression(jsonStr); + if (jsonStringExpression.length() >= 65536) { + // Java compiler would give "constant string too long" error on the generated file. + // The length of a string constant in a class file is limited to 2^16 bytes in UTF-8 encoding. + // There is also a related "code too large" error, for limit on Java method size in bytecode. + throw new ConstantStringTooLongException(); + } + javaFile.publicFinalClass(model.getName() + "Tests", classBlock -> { // testDeserialize classBlock.annotation("org.junit.jupiter.api.Test"); classBlock.publicMethod("void testDeserialize() throws Exception", methodBlock -> { methodBlock.line(String.format("%1$s model = BinaryData.fromString(%2$s).toObject(%1$s.class);", - model.getName(), getJavaStringExpression(jsonStr))); + model.getName(), jsonStringExpression)); writer.writeAssertion(methodBlock); }); @@ -91,24 +109,4 @@ public void write(ClientModel model, JavaFile javaFile) { } }); } - - private static String getJavaStringExpression(String jsonStr) { - // 2^16, but half the size, for there maybe lots of `\"` in JSON - final int maxConstantStringLength = 65536 / 2; - if (jsonStr.length() < maxConstantStringLength) { - return ClassType.STRING.defaultValueExpression(jsonStr); - } else { - // avoid "constant string too long" - StringBuilder sb = new StringBuilder("String.join(\"\""); - String remains = jsonStr; - while (remains.length() >= maxConstantStringLength) { - String current = remains.substring(0, maxConstantStringLength - 1); - remains = remains.substring(maxConstantStringLength - 1); - - sb.append(", ").append(ClassType.STRING.defaultValueExpression(current)); - } - sb.append(")"); - return sb.toString(); - } - } } diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java new file mode 100644 index 00000000000..998553b83df --- /dev/null +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.typespec.http.client.generator.core.util; + +public class ConstantStringTooLongException extends RuntimeException { + + public ConstantStringTooLongException() { + super("Constant string too long."); + } +}