diff --git a/buildspec.yaml b/buildspec.yaml index 00fbd9c063..0388c8562e 100644 --- a/buildspec.yaml +++ b/buildspec.yaml @@ -6,6 +6,9 @@ phases: # Temporarily - install yarn if it's not there already - yarn --version || npm install --global yarn - yarn install --frozen-lockfile + # Temporary - gcc is needed for testify, but we can remove this once we have a new release + # of superchain, which will include it as of now. + - yum -y install gcc build: commands: - yarn build && yarn test diff --git a/gh-pages/content/specification/.pages.yml b/gh-pages/content/specification/.pages.yml index 493ce64c80..85e73d4878 100644 --- a/gh-pages/content/specification/.pages.yml +++ b/gh-pages/content/specification/.pages.yml @@ -6,3 +6,4 @@ nav: - The jsii kernel API: 3-kernel-api.md - 4-standard-compliance-suite.md - 5-new-language-intake.md + - 6-compliance-report.md diff --git a/gh-pages/content/specification/6-compliance-report.md b/gh-pages/content/specification/6-compliance-report.md new file mode 100644 index 0000000000..628574805f --- /dev/null +++ b/gh-pages/content/specification/6-compliance-report.md @@ -0,0 +1,132 @@ + + +# Compliance Report + +This section details the current state of each language binding with respect to our standard compliance suite. + +!!! Note + The following languages are currently excluded from the tests and are marked as N/A: + + **dotnet**: Underwent a different compliance process. Will be aligned in the future. + + **python**: Underwent a different compliance process. Will be aligned in the future. + +| number | test | description | java (100.00%) | golang (0.85%) | Dotnet | Python | +| ------ | ----------------------------------------------------------------- | ----------- | -------------- | -------------- | ------ | ------ | +| 1 | asyncOverrides_overrideCallsSuper | | ✅ | ❌ | N/A | N/A | +| 2 | arrayReturnedByMethodCanBeRead | | ✅ | ❌ | N/A | N/A | +| 3 | unionProperties | | ✅ | ❌ | N/A | N/A | +| 4 | syncOverrides | | ✅ | ❌ | N/A | N/A | +| 5 | useEnumFromScopedModule | | ✅ | ❌ | N/A | N/A | +| 6 | createObjectAndCtorOverloads | | ✅ | ❌ | N/A | N/A | +| 7 | fail_syncOverrides_callsDoubleAsync_method | | ✅ | ❌ | N/A | N/A | +| 8 | collectionOfInterfaces_MapOfStructs | | ✅ | ❌ | N/A | N/A | +| 9 | asyncOverrides_overrideAsyncMethod | | ✅ | ❌ | N/A | N/A | +| 10 | statics | | ✅ | ❌ | N/A | N/A | +| 11 | structs_returnedLiteralEqualsNativeBuilt | | ✅ | ❌ | N/A | N/A | +| 12 | classesCanSelfReferenceDuringClassInitialization | | ✅ | ❌ | N/A | N/A | +| 13 | canObtainStructReferenceWithOverloadedSetter | | ✅ | ❌ | N/A | N/A | +| 14 | callbacksCorrectlyDeserializeArguments | | ✅ | ❌ | N/A | N/A | +| 15 | canUseInterfaceSetters | | ✅ | ❌ | N/A | N/A | +| 16 | propertyOverrides_interfaces | | ✅ | ❌ | N/A | N/A | +| 17 | syncOverrides_callsSuper | | ✅ | ❌ | N/A | N/A | +| 18 | testJsiiAgent | | ✅ | ❌ | N/A | N/A | +| 19 | doNotOverridePrivates_method_private | | ✅ | ❌ | N/A | N/A | +| 20 | pureInterfacesCanBeUsedTransparently | | ✅ | ❌ | N/A | N/A | +| 21 | nullShouldBeTreatedAsUndefined | | ✅ | ❌ | N/A | N/A | +| 22 | primitiveTypes | | ✅ | ❌ | N/A | N/A | +| 23 | reservedKeywordsAreSlugifiedInClassProperties | | ✅ | ❌ | N/A | N/A | +| 24 | objectIdDoesNotGetReallocatedWhenTheConstructorPassesThisOut | | ✅ | ❌ | N/A | N/A | +| 25 | interfaceBuilder | | ✅ | ❌ | N/A | N/A | +| 26 | unionTypes | | ✅ | ❌ | N/A | N/A | +| 27 | arrays | | ✅ | ❌ | N/A | N/A | +| 28 | staticMapInClassCannotBeModified | | ✅ | ❌ | N/A | N/A | +| 29 | consts | | ✅ | ❌ | N/A | N/A | +| 30 | pureInterfacesCanBeUsedTransparently_WhenTransitivelyImplementing | | ✅ | ❌ | N/A | N/A | +| 31 | reservedKeywordsAreSlugifiedInMethodNames | | ✅ | ❌ | N/A | N/A | +| 32 | exceptions | | ✅ | ❌ | N/A | N/A | +| 33 | testLiteralInterface | | ✅ | ❌ | N/A | N/A | +| 34 | structs_nonOptionalhashCode | | ✅ | ❌ | N/A | N/A | +| 35 | propertyOverrides_set_throws | | ✅ | ❌ | N/A | N/A | +| 36 | canLeverageIndirectInterfacePolymorphism | | ✅ | ❌ | N/A | N/A | +| 37 | fluentApi | | ✅ | ❌ | N/A | N/A | +| 38 | staticListInClassCanBeReadCorrectly | | ✅ | ❌ | N/A | N/A | +| 39 | mapReturnedByMethodCannotBeModified | | ✅ | ❌ | N/A | N/A | +| 40 | receiveInstanceOfPrivateClass | | ✅ | ❌ | N/A | N/A | +| 41 | staticMapInClassCanBeReadCorrectly | | ✅ | ❌ | N/A | N/A | +| 42 | testNativeObjectsWithInterfaces | | ✅ | ❌ | N/A | N/A | +| 43 | doNotOverridePrivates_property_getter_public | | ✅ | ❌ | N/A | N/A | +| 44 | equalsIsResistantToPropertyShadowingResultVariable | | ✅ | ❌ | N/A | N/A | +| 45 | listInClassCanBeReadCorrectly | | ✅ | ❌ | N/A | N/A | +| 46 | useNestedStruct | | ✅ | ❌ | N/A | N/A | +| 47 | testFluentApiWithDerivedClasses | | ✅ | ❌ | N/A | N/A | +| 48 | interfacesCanBeUsedTransparently_WhenAddedToJsiiType | | ✅ | ❌ | N/A | N/A | +| 49 | canOverrideProtectedGetter | | ✅ | ❌ | N/A | N/A | +| 50 | getAndSetEnumValues | | ✅ | ❌ | N/A | N/A | +| 51 | structs_nonOptionalequals | | ✅ | ❌ | N/A | N/A | +| 52 | testInterfaceParameter | | ✅ | ❌ | N/A | N/A | +| 53 | liftedKwargWithSameNameAsPositionalArg | | ✅ | ❌ | N/A | N/A | +| 54 | creationOfNativeObjectsFromJavaScriptObjects | | ✅ | ❌ | N/A | N/A | +| 55 | canOverrideProtectedMethod | | ✅ | ❌ | N/A | N/A | +| 56 | canLoadEnumValues | | ✅ | ❌ | N/A | N/A | +| 57 | eraseUnsetDataValues | | ✅ | ❌ | N/A | N/A | +| 58 | maps | | ✅ | ✅ | N/A | N/A | +| 59 | structs_containsNullChecks | | ✅ | ❌ | N/A | N/A | +| 60 | canOverrideProtectedSetter | | ✅ | ❌ | N/A | N/A | +| 61 | asyncOverrides_callAsyncMethod | | ✅ | ❌ | N/A | N/A | +| 62 | nodeStandardLibrary | | ✅ | ❌ | N/A | N/A | +| 63 | dates | | ✅ | ❌ | N/A | N/A | +| 64 | collectionOfInterfaces_ListOfStructs | | ✅ | ❌ | N/A | N/A | +| 65 | objRefsAreLabelledUsingWithTheMostCorrectType | | ✅ | ❌ | N/A | N/A | +| 66 | unionPropertiesWithBuilder | | ✅ | ❌ | N/A | N/A | +| 67 | doNotOverridePrivates_property_getter_private | | ✅ | ❌ | N/A | N/A | +| 68 | structs_withDiamondInheritance_correctlyDedupeProperties | | ✅ | ❌ | N/A | N/A | +| 69 | abstractMembersAreCorrectlyHandled | | ✅ | ❌ | N/A | N/A | +| 70 | doNotOverridePrivates_property_by_name_private | | ✅ | ❌ | N/A | N/A | +| 71 | testNullIsAValidOptionalMap | | ✅ | ❌ | N/A | N/A | +| 72 | mapReturnedByMethodCanBeRead | | ✅ | ❌ | N/A | N/A | +| 73 | structs_multiplePropertiesEquals | | ✅ | ❌ | N/A | N/A | +| 74 | mapInClassCanBeReadCorrectly | | ✅ | ❌ | N/A | N/A | +| 75 | staticListInClassCannotBeModified | | ✅ | ❌ | N/A | N/A | +| 76 | collectionOfInterfaces_MapOfInterfaces | | ✅ | ❌ | N/A | N/A | +| 77 | asyncOverrides_overrideThrows | | ✅ | ❌ | N/A | N/A | +| 78 | callMethods | | ✅ | ❌ | N/A | N/A | +| 79 | returnAbstract | | ✅ | ❌ | N/A | N/A | +| 80 | dynamicTypes | | ✅ | ❌ | N/A | N/A | +| 81 | hashCodeIsResistantToPropertyShadowingResultVariable | | ✅ | ❌ | N/A | N/A | +| 82 | returnSubclassThatImplementsInterface976 | | ✅ | ❌ | N/A | N/A | +| 83 | structs_optionalEquals | | ✅ | ❌ | N/A | N/A | +| 84 | propertyOverrides_get_calls_super | | ✅ | ❌ | N/A | N/A | +| 85 | unmarshallIntoAbstractType | | ✅ | ❌ | N/A | N/A | +| 86 | structs_multiplePropertiesHashCode | | ✅ | ❌ | N/A | N/A | +| 87 | fail_syncOverrides_callsDoubleAsync_propertyGetter | | ✅ | ❌ | N/A | N/A | +| 88 | propertyOverrides_get_set | | ✅ | ❌ | N/A | N/A | +| 89 | variadicMethodCanBeInvoked | | ✅ | ❌ | N/A | N/A | +| 90 | collectionTypes | | ✅ | ❌ | N/A | N/A | +| 91 | asyncOverrides_overrideAsyncMethodByParentClass | | ✅ | ❌ | N/A | N/A | +| 92 | structs_optionalHashCode | | ✅ | ❌ | N/A | N/A | +| 93 | testStructsCanBeDowncastedToParentType | | ✅ | ❌ | N/A | N/A | +| 94 | propertyOverrides_get_throws | | ✅ | ❌ | N/A | N/A | +| 95 | getSetPrimitiveProperties | | ✅ | ❌ | N/A | N/A | +| 96 | getAndSetNonPrimitiveProperties | | ✅ | ❌ | N/A | N/A | +| 97 | reservedKeywordsAreSlugifiedInStructProperties | | ✅ | ❌ | N/A | N/A | +| 98 | fail_syncOverrides_callsDoubleAsync_propertySetter | | ✅ | ❌ | N/A | N/A | +| 99 | doNotOverridePrivates_method_public | | ✅ | ❌ | N/A | N/A | +| 100 | testNullIsAValidOptionalList | | ✅ | ❌ | N/A | N/A | +| 101 | mapInClassCannotBeModified | | ✅ | ❌ | N/A | N/A | +| 102 | doNotOverridePrivates_property_by_name_public | | ✅ | ❌ | N/A | N/A | +| 103 | asyncOverrides_twoOverrides | | ✅ | ❌ | N/A | N/A | +| 104 | propertyOverrides_set_calls_super | | ✅ | ❌ | N/A | N/A | +| 105 | iso8601DoesNotDeserializeToDate | | ✅ | ❌ | N/A | N/A | +| 106 | collectionOfInterfaces_ListOfInterfaces | | ✅ | ❌ | N/A | N/A | +| 107 | undefinedAndNull | | ✅ | ❌ | N/A | N/A | +| 108 | structs_serializeToJsii | | ✅ | ❌ | N/A | N/A | +| 109 | structsAreUndecoratedOntheWayToKernel | | ✅ | ❌ | N/A | N/A | +| 110 | canObtainReferenceWithOverloadedSetter | | ✅ | ❌ | N/A | N/A | +| 111 | testJSObjectLiteralToNative | | ✅ | ❌ | N/A | N/A | +| 112 | structs_stepBuilders | | ✅ | ❌ | N/A | N/A | +| 113 | classWithPrivateConstructorAndAutomaticProperties | | ✅ | ❌ | N/A | N/A | +| 114 | arrayReturnedByMethodCannotBeModified | | ✅ | ❌ | N/A | N/A | +| 115 | correctlyDeserializesStructUnions | | ✅ | ❌ | N/A | N/A | +| 116 | subclassing | | ✅ | ❌ | N/A | N/A | +| 117 | testInterfaces | | ✅ | ❌ | N/A | N/A | diff --git a/package.json b/package.json index 18efa4ddd5..0d14ec2e38 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,10 @@ "bump": "bash scripts/bump.sh", "dist-clean": "lerna run dist-clean --stream && rm -rf dist", "package": "bash scripts/package.sh", - "test": "lerna run test --concurrency=1 --stream", + "test": "lerna run test --concurrency=1 --stream && yarn compliance", "test:integ": "lerna run test:integ --stream", - "test:update": "lerna run test:update --concurrency=1 --stream" + "test:update": "lerna run test:update --concurrency=1 --stream", + "compliance": "(cd tools/jsii-compliance && yarn report)" }, "devDependencies": { "@jest/types": "^26.6.2", diff --git a/packages/@jsii/go-runtime-test/.eslintrc.yaml b/packages/@jsii/go-runtime-test/.eslintrc.yaml new file mode 100644 index 0000000000..308d08c432 --- /dev/null +++ b/packages/@jsii/go-runtime-test/.eslintrc.yaml @@ -0,0 +1,8 @@ +--- +extends: ../../../eslint-config.yaml +rules: + 'import/no-extraneous-dependencies': + - error + - devDependencies: ['**/build-tools/**'] # Only allow importing devDependencies from tests + optionalDependencies: false # Disallow importing optional dependencies (those shouldn't be used here) + peerDependencies: false # Disallow importing peer dependencies (those shouldn't be used here) diff --git a/packages/@jsii/go-runtime-test/.gitignore b/packages/@jsii/go-runtime-test/.gitignore new file mode 100644 index 0000000000..cfabfbd25d --- /dev/null +++ b/packages/@jsii/go-runtime-test/.gitignore @@ -0,0 +1,7 @@ +/jsii-calc/ + +*.js +*.d.ts + +# Ignore our test files +project/compliance-report.json diff --git a/packages/@jsii/go-runtime-test/LICENSE b/packages/@jsii/go-runtime-test/LICENSE new file mode 100644 index 0000000000..129acd53d9 --- /dev/null +++ b/packages/@jsii/go-runtime-test/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/@jsii/go-runtime-test/NOTICE b/packages/@jsii/go-runtime-test/NOTICE new file mode 100644 index 0000000000..dc4ac3f857 --- /dev/null +++ b/packages/@jsii/go-runtime-test/NOTICE @@ -0,0 +1,2 @@ +jsii +Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@jsii/go-runtime-test/README.md b/packages/@jsii/go-runtime-test/README.md new file mode 100644 index 0000000000..700da83a0a --- /dev/null +++ b/packages/@jsii/go-runtime-test/README.md @@ -0,0 +1,7 @@ +# Go Runtime Test + +This directory contains compliance tests for the go language bindings. + +## Usage + +`yarn build && yarn test` \ No newline at end of file diff --git a/packages/@jsii/go-runtime/build-tools/gen-calc.ts b/packages/@jsii/go-runtime-test/build-tools/gen-calc.ts similarity index 82% rename from packages/@jsii/go-runtime/build-tools/gen-calc.ts rename to packages/@jsii/go-runtime-test/build-tools/gen-calc.ts index f0047f3fb7..cc2ddb65f2 100644 --- a/packages/@jsii/go-runtime/build-tools/gen-calc.ts +++ b/packages/@jsii/go-runtime-test/build-tools/gen-calc.ts @@ -1,12 +1,10 @@ #!/usr/bin/env npx ts-node +import { runCommand } from '@jsii/go-runtime/build-tools/_constants'; +import { localRuntimeModules } from '@jsii/go-runtime/lib/local-runtime-modules'; import { readFileSync, removeSync, writeFileSync } from 'fs-extra'; import { join, relative, resolve } from 'path'; -import { runtimeModules } from '../lib'; -import { localRuntimeModules } from '../lib/local-runtime-modules'; -import { runCommand } from './_constants'; - const genRoot = join(__dirname, '..', 'jsii-calc'); removeSync(genRoot); @@ -28,10 +26,6 @@ runCommand( // Inject "replaces" in the go.mod files so IDEs do not struggle too much... const genModules = localRuntimeModules(genRoot); -const localModules = { - ...genModules, - ...runtimeModules, -}; for (const localPath of Object.values(genModules)) { const goModFile = join(localPath, 'go.mod'); const goMod = readFileSync(goModFile, 'utf8'); @@ -45,8 +39,8 @@ for (const localPath of Object.values(genModules)) { const depRegex = /([a-z0-9._~/-]+)\s+v\d/gi; while ((matches = depRegex.exec(goMod)) != null) { const [, dep] = matches; - if (dep in localModules) { - const depPath = localModules[dep]; + if (dep in genModules) { + const depPath = genModules[dep]; replaces.push({ dep, depPath }); } } diff --git a/packages/@jsii/go-runtime-test/package.json b/packages/@jsii/go-runtime-test/package.json new file mode 100644 index 0000000000..02e937f601 --- /dev/null +++ b/packages/@jsii/go-runtime-test/package.json @@ -0,0 +1,25 @@ +{ + "name": "@jsii/go-runtime-test", + "version": "0.0.0", + "private": true, + "description": "", + "scripts": { + "build": "tsc --build && yarn gen:calc", + "fmt": "(cd project && go fmt ./...)", + "lint": "(cd project && go vet ./...)", + "test": "(cd project && go test ./...)", + "lint:fix": "yarn lint --fix", + "gen:calc": "node build-tools/gen-calc.js" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "jsii-pacmak": "^0.0.0" + }, + "dependencies": { + "fs-extra": "^9.1.0", + "@jsii/go-runtime": "^0.0.0" + } + +} diff --git a/packages/@jsii/go-runtime/jsii-calc-test/callbacks_test.go b/packages/@jsii/go-runtime-test/project/callbacks_test.go similarity index 97% rename from packages/@jsii/go-runtime/jsii-calc-test/callbacks_test.go rename to packages/@jsii/go-runtime-test/project/callbacks_test.go index 3859470cef..cc02ca811d 100644 --- a/packages/@jsii/go-runtime/jsii-calc-test/callbacks_test.go +++ b/packages/@jsii/go-runtime-test/project/callbacks_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "testing" diff --git a/packages/@jsii/go-runtime-test/project/compliance_test.go b/packages/@jsii/go-runtime-test/project/compliance_test.go new file mode 100644 index 0000000000..6899df6b00 --- /dev/null +++ b/packages/@jsii/go-runtime-test/project/compliance_test.go @@ -0,0 +1,76 @@ +package tests + +import ( + "encoding/json" + "io/ioutil" + "strings" + "testing" + + calc "github.com/aws/jsii/jsii-calc/go/jsiicalc/v3" + calclib "github.com/aws/jsii/jsii-calc/go/scopejsiicalclib" + "github.com/stretchr/testify/suite" +) + +type ComplianceSuite struct { + suite.Suite + report map[string]interface{} +} + +func (suite *ComplianceSuite) SetupSuite() { + suite.report = map[string]interface{}{} +} + +func (suite *ComplianceSuite) TearDownSuite() { + report, err := json.MarshalIndent(suite.report, "", " ") + + if err != nil { + suite.FailNowf("Failed marshalling report: %s", err.Error()) + } + err = ioutil.WriteFile("./compliance-report.json", report, 0644) + if err != nil { + suite.FailNowf("Failed writing report: %s", err.Error()) + } +} + +func (suite *ComplianceSuite) AfterTest(suiteName, testName string) { + + status := "success" + if suite.T().Failed() { + status = "failure" + } + if suite.T().Skipped() { + status = "skipped" + } + + // remove the 'Test' prefix to make it more comparable with other languages who don't require it. + suite.report[strings.Replace(testName, "Test", "", 1)] = map[string]interface{}{"status": status} +} + +func (suite *ComplianceSuite) TestMaps() { + + t := suite.T() + + allTypes := calc.NewAllTypes() + actual := allTypes.MapProperty() + if len(actual) != 0 { + t.Errorf("Expected length of empty map to be 0. Got: %d", len(actual)) + } + + question := "The answer to the ultimate question of life, the universe, and everything" + answer := calclib.NewNumber(42) + allTypes.SetMapProperty(map[string]calclib.Number{ + question: answer, + }) + actual = allTypes.MapProperty() + if len(actual) != 1 { + t.Errorf("Expected length of empty map to be 1. Got: %d", len(actual)) + } + if actual[question].Value() != answer.Value() { + t.Errorf("Expected to have the value %v in there, got: %v", answer, actual[question]) + } +} + +// required to make `go test` recognize the suite. +func TestComplianceSuite(t *testing.T) { + suite.Run(t, new(ComplianceSuite)) +} diff --git a/packages/@jsii/go-runtime-test/project/go.mod b/packages/@jsii/go-runtime-test/project/go.mod new file mode 100644 index 0000000000..9e3edf4c57 --- /dev/null +++ b/packages/@jsii/go-runtime-test/project/go.mod @@ -0,0 +1,18 @@ +module github.com/aws/jsii/go-runtime-test + +go 1.15 + +require ( + github.com/aws/jsii-runtime-go v0.0.0 + github.com/aws/jsii/jsii-calc/go/jsiicalc/v3 v3.20.120 + github.com/aws/jsii/jsii-calc/go/scopejsiicalclib v0.0.0 + github.com/stretchr/testify v1.7.0 +) + +replace ( + github.com/aws/jsii-runtime-go => ../../go-runtime/jsii-runtime-go + github.com/aws/jsii/jsii-calc/go/jsiicalc/v3 => ../jsii-calc/go/jsiicalc + github.com/aws/jsii/jsii-calc/go/scopejsiicalcbase => ../jsii-calc/go/scopejsiicalcbase + github.com/aws/jsii/jsii-calc/go/scopejsiicalcbaseofbase/v2 => ../jsii-calc/go/scopejsiicalcbaseofbase + github.com/aws/jsii/jsii-calc/go/scopejsiicalclib => ../jsii-calc/go/scopejsiicalclib +) diff --git a/packages/@jsii/go-runtime-test/project/go.sum b/packages/@jsii/go-runtime-test/project/go.sum new file mode 100644 index 0000000000..46621f39b3 --- /dev/null +++ b/packages/@jsii/go-runtime-test/project/go.sum @@ -0,0 +1,14 @@ +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/packages/@jsii/go-runtime/jsii-calc-test/main_test.go b/packages/@jsii/go-runtime-test/project/main_test.go similarity index 90% rename from packages/@jsii/go-runtime/jsii-calc-test/main_test.go rename to packages/@jsii/go-runtime-test/project/main_test.go index 494b163518..fb91f8103b 100644 --- a/packages/@jsii/go-runtime/jsii-calc-test/main_test.go +++ b/packages/@jsii/go-runtime-test/project/main_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "fmt" @@ -236,24 +236,3 @@ func TestReturnsSpecialParam(t *testing.T) { t.Errorf("Expected type: %s; Actual: %s", expected, actual) } } - -func TestMaps(t *testing.T) { - allTypes := calc.NewAllTypes() - actual := allTypes.MapProperty() - if len(actual) != 0 { - t.Errorf("Expected length of empty map to be 0. Got: %d", len(actual)) - } - - question := "The answer to the ultimate question of life, the universe, and everything" - answer := calclib.NewNumber(42) - allTypes.SetMapProperty(map[string]calclib.Number{ - question: answer, - }) - actual = allTypes.MapProperty() - if len(actual) != 1 { - t.Errorf("Expected length of empty map to be 1. Got: %d", len(actual)) - } - if actual[question].Value() != answer.Value() { - t.Errorf("Expected to have the value %v in there, got: %v", answer, actual[question]) - } -} diff --git a/packages/@jsii/go-runtime-test/tsconfig.json b/packages/@jsii/go-runtime-test/tsconfig.json new file mode 100644 index 0000000000..5228b0f5a3 --- /dev/null +++ b/packages/@jsii/go-runtime-test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig-base", + "include": ["**/*.ts"] +} diff --git a/packages/@jsii/go-runtime/build-tools/all-go.ts b/packages/@jsii/go-runtime/build-tools/all-go.ts deleted file mode 100644 index 0ffdc7fa6d..0000000000 --- a/packages/@jsii/go-runtime/build-tools/all-go.ts +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env node - -import { readdirSync, statSync } from 'fs'; -import { join } from 'path'; -import { cwd } from 'process'; - -import { runCommand } from './_constants'; - -/** - * Recursively searches the provided directory for `go.mod` files (excluding - * sub-directories named `node_modules` and `jsii-calc`), and executes the - * supplied go command in the parent directory of eacu such file. - * - * @example - * # Running "go fmt" on all modules: - * $ all-go.js fmt ./... - * - * # Running "go test" in all modules: - * $ all-go.js test ./... - */ -function visit(dir: string): void { - for (const file of readdirSync(dir)) { - const path = join(dir, file); - if (statSync(path).isDirectory()) { - if (file !== 'node_modules' && file !== 'jsii-calc') { - visit(path); - } - continue; - } - if (file === 'go.mod') { - const [cmd, ...args] = process.argv.slice(2); - console.error(`$ ${cmd} ${args.join(' ')} # ${path}`); - try { - runCommand(cmd, args, { cwd: dir, stdio: 'inherit' }); - } catch (e) { - console.error(e.message); - process.exit(-1); - } - } - } -} - -visit(cwd()); diff --git a/packages/@jsii/go-runtime/go.mod b/packages/@jsii/go-runtime/go.mod index 0240163ba9..3fc06ea17d 100644 --- a/packages/@jsii/go-runtime/go.mod +++ b/packages/@jsii/go-runtime/go.mod @@ -3,17 +3,6 @@ module github.com/aws/jsii go 1.15 require ( - github.com/aws/jsii-runtime-go v0.0.0 - github.com/aws/jsii/jsii-calc/go/jsiicalc/v3 v3.20.120 - github.com/aws/jsii/jsii-calc/go/scopejsiicalclib v0.0.0 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 golang.org/x/tools v0.1.0 ) - -replace ( - github.com/aws/jsii-runtime-go => ./jsii-runtime-go - github.com/aws/jsii/jsii-calc/go/jsiicalc/v3 => ./jsii-calc/go/jsiicalc - github.com/aws/jsii/jsii-calc/go/scopejsiicalcbase => ./jsii-calc/go/scopejsiicalcbase - github.com/aws/jsii/jsii-calc/go/scopejsiicalcbaseofbase/v2 => ./jsii-calc/go/scopejsiicalcbaseofbase - github.com/aws/jsii/jsii-calc/go/scopejsiicalclib => ./jsii-calc/go/scopejsiicalclib -) diff --git a/packages/@jsii/go-runtime/go.sum b/packages/@jsii/go-runtime/go.sum index d1f39904fc..c2d920e438 100644 --- a/packages/@jsii/go-runtime/go.sum +++ b/packages/@jsii/go-runtime/go.sum @@ -1,5 +1,3 @@ -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/packages/@jsii/go-runtime/package.json b/packages/@jsii/go-runtime/package.json index ae2e8a55a2..cd9fd20c85 100644 --- a/packages/@jsii/go-runtime/package.json +++ b/packages/@jsii/go-runtime/package.json @@ -6,16 +6,14 @@ "main": "lib/index.js", "scripts": { "build": "tsc --build && npm run gen:rt", - "doc": "yarn --silent go:run godoc", - "fmt": "yarn --silent go:all yarn --silent go:run goimports .", - "gen:calc": "node build-tools/gen-calc.js", "gen:rt": "node build-tools/gen.js", - "generate": "npm run gen:rt && npm run gen:calc", - "go:all": "node build-tools/all-go.js", + "generate": "npm run gen:rt", "go:run": "go mod download && node build-tools/go-run.js", "package": "build-tools/package.sh", - "lint": "yarn --silent go:all go vet ./... && yarn --silent go:all yarn --silent go:run golint ./...", - "test": "npm run gen:calc && yarn --silent go:all go test ./..." + "doc": "(cd jsii-runtime-go && yarn --silent go:run godoc)", + "fmt": "(cd jsii-runtime-go && yarn --silent go:all yarn --silent go:run goimports .)", + "lint": "(cd jsii-runtime-go && go vet ./... && yarn --silent go:run golint ./...)", + "test": "(cd jsii-runtime-go && go test ./...)" }, "keywords": [], "author": "", @@ -30,7 +28,6 @@ "eslint": "^7.20.0", "fs-extra": "^9.1.0", "jsii-calc": "^3.20.120", - "jsii-pacmak": "^0.0.0", "prettier": "^2.2.1", "ts-node": "^9.1.1", "typescript": "~3.9.9" diff --git a/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/ComplianceSuiteHarness.java b/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/ComplianceSuiteHarness.java index 2a728d7791..2271b4d817 100644 --- a/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/ComplianceSuiteHarness.java +++ b/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/ComplianceSuiteHarness.java @@ -39,8 +39,7 @@ public void beforeEach(final ExtensionContext extensionContext) throws Exception @Override public void afterEach(final ExtensionContext extensionContext) { JsiiRuntime.messageInspector.remove(); - final ObjectNode entry = result.putObject(String.format("%s.%s", - extensionContext.getRequiredTestClass().getSimpleName(), extensionContext.getRequiredTestMethod().getName())); + final ObjectNode entry = result.putObject(extensionContext.getRequiredTestMethod().getName()); entry.put("status", extensionContext.getExecutionException().isPresent() ? "failure" : "success"); entry.putPOJO("kernelTrace", kernelTraces.remove(extensionContext.getUniqueId())); } diff --git a/superchain/Dockerfile b/superchain/Dockerfile index 78d2f7044a..e156799403 100644 --- a/superchain/Dockerfile +++ b/superchain/Dockerfile @@ -75,6 +75,8 @@ RUN curl -sL https://golang.org/dl/go1.16.linux-amd64.tar.gz -o /tmp/go.tar.gz && mkdir -p /usr/local && (cd /usr/local && tar -xzf /tmp/go.tar.gz) ENV PATH="$GOROOT/bin:$PATH" +# Install GCC +RUN yum -y install gcc # Install Docker RUN amazon-linux-extras install docker \ diff --git a/tools/jsii-compliance/.eslintrc.yaml b/tools/jsii-compliance/.eslintrc.yaml new file mode 100644 index 0000000000..8ca30cb87b --- /dev/null +++ b/tools/jsii-compliance/.eslintrc.yaml @@ -0,0 +1,2 @@ +--- +extends: ../../eslint-config.yaml diff --git a/tools/jsii-compliance/README.md b/tools/jsii-compliance/README.md new file mode 100644 index 0000000000..a31619cb38 --- /dev/null +++ b/tools/jsii-compliance/README.md @@ -0,0 +1,65 @@ +# jsii compliance + +This directory contains scripts and resources to create and validate the jsii compliance suite. + +### Compliance Suite + +The compliance suite is defined as a collection of abstract test cases, that each language binding must implement individualy. +Language specific exclusions may be added to each test separately, or to the entire suite. + +> See [suite.ts](./suite.ts). + +Note that if you add a compliance test to a specific language binding only, i.e without adding it to the suite definition, the build will fail. For example: + +```console +Test 'REMOVEME' from golang report does not exist in the compliance suite. Please add it to the suite definition. +``` + +### Compliance Report + +Each language binding is responsible for creating a language specific report during the `test` phase of our build. + +The report takes the following form: + +```json +{ + "": { + "status": "success | skipped" + } +} +``` + +The `` must match the test case name in the suite definition. For example, the following test case definition: + +```json +{ + "name": "maps", + "description": "", + "exclusions": {} +}, +``` + +Requires that each language report to include an entry with a key called `maps`. + +> Note that matching is **not case sensitive**, which means you can and should adhere to the +> conventions of your specific language when writing the tests. + +These reports are then aggregated into a single multi-language compliance report. It is generated during build time, +and should be checked into [source control](../../gh-pages/content/specification/6-compliance-report.md). + +> The report is generated automatically as part of the global `test` phase of the repository. You can also directly generate the report by running `yarn compliance` from the top level directory of the repo, or `yarn report` from this package directory. + +Note that if you add a test and don't re-generate the report, the build will fail: + +```console +gh-pages/content/specification/6-compliance-report.md: needs update +``` + +The report contains the state of each test with respect to each language binding. +Every test will have one of the following statuses: + +- ✅ - Test passes. +- ❌ - Test is either missing or is not reporting success. +- N/A - The test was excluded. + +Eventually, this report lands on our [docs site](https://aws.github.io/jsii/specification/6-compliance-report/). diff --git a/tools/jsii-compliance/package.json b/tools/jsii-compliance/package.json new file mode 100644 index 0000000000..6f41c75012 --- /dev/null +++ b/tools/jsii-compliance/package.json @@ -0,0 +1,24 @@ +{ + "name": "jsii-compliance", + "version": "0.0.0", + "private": true, + "description": "", + "scripts": { + "report": "ts-node report.ts", + "lint": "eslint . --ext .js,.ts", + "lint:fix": "yarn lint --fix" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "dependencies": { + "tablemark": "^2.0.0" + }, + "devDependencies": { + "@types/node": "^10.17.52", + "eslint": "^7.20.0", + "prettier": "^2.2.1", + "ts-node": "^9.1.1", + "typescript": "~3.9.9" + } +} diff --git a/tools/jsii-compliance/report.ts b/tools/jsii-compliance/report.ts new file mode 100755 index 0000000000..af5fd079ba --- /dev/null +++ b/tools/jsii-compliance/report.ts @@ -0,0 +1,220 @@ +#!/usr/bin/env npx ts-node + +import * as fs from 'fs'; +import * as path from 'path'; + +import * as schema from './schema'; +import { suite } from './suite'; + +// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires +const tablemark = require('tablemark'); + +const SUCCESS = '✅'; +const FAILURE = '❌'; +const NA = 'N/A'; + +/** + * Determine whether or not a test case should be excluded from a specific language. + * An exclusion happens either when the entire language binding is excluded, or a specific test case. + * + * @param test the test. + * @param suite the suite. + * @param language the language. + */ +function isExcluded( + testCase: schema.TestCase, + suite: schema.Suite, + language: string, +) { + const testExcluded = !!testCase.exclusions?.[language]; + const bindingExcluded = !!suite.exclusions?.[language]; + return testExcluded || bindingExcluded; +} + +/** + * Determines the status of a specific test case with respect to a specific language. + * + * @param testCase the test case. + * @param language test language. + * @param reports the reports collected from all language bindings. + */ +function determineTestStatus( + testCase: schema.TestCase, + language: string, + reports: Record, +) { + const report = reports[language]; + const testResult = report?.[normalizeTestName(testCase.name)]; + + if (!testResult) return FAILURE; + return testResult.status === 'success' ? SUCCESS : FAILURE; +} + +/** + * Given a test name, normalize it so it can be compared across different language bindings. + * + * @param testName the test name. + */ +function normalizeTestName(testName: string): string { + return testName.toUpperCase(); +} + +/** + * Given a compliance report, normalize its test names so they are comparable to the + * tests defined in the suite. + * + * @param report report + */ +function normalizeReport(report: schema.Report): schema.Report { + const normalized: schema.Report = {}; + for (const [testName, _report] of Object.entries(report)) { + normalized[normalizeTestName(testName)] = _report; + } + return normalized; +} + +/** + * Validates that a language specific compliance report doesn't violate the suite. + * + * Possible violations are: + * + * - A test exist in the report that doesn't exist in the suite definition. + * + * @param report the report. + * @param language the language. + * @param suite the suite. + * + * @returns A list of validation errors. + */ +function validateReport( + report: schema.Report, + language: string, + suite: schema.Suite, +): string[] { + const testsInReport = Object.keys(report); + + const testsInSuite = suite.testCases.map((t: schema.TestCase) => + normalizeTestName(t.name), + ); + + const errors: string[] = []; + + // make sure every test in the language report exist in the suite. + // this prevents us from adding tests only to a specific language. + for (const test of testsInReport) { + if (!testsInSuite.includes(test)) { + errors.push( + `Test '${test}' from ${language} report does not exist in the compliance suite. If this test is language specific, + move it out of the compliance test, otherwise, add the test to the compliance suite definition.`, + ); + } + } + + return errors; +} + +/** + * Collect all the individual reports into a single collection. Ignores bindings that are missing their report file. + * + * @param suite the compliance suite. + */ +function collectReports(suite: schema.Suite): Record { + const reports: Record = {}; + for (const [language, binding] of Object.entries(suite.bindings)) { + const reportFile = path.join(__dirname, '..', '..', binding.report); + console.log(`Collecting ${language} report from: ${reportFile}`); + if (fs.existsSync(reportFile)) { + reports[language] = normalizeReport( + JSON.parse(fs.readFileSync(reportFile, 'utf-8')), + ); + } + } + return reports; +} + +console.log('Collecting individual lanaguage binding reports'); +const reports = collectReports(suite); + +console.log('Validating reports'); +const errors = []; +for (const [language, report] of Object.entries(reports)) { + errors.push(...validateReport(report, language, suite)); +} + +if (errors.length > 0) { + console.error('Found multiple validation errors:'); + for (const error of errors) { + console.error(error); + } + process.exit(1); +} + +console.log('Creating aggregated report'); + +const rows = new Array>(); +const successes: Record = {}; + +for (const [i, testCase] of suite.testCases.entries()) { + const row: Record = { + number: `${i + 1}`, + test: testCase.name, + description: testCase.description, + }; + + for (const language of Object.keys(suite.bindings)) { + const status = isExcluded(testCase, suite, language) + ? NA + : determineTestStatus(testCase, language, reports); + row[language] = status; + + if (status === SUCCESS) { + successes[language] = + successes[language] === undefined ? 1 : successes[language] + 1; + } + } + + rows.push(row); +} + +const columns = ['number', 'test', 'description']; + +for (const language of Object.keys(reports)) { + const coverage = ( + (successes[language] / suite.testCases.length) * + 100 + ).toFixed(2); + columns.push(`${language} (${coverage}%)`); +} + +const target = path.join( + __dirname, + '..', + '..', + 'gh-pages', + 'content', + 'specification', + '6-compliance-report.md', +); +const header = ` + +# Compliance Report + +This section details the current state of each language binding with respect to our standard compliance suite.`; + +let exclusions = undefined; + +if (suite.exclusions) { + exclusions = `!!! Note + The following languages are currently excluded from the tests and are marked as N/A:`; + for (const language of Object.keys(suite.exclusions ?? {})) { + exclusions = `${exclusions}\n\n **${language}**: ${suite.exclusions[language].reason}`; + } + exclusions = `${exclusions}\n\n`; +} + +fs.writeFileSync( + target, + `${header}\n\n${exclusions}${tablemark(rows, { columns })}`, +); + +console.log(`Report written to ${target}`); diff --git a/tools/jsii-compliance/schema.ts b/tools/jsii-compliance/schema.ts new file mode 100644 index 0000000000..292346d563 --- /dev/null +++ b/tools/jsii-compliance/schema.ts @@ -0,0 +1,99 @@ +/** + * Compliance suite definition. + */ +export interface Suite { + /** + * Suite name. + */ + readonly name: string; + + /** + * Suite description. + */ + readonly description: string; + + /** + * Language bindings the suite applies to. + */ + readonly bindings: Record; + + /** + * Language bindings exclusions. + */ + readonly exclusions?: Record; + + /** + * A list of test cases the suite enforces. + */ + readonly testCases: TestCase[]; +} + +/** + * Specific test case. + */ +export interface TestCase { + /** + * Test case name. + */ + readonly name: string; + + /** + * Test case description. + */ + readonly description: string; + + /** + * Language specific exclusions for a test case. + */ + readonly exclusions?: Record; +} + +/** + * Language binding. + */ +export interface Binding { + /** + * Location of the language specific report. + */ + readonly report: string; +} + +/** + * Exclusion of a specific language binding. + */ +export interface BindingExclusion { + /** + * Reason for exclusion. + */ + readonly reason: string; +} + +/** + * Exclusion of a specific test from a specific binding. + */ +export interface TestExclusion { + /** + * The language to exclude the test from. + */ + readonly language: string; + + /** + * The exclusion reason. + */ + readonly reason: string; +} + +/** + * Inidividual test result. + */ +export interface TestResult { + /** + * Status of execution. + */ + readonly status: 'success' | 'skipped'; +} + +/** + * Language specific compliance report. + */ +export type Report = Record; diff --git a/tools/jsii-compliance/suite.ts b/tools/jsii-compliance/suite.ts new file mode 100644 index 0000000000..e24e77b365 --- /dev/null +++ b/tools/jsii-compliance/suite.ts @@ -0,0 +1,618 @@ +import * as schema from './schema'; + +export const suite: schema.Suite = { + name: 'standard', + description: + 'JSII standard compliance test suite. These tests must be implemented in each language binding.', + bindings: { + java: { + report: 'packages/@jsii/java-runtime-test/project/compliance-report.json', + }, + golang: { + report: 'packages/@jsii/go-runtime-test/project/compliance-report.json', + }, + dotnet: { + report: 'packages/@jsii/dotnet-runtime-test/compliance-report.json', + }, + python: { + report: 'packages/@jsii/python-runtime-test/compliance-report.json', + }, + }, + exclusions: { + dotnet: { + reason: + 'Underwent a different compliance process. Will be aligned in the future.', + }, + python: { + reason: + 'Underwent a different compliance process. Will be aligned in the future.', + }, + }, + testCases: [ + { + name: 'asyncOverrides_overrideCallsSuper', + description: '', + exclusions: {}, + }, + { + name: 'arrayReturnedByMethodCanBeRead', + description: '', + exclusions: {}, + }, + { + name: 'unionProperties', + description: '', + exclusions: {}, + }, + { + name: 'syncOverrides', + description: '', + exclusions: {}, + }, + { + name: 'useEnumFromScopedModule', + description: '', + exclusions: {}, + }, + { + name: 'createObjectAndCtorOverloads', + description: '', + exclusions: {}, + }, + { + name: 'fail_syncOverrides_callsDoubleAsync_method', + description: '', + exclusions: {}, + }, + { + name: 'collectionOfInterfaces_MapOfStructs', + description: '', + exclusions: {}, + }, + { + name: 'asyncOverrides_overrideAsyncMethod', + description: '', + exclusions: {}, + }, + { + name: 'statics', + description: '', + exclusions: {}, + }, + { + name: 'structs_returnedLiteralEqualsNativeBuilt', + description: '', + exclusions: {}, + }, + { + name: 'classesCanSelfReferenceDuringClassInitialization', + description: '', + exclusions: {}, + }, + { + name: 'canObtainStructReferenceWithOverloadedSetter', + description: '', + exclusions: {}, + }, + { + name: 'callbacksCorrectlyDeserializeArguments', + description: '', + exclusions: {}, + }, + { + name: 'canUseInterfaceSetters', + description: '', + exclusions: {}, + }, + { + name: 'propertyOverrides_interfaces', + description: '', + exclusions: {}, + }, + { + name: 'syncOverrides_callsSuper', + description: '', + exclusions: {}, + }, + { + name: 'testJsiiAgent', + description: '', + exclusions: {}, + }, + { + name: 'doNotOverridePrivates_method_private', + description: '', + exclusions: {}, + }, + { + name: 'pureInterfacesCanBeUsedTransparently', + description: '', + exclusions: {}, + }, + { + name: 'nullShouldBeTreatedAsUndefined', + description: '', + exclusions: {}, + }, + { + name: 'primitiveTypes', + description: '', + exclusions: {}, + }, + { + name: 'reservedKeywordsAreSlugifiedInClassProperties', + description: '', + exclusions: {}, + }, + { + name: 'objectIdDoesNotGetReallocatedWhenTheConstructorPassesThisOut', + description: '', + exclusions: {}, + }, + { + name: 'interfaceBuilder', + description: '', + exclusions: {}, + }, + { + name: 'unionTypes', + description: '', + exclusions: {}, + }, + { + name: 'arrays', + description: '', + exclusions: {}, + }, + { + name: 'staticMapInClassCannotBeModified', + description: '', + exclusions: {}, + }, + { + name: 'consts', + description: '', + exclusions: {}, + }, + { + name: 'pureInterfacesCanBeUsedTransparently_WhenTransitivelyImplementing', + description: '', + exclusions: {}, + }, + { + name: 'reservedKeywordsAreSlugifiedInMethodNames', + description: '', + exclusions: {}, + }, + { + name: 'exceptions', + description: '', + exclusions: {}, + }, + { + name: 'testLiteralInterface', + description: '', + exclusions: {}, + }, + { + name: 'structs_nonOptionalhashCode', + description: '', + exclusions: {}, + }, + { + name: 'propertyOverrides_set_throws', + description: '', + exclusions: {}, + }, + { + name: 'canLeverageIndirectInterfacePolymorphism', + description: '', + exclusions: {}, + }, + { + name: 'fluentApi', + description: '', + exclusions: {}, + }, + { + name: 'staticListInClassCanBeReadCorrectly', + description: '', + exclusions: {}, + }, + { + name: 'mapReturnedByMethodCannotBeModified', + description: '', + exclusions: {}, + }, + { + name: 'receiveInstanceOfPrivateClass', + description: '', + exclusions: {}, + }, + { + name: 'staticMapInClassCanBeReadCorrectly', + description: '', + exclusions: {}, + }, + { + name: 'testNativeObjectsWithInterfaces', + description: '', + exclusions: {}, + }, + { + name: 'doNotOverridePrivates_property_getter_public', + description: '', + exclusions: {}, + }, + { + name: 'equalsIsResistantToPropertyShadowingResultVariable', + description: '', + exclusions: {}, + }, + { + name: 'listInClassCanBeReadCorrectly', + description: '', + exclusions: {}, + }, + { + name: 'useNestedStruct', + description: '', + exclusions: {}, + }, + { + name: 'testFluentApiWithDerivedClasses', + description: '', + exclusions: {}, + }, + { + name: 'interfacesCanBeUsedTransparently_WhenAddedToJsiiType', + description: '', + exclusions: {}, + }, + { + name: 'canOverrideProtectedGetter', + description: '', + exclusions: {}, + }, + { + name: 'getAndSetEnumValues', + description: '', + exclusions: {}, + }, + { + name: 'structs_nonOptionalequals', + description: '', + exclusions: {}, + }, + { + name: 'testInterfaceParameter', + description: '', + exclusions: {}, + }, + { + name: 'liftedKwargWithSameNameAsPositionalArg', + description: '', + exclusions: {}, + }, + { + name: 'creationOfNativeObjectsFromJavaScriptObjects', + description: '', + exclusions: {}, + }, + { + name: 'canOverrideProtectedMethod', + description: '', + exclusions: {}, + }, + { + name: 'canLoadEnumValues', + description: '', + exclusions: {}, + }, + { + name: 'eraseUnsetDataValues', + description: '', + exclusions: {}, + }, + { + name: 'maps', + description: '', + exclusions: {}, + }, + { + name: 'structs_containsNullChecks', + description: '', + exclusions: {}, + }, + { + name: 'canOverrideProtectedSetter', + description: '', + exclusions: {}, + }, + { + name: 'asyncOverrides_callAsyncMethod', + description: '', + exclusions: {}, + }, + { + name: 'nodeStandardLibrary', + description: '', + exclusions: {}, + }, + { + name: 'dates', + description: '', + exclusions: {}, + }, + { + name: 'collectionOfInterfaces_ListOfStructs', + description: '', + exclusions: {}, + }, + { + name: 'objRefsAreLabelledUsingWithTheMostCorrectType', + description: '', + exclusions: {}, + }, + { + name: 'unionPropertiesWithBuilder', + description: '', + exclusions: {}, + }, + { + name: 'doNotOverridePrivates_property_getter_private', + description: '', + exclusions: {}, + }, + { + name: 'structs_withDiamondInheritance_correctlyDedupeProperties', + description: '', + exclusions: {}, + }, + { + name: 'abstractMembersAreCorrectlyHandled', + description: '', + exclusions: {}, + }, + { + name: 'doNotOverridePrivates_property_by_name_private', + description: '', + exclusions: {}, + }, + { + name: 'testNullIsAValidOptionalMap', + description: '', + exclusions: {}, + }, + { + name: 'mapReturnedByMethodCanBeRead', + description: '', + exclusions: {}, + }, + { + name: 'structs_multiplePropertiesEquals', + description: '', + exclusions: {}, + }, + { + name: 'mapInClassCanBeReadCorrectly', + description: '', + exclusions: {}, + }, + { + name: 'staticListInClassCannotBeModified', + description: '', + exclusions: {}, + }, + { + name: 'collectionOfInterfaces_MapOfInterfaces', + description: '', + exclusions: {}, + }, + { + name: 'asyncOverrides_overrideThrows', + description: '', + exclusions: {}, + }, + { + name: 'callMethods', + description: '', + exclusions: {}, + }, + { + name: 'returnAbstract', + description: '', + exclusions: {}, + }, + { + name: 'dynamicTypes', + description: '', + exclusions: {}, + }, + { + name: 'hashCodeIsResistantToPropertyShadowingResultVariable', + description: '', + exclusions: {}, + }, + { + name: 'returnSubclassThatImplementsInterface976', + description: '', + exclusions: {}, + }, + { + name: 'structs_optionalEquals', + description: '', + exclusions: {}, + }, + { + name: 'propertyOverrides_get_calls_super', + description: '', + exclusions: {}, + }, + { + name: 'unmarshallIntoAbstractType', + description: '', + exclusions: {}, + }, + { + name: 'structs_multiplePropertiesHashCode', + description: '', + exclusions: {}, + }, + { + name: 'fail_syncOverrides_callsDoubleAsync_propertyGetter', + description: '', + exclusions: {}, + }, + { + name: 'propertyOverrides_get_set', + description: '', + exclusions: {}, + }, + { + name: 'variadicMethodCanBeInvoked', + description: '', + exclusions: {}, + }, + { + name: 'collectionTypes', + description: '', + exclusions: {}, + }, + { + name: 'asyncOverrides_overrideAsyncMethodByParentClass', + description: '', + exclusions: {}, + }, + { + name: 'structs_optionalHashCode', + description: '', + exclusions: {}, + }, + { + name: 'testStructsCanBeDowncastedToParentType', + description: '', + exclusions: {}, + }, + { + name: 'propertyOverrides_get_throws', + description: '', + exclusions: {}, + }, + { + name: 'getSetPrimitiveProperties', + description: '', + exclusions: {}, + }, + { + name: 'getAndSetNonPrimitiveProperties', + description: '', + exclusions: {}, + }, + { + name: 'reservedKeywordsAreSlugifiedInStructProperties', + description: '', + exclusions: {}, + }, + { + name: 'fail_syncOverrides_callsDoubleAsync_propertySetter', + description: '', + exclusions: {}, + }, + { + name: 'doNotOverridePrivates_method_public', + description: '', + exclusions: {}, + }, + { + name: 'testNullIsAValidOptionalList', + description: '', + exclusions: {}, + }, + { + name: 'mapInClassCannotBeModified', + description: '', + exclusions: {}, + }, + { + name: 'doNotOverridePrivates_property_by_name_public', + description: '', + exclusions: {}, + }, + { + name: 'asyncOverrides_twoOverrides', + description: '', + exclusions: {}, + }, + { + name: 'propertyOverrides_set_calls_super', + description: '', + exclusions: {}, + }, + { + name: 'iso8601DoesNotDeserializeToDate', + description: '', + exclusions: {}, + }, + { + name: 'collectionOfInterfaces_ListOfInterfaces', + description: '', + exclusions: {}, + }, + { + name: 'undefinedAndNull', + description: '', + exclusions: {}, + }, + { + name: 'structs_serializeToJsii', + description: '', + exclusions: {}, + }, + { + name: 'structsAreUndecoratedOntheWayToKernel', + description: '', + exclusions: {}, + }, + { + name: 'canObtainReferenceWithOverloadedSetter', + description: '', + exclusions: {}, + }, + { + name: 'testJSObjectLiteralToNative', + description: '', + exclusions: {}, + }, + { + name: 'structs_stepBuilders', + description: '', + exclusions: {}, + }, + { + name: 'classWithPrivateConstructorAndAutomaticProperties', + description: '', + exclusions: {}, + }, + { + name: 'arrayReturnedByMethodCannotBeModified', + description: '', + exclusions: {}, + }, + { + name: 'correctlyDeserializesStructUnions', + description: '', + exclusions: {}, + }, + { + name: 'subclassing', + description: '', + exclusions: {}, + }, + { + name: 'testInterfaces', + description: '', + exclusions: {}, + }, + ], +}; diff --git a/tools/jsii-compliance/tsconfig.json b/tools/jsii-compliance/tsconfig.json new file mode 100644 index 0000000000..3541c14c28 --- /dev/null +++ b/tools/jsii-compliance/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig-base.json", + "include": ["**/*.ts"] +} diff --git a/yarn.lock b/yarn.lock index 9ce0f7b494..9a869374ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4385,6 +4385,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= + get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -5820,6 +5825,13 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json-to-markdown-table@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-to-markdown-table/-/json-to-markdown-table-1.0.0.tgz#2b3c6af550d642c1d72753bc01b6b93dc40b7b39" + integrity sha1-Kzxq9VDWQsHXJ1O8Aba5PcQLezk= + dependencies: + lodash "^4.16.4" + json5@2.x, json5@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" @@ -6086,6 +6098,11 @@ lodash@4.x, lodash@^4.11.2, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.19, l resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lodash@^4.16.4: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log4js@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" @@ -6105,6 +6122,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -6526,6 +6548,13 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + node-fetch-npm@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" @@ -7876,6 +7905,14 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +sentence-case@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" + integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + serialize-javascript@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" @@ -8154,6 +8191,14 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split-text-to-chunks@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/split-text-to-chunks/-/split-text-to-chunks-1.0.0.tgz#9b9bd2b8530e18b09697b1b8ca4485d31608eeb7" + integrity sha512-HLtEwXK/T4l7QZSJ/kOSsZC0o5e2Xg3GzKKFxm0ZexJXw0Bo4CaEl39l7MCSRHk9EOOL5jT8JIDjmhTtcoe6lQ== + dependencies: + get-stdin "^5.0.1" + minimist "^1.2.0" + split2@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" @@ -8483,6 +8528,14 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" +tablemark@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tablemark/-/tablemark-2.0.0.tgz#8eb0db5743d24d1f1d795c3ecd05c28f9c55f635" + integrity sha512-bgvShWeSUIWO4j6NH7wGrn1xUQMJ/LRZFIRWE6ka5CFNfQ5qOP9FB4amKCurUUr7C0K4AJGHYDLkaf8A6t5Aww== + dependencies: + sentence-case "^2.1.1" + split-text-to-chunks "^1.0.0" + tapable@^2.1.1, tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" @@ -8940,6 +8993,18 @@ upath@^1.2.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"