Skip to content

Commit 1973f25

Browse files
Add CBOR Tests and update Protocol test models to 1.0.5931.0 and Smithy Version 1.58.0
1 parent d6a1c84 commit 1973f25

File tree

21 files changed

+984
-24
lines changed

21 files changed

+984
-24
lines changed

extensions/AWSSDK.Extensions.sln

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWSSDK.CommonTest", "..\sdk
7474
EndProject
7575
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWSSDK.S3.NetFramework", "..\sdk\src\Services\S3\AWSSDK.S3.NetFramework.csproj", "{1A313326-988F-4FF2-992E-6D7E50DCF598}"
7676
EndProject
77+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWSSDK.Extensions.CborProtocol.NetFramework", "src\AWSSDK.Extensions.CborProtocol\AWSSDK.Extensions.CborProtocol.NetFramework.csproj", "{4FCCBD72-8E9B-D74B-6538-D8684D181230}"
78+
EndProject
79+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWSSDK.Extensions.CborProtocol.NetStandard", "src\AWSSDK.Extensions.CborProtocol\AWSSDK.Extensions.CborProtocol.NetStandard.csproj", "{24CBBC97-409E-BAC8-0553-D260AB0B8C6A}"
80+
EndProject
81+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CborProtocol.Tests.NetFramework", "test\CborProtocol.Tests\CborProtocol.Tests.NetFramework.csproj", "{9FFDECAB-94D6-7EB1-1A5B-487492600755}"
82+
EndProject
7783
Global
7884
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7985
Debug|Any CPU = Debug|Any CPU
@@ -204,6 +210,18 @@ Global
204210
{1A313326-988F-4FF2-992E-6D7E50DCF598}.Debug|Any CPU.Build.0 = Debug|Any CPU
205211
{1A313326-988F-4FF2-992E-6D7E50DCF598}.Release|Any CPU.ActiveCfg = Release|Any CPU
206212
{1A313326-988F-4FF2-992E-6D7E50DCF598}.Release|Any CPU.Build.0 = Release|Any CPU
213+
{4FCCBD72-8E9B-D74B-6538-D8684D181230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
214+
{4FCCBD72-8E9B-D74B-6538-D8684D181230}.Debug|Any CPU.Build.0 = Debug|Any CPU
215+
{4FCCBD72-8E9B-D74B-6538-D8684D181230}.Release|Any CPU.ActiveCfg = Release|Any CPU
216+
{4FCCBD72-8E9B-D74B-6538-D8684D181230}.Release|Any CPU.Build.0 = Release|Any CPU
217+
{24CBBC97-409E-BAC8-0553-D260AB0B8C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
218+
{24CBBC97-409E-BAC8-0553-D260AB0B8C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
219+
{24CBBC97-409E-BAC8-0553-D260AB0B8C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
220+
{24CBBC97-409E-BAC8-0553-D260AB0B8C6A}.Release|Any CPU.Build.0 = Release|Any CPU
221+
{9FFDECAB-94D6-7EB1-1A5B-487492600755}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
222+
{9FFDECAB-94D6-7EB1-1A5B-487492600755}.Debug|Any CPU.Build.0 = Debug|Any CPU
223+
{9FFDECAB-94D6-7EB1-1A5B-487492600755}.Release|Any CPU.ActiveCfg = Release|Any CPU
224+
{9FFDECAB-94D6-7EB1-1A5B-487492600755}.Release|Any CPU.Build.0 = Release|Any CPU
207225
EndGlobalSection
208226
GlobalSection(SolutionProperties) = preSolution
209227
HideSolutionNode = FALSE
@@ -240,6 +258,9 @@ Global
240258
{58BD3C7A-087C-4ECB-87C6-A3445025BEF5} = {A960D001-40B3-4B1A-A890-D1049FB7586E}
241259
{AF5B5402-7C9A-4E5E-B0C2-9278BAE0435C} = {A960D001-40B3-4B1A-A890-D1049FB7586E}
242260
{1A313326-988F-4FF2-992E-6D7E50DCF598} = {0BA39F07-84D6-420B-82D3-6DC3AF016C65}
261+
{4FCCBD72-8E9B-D74B-6538-D8684D181230} = {3D822DC2-ED2E-4434-BC4F-CE7FCD846B02}
262+
{24CBBC97-409E-BAC8-0553-D260AB0B8C6A} = {3D822DC2-ED2E-4434-BC4F-CE7FCD846B02}
263+
{9FFDECAB-94D6-7EB1-1A5B-487492600755} = {A960D001-40B3-4B1A-A890-D1049FB7586E}
243264
EndGlobalSection
244265
GlobalSection(ExtensibilityGlobals) = postSolution
245266
SolutionGuid = {949367A4-5683-4FD3-93F4-A2CEA6EECB21}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>net472</TargetFrameworks>
4+
<AssemblyName>CborProtocol.Tests</AssemblyName>
5+
<PackageId>CborProtocol.Tests</PackageId>
6+
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
7+
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
8+
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
9+
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
10+
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
11+
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
12+
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
13+
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
14+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
15+
<LangVersion>Latest</LangVersion>
16+
</PropertyGroup>
17+
<ItemGroup>
18+
<PackageReference Include="xunit" Version="2.9.3" />
19+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
20+
<PrivateAssets>all</PrivateAssets>
21+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
22+
</PackageReference>
23+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<ProjectReference Include="../../../sdk/src/Core/AWSSDK.Core.NetFramework.csproj" />
28+
<ProjectReference Include="../../src/AWSSDK.Extensions.CborProtocol/AWSSDK.Extensions.CborProtocol.NetFramework.csproj" />
29+
</ItemGroup>
30+
</Project>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Formats.Cbor;
3+
using Xunit;
4+
using AWSSDK.Extensions.CborProtocol;
5+
6+
namespace Amazon.CborProtocol.Tests;
7+
8+
public class WriteDateTimeTests
9+
{
10+
[Theory]
11+
[InlineData("1969-12-31T23:59:59Z", -1.0, CborReaderState.NegativeInteger, 2)]
12+
[InlineData("1970-01-01T00:00:01Z", 1.0, CborReaderState.UnsignedInteger, 2)]
13+
[InlineData("1970-01-01T00:00:00.500Z", 0.5, CborReaderState.SinglePrecisionFloat, 6)]
14+
[InlineData("2025-01-01T00:00:00Z", 1735689600.0, CborReaderState.UnsignedInteger, 6)]
15+
[InlineData("2025-06-19T20:15:28.468Z", 1750364128.468, CborReaderState.DoublePrecisionFloat, 10)]
16+
public void WriteDateTime_EncodesCorrectly(string isoDate, double expectedUnixEpoch, CborReaderState expectedState, int expectedTotalBytes)
17+
{
18+
var dt = DateTime.Parse(isoDate).ToUniversalTime();
19+
var writer = new CborWriter();
20+
writer.WriteDateTime(dt);
21+
22+
var encoded = writer.Encode();
23+
var reader = new CborReader(encoded);
24+
var tag = reader.ReadTag();
25+
26+
Assert.Equal(CborTag.UnixTimeSeconds, tag);
27+
Assert.Equal(expectedState, reader.PeekState());
28+
29+
double value = 0;
30+
31+
if (expectedState == CborReaderState.SinglePrecisionFloat)
32+
value = reader.ReadSingle();
33+
else if (expectedState == CborReaderState.DoublePrecisionFloat)
34+
value = reader.ReadDouble();
35+
else if (expectedState == CborReaderState.UnsignedInteger)
36+
value = reader.ReadUInt64();
37+
else if (expectedState == CborReaderState.NegativeInteger)
38+
value = reader.ReadInt64();
39+
else
40+
Assert.Fail($"CBOR state not found: ${reader.PeekState()}");
41+
42+
Assert.Equal(expectedUnixEpoch, value);
43+
Assert.Equal(expectedTotalBytes, encoded.Length);
44+
}
45+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Formats.Cbor;
2+
using Xunit;
3+
using AWSSDK.Extensions.CborProtocol;
4+
5+
namespace Amazon.CborProtocol.Tests;
6+
7+
public class WriteOptimizedNumberTests
8+
{
9+
[Theory]
10+
[InlineData(0, 1)] // Fits in initial byte (major type + value)
11+
[InlineData(23, 1)] // Still in initial byte
12+
[InlineData(24, 2)] // One additional byte for UInt8
13+
[InlineData(255, 2)] // UInt8
14+
[InlineData(256, 3)] // UInt16
15+
[InlineData(65535, 3)] // UInt16 max
16+
[InlineData(65536, 5)] // UInt32
17+
[InlineData(uint.MaxValue, 5)] // UInt32
18+
[InlineData((long)uint.MaxValue + 1, 9)] // UInt64
19+
public void WriteOptimizedNumber_UsesExpectedEncodingLength_ForUInt64(double value, int expectedBytes)
20+
{
21+
var writer = new CborWriter();
22+
writer.WriteOptimizedNumber((double)value);
23+
var encoded = writer.Encode();
24+
25+
Assert.Equal(expectedBytes, encoded.Length);
26+
}
27+
28+
[Theory]
29+
[InlineData(-1, 1)] // Negative integers encoded compactly
30+
[InlineData(-24, 1)]
31+
[InlineData(-25, 2)] // Beyond negative byte range
32+
[InlineData(int.MinValue, 5)] // Should require full 32 bits
33+
[InlineData(long.MinValue, 9)] // Full 64-bit negative integer
34+
public void WriteOptimizedNumber_UsesExpectedEncodingLength_ForNegativeIntegers(long value, int expectedBytes)
35+
{
36+
var writer = new CborWriter();
37+
writer.WriteOptimizedNumber((double)value);
38+
var encoded = writer.Encode();
39+
40+
Assert.Equal(expectedBytes, encoded.Length);
41+
}
42+
43+
[Theory]
44+
[InlineData(1.5, 5, 0xFA)] // float32 marker + 4 bytes
45+
[InlineData(123456789.12345, 9, 0xFB)] // Doesn't fit in float32
46+
public void WriteOptimizedNumber_Float32_IsManuallyEncodedAsFiveBytes(double value, int expectedBytes, byte floatMarker)
47+
{
48+
var writer = new CborWriter();
49+
writer.WriteOptimizedNumber(value);
50+
var encoded = writer.Encode();
51+
52+
Assert.Equal(expectedBytes, encoded.Length);
53+
Assert.Equal(floatMarker, encoded[0]); // Check float32 CBOR marker
54+
}
55+
}

generator/ProtocolTestsGenerator/settings.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ pluginManagement {
1212
dependencyResolutionManagement {
1313
versionCatalogs {
1414
create("codegen") {
15-
version("smithy", "1.54.0")
16-
library("protocol-tests", "software.amazon.smithy", "smithy-aws-protocol-tests").versionRef("smithy")
15+
version("smithy", "1.58.0")
16+
library("protocol-tests", "software.amazon.smithy", "smithy-protocol-tests").versionRef("smithy")
17+
library("protocol-aws-tests", "software.amazon.smithy", "smithy-aws-protocol-tests").versionRef("smithy")
1718
library("codegen-core", "software.amazon.smithy", "smithy-codegen-core").versionRef("smithy")
1819
library("protocol-tests-traits", "software.amazon.smithy", "smithy-protocol-test-traits").versionRef("smithy")
1920
library("aws-traits", "software.amazon.smithy", "smithy-aws-traits").versionRef("smithy")

generator/ProtocolTestsGenerator/smithy-dotnet-codegen/src/main/java/software/amazon/smithy/dotnet/codegen/HttpProtocolTestGenerator.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package software.amazon.smithy.dotnet.codegen;
1717

1818
import software.amazon.smithy.aws.traits.ServiceTrait;
19+
import software.amazon.smithy.model.traits.TitleTrait;
1920
import software.amazon.smithy.codegen.core.CodegenException;
2021
import software.amazon.smithy.dotnet.codegen.utils.ProtocolTestUtils;
2122
import software.amazon.smithy.model.Model;
@@ -51,7 +52,14 @@ public HttpProtocolTestGenerator(
5152
this.model = context.model();
5253
this.service = settings.getService(model);
5354
this.context = context;
54-
this.serviceNamespace = service.getTrait(ServiceTrait.class).get().getSdkId().replace(" ", "");
55+
56+
String serviceNamespace = null;
57+
if(service.getTrait(ServiceTrait.class).isPresent())
58+
serviceNamespace = service.getTrait(ServiceTrait.class).get().getSdkId();
59+
else if(service.getTrait(TitleTrait.class).isPresent())
60+
serviceNamespace = service.getTrait(TitleTrait.class).get().getValue().replace("Service", "");
61+
62+
this.serviceNamespace = serviceNamespace.replace(" ", "");
5563
}
5664

5765
@Override
@@ -60,6 +68,8 @@ public void run() {
6068
OperationIndex operationIndex = OperationIndex.of(model);
6169
for (OperationShape operation : new TreeSet<>(topDownIndex.getContainedOperations(service))) {
6270
var operationName = operation.getId().getName();
71+
if (ProtocolTestCustomizations.OperationsToSkip.contains(operationName))
72+
continue;
6373
context.writerDelegator().useFileWriter(operationName + ".cs", serviceName, writer -> {
6474
this.writer = writer;
6575
addServiceProtocolSpecificImports();
@@ -87,14 +97,17 @@ private void addServiceProtocolSpecificImports() {
8797
} else if (this.serviceName.toLowerCase().contains("xml")) {
8898
writer.addImport(serviceName, "System.Xml");
8999
writer.addImport(serviceName, "System.Xml.Linq");
100+
} else if (this.serviceName.toLowerCase().contains("rpcv2")) {
101+
writer.addImport(serviceName, "AWSSDK.Extensions.CborProtocol.Internal");
90102
}
91103
}
92104

93105
private void generateErrorResponseTests(OperationShape operation, OperationIndex index) {
94106
for (StructureShape error : index.getErrors(operation, service)) {
95107
error.getTrait(HttpResponseTestsTrait.class).ifPresent(trait -> {
96108
for (HttpResponseTestCase httpResponseTestCase : trait.getTestCasesFor(AppliesTo.CLIENT)) {
97-
generateErrorResponseTest(operation, error, httpResponseTestCase);
109+
if(!trait.getTestCasesFor(AppliesTo.CLIENT).getFirst().getProtocol().getName().toLowerCase().contains("cbor")) // Skip CBOR response tests until the unmarshallers are ready
110+
generateErrorResponseTest(operation, error, httpResponseTestCase);
98111
}
99112
});
100113
}
@@ -283,6 +296,9 @@ private void assertRequestBody(HttpRequestTestCase httpRequestTestCase) {
283296
} else if (httpRequestTestCase.getProtocol().getName().equals("restXml")) {
284297
writer.write("var expectedBody = $S;", httpRequestTestCase.getBody());
285298
writer.write("XmlTestUtils.AssertBody(marshalledRequest,expectedBody);");
299+
} else if (this.marshallerType.equals("Cbor")) {
300+
writer.write("var expectedBody = $S;", httpRequestTestCase.getBody());
301+
writer.write("CborProtocolUtils.AssertBody(marshalledRequest, expectedBody);");
286302
} else {
287303
throw new CodegenException("Unsupported protocol detected while generating request test block.");
288304
}
@@ -294,7 +310,8 @@ private void generateResponseTests(OperationShape operation) {
294310
setMarshallerType(trait.getTestCasesFor(AppliesTo.CLIENT).getFirst().getProtocol().getName());
295311
}
296312
for (HttpResponseTestCase httpResponseTestCase : trait.getTestCasesFor(AppliesTo.CLIENT)) {
297-
if (ProtocolTestCustomizations.TestsToSkip.contains(httpResponseTestCase.getId()) || httpResponseTestCase.hasTag("defaults"))
313+
if (ProtocolTestCustomizations.TestsToSkip.contains(httpResponseTestCase.getId()) || httpResponseTestCase.hasTag("defaults")
314+
|| trait.getTestCasesFor(AppliesTo.CLIENT).getFirst().getProtocol().getName().toLowerCase().contains("cbor")) // Skip CBOR response tests until the unmarshallers are ready
298315
continue;
299316
generateResponseTest(operation, httpResponseTestCase);
300317
}
@@ -343,6 +360,8 @@ private void setMarshallerType(String protocol) {
343360
this.marshallerType = "Json";
344361
} else if (protocol.toLowerCase().contains("xml") || protocol.toLowerCase().contains("query")) {
345362
this.marshallerType = "Xml";
363+
} else if (protocol.toLowerCase().contains("cbor")) {
364+
this.marshallerType = "Cbor";
346365
}
347366
}
348367

generator/ProtocolTestsGenerator/smithy-dotnet-codegen/src/main/java/software/amazon/smithy/dotnet/codegen/customizations/ProtocolTestCustomizations.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ private ProtocolTestCustomizations() {
2020
"SparseBooleanMap",
2121
"SparseNumberMap"
2222
);
23+
24+
// These operations don't exist in C2J
25+
public static final List<String> OperationsToSkip = Arrays.asList(
26+
"RpcV2CborSparseMaps",
27+
"OperationWithDefaults",
28+
"SparseNullsOperation"
29+
);
30+
2331
//The rename is written in smithy and since we're generating from the C2J structures we will skip this test.
2432
public static final List<String> TestsToSkip = Arrays.asList(
2533
"RestJsonSerializeRenamedStructureUnionValue",

generator/ProtocolTestsGenerator/smithy-dotnet-protocol-test/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ repositories {
2727

2828
dependencies {
2929
implementation(project(":smithy-dotnet-codegen"))
30+
implementation(codegen.protocol.aws.tests)
3031
implementation(codegen.protocol.tests)
3132
}
3233

generator/ProtocolTestsGenerator/smithy-dotnet-protocol-test/smithy-build.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@
127127
"packageVersion": "0.0.1"
128128
}
129129
}
130+
},
131+
"RpcV2Protocol": {
132+
"transforms": [
133+
{
134+
"name": "includeServices",
135+
"args": {
136+
"services": [
137+
"smithy.protocoltests.rpcv2Cbor#RpcV2Protocol"
138+
]
139+
}
140+
}
141+
],
142+
"plugins": {
143+
"dotnet-protocol-test-codegen": {
144+
"service": "smithy.protocoltests.rpcv2Cbor#RpcV2Protocol",
145+
"packageVersion": "0.0.1"
146+
}
147+
}
130148
}
131149
}
132150
}

generator/TestServiceModels/restjson-tests-client/rest-json-protocol-2019-12-16.normal.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,17 @@
176176
"documentation":"<p>This example tests httpChecksumRequired trait</p>",
177177
"httpChecksumRequired":true
178178
},
179+
"HttpEmptyPrefixHeaders":{
180+
"name":"HttpEmptyPrefixHeaders",
181+
"http":{
182+
"method":"GET",
183+
"requestUri":"/HttpEmptyPrefixHeaders",
184+
"responseCode":200
185+
},
186+
"input":{"shape":"HttpEmptyPrefixHeadersInput"},
187+
"output":{"shape":"HttpEmptyPrefixHeadersOutput"},
188+
"documentation":"<p>Clients that perform this test extract all headers from the response.</p>"
189+
},
179190
"HttpEnumPayload":{
180191
"name":"HttpEnumPayload",
181192
"http":{
@@ -1030,6 +1041,36 @@
10301041
"type":"timestamp",
10311042
"timestampFormat":"rfc822"
10321043
},
1044+
"HttpEmptyPrefixHeadersInput":{
1045+
"type":"structure",
1046+
"members":{
1047+
"prefixHeaders":{
1048+
"shape":"StringMap",
1049+
"location":"headers",
1050+
"locationName":""
1051+
},
1052+
"specificHeader":{
1053+
"shape":"String",
1054+
"location":"header",
1055+
"locationName":"hello"
1056+
}
1057+
}
1058+
},
1059+
"HttpEmptyPrefixHeadersOutput":{
1060+
"type":"structure",
1061+
"members":{
1062+
"prefixHeaders":{
1063+
"shape":"StringMap",
1064+
"location":"headers",
1065+
"locationName":""
1066+
},
1067+
"specificHeader":{
1068+
"shape":"String",
1069+
"location":"header",
1070+
"locationName":"hello"
1071+
}
1072+
}
1073+
},
10331074
"HttpPayloadTraitsInputOutput":{
10341075
"type":"structure",
10351076
"members":{

0 commit comments

Comments
 (0)