Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.kson.jsonsuite

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
Expand Down Expand Up @@ -273,10 +274,10 @@ ${ tests.joinToString("\n") {
| $schemaComment
| assertKsonEnforcesSchema(
| ${"\"\"\""}
| ${formatForTest(test.data)}
| ${formatForTest(test.data, " ")}
| ${"\"\"\""},
| ${"\"\"\""}
| ${formatForTest(schema.schema)}
| ${formatForTest(schema.schema, " ")}
| ${"\"\"\""},
| ${test.valid},
| ${"\"\"\""}${formatForTest(schema.description)} -> ${formatForTest(test.description)}${"\"\"\""})
Expand Down Expand Up @@ -362,10 +363,16 @@ private class SchemaTestDataLoader(private val testDefinitionFilesDir: Path, pri
}
}

private val prettyPrintingJson = Json { prettyPrint = true }

private fun formatForTest(jsonElement: JsonElement): String {
return formatForTest(jsonElement.toString())
@OptIn(ExperimentalSerializationApi::class)
private val prettyPrintingJson = Json {
prettyPrint = true
// 4 spaces for indentation
prettyPrintIndent = " "
}
private fun formatForTest(jsonElement: JsonElement, indent: String): String {
return formatForTest(prettyPrintingJson.encodeToString(JsonElement.serializer(), jsonElement))
.split("\n")
.joinToString(separator = "\n$indent")
}

private fun formatForTest(string: String): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,122 +2,40 @@ package org.kson.jsonsuite

/**
* This is the list of [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite)
* tests which test as-yet unimplemented aspects of our Json Schema support.
*
* TODO: once Json Schema support is fully implemented, this list should be empty and hence deleted.
* This will likely be done in an iterative fashion, eliminating the exclusions here for few tests at a time
* as support is filled out
* tests which should not be run as part of test suite. See comments on specific exclusions for more details.
*/
fun schemaTestSuiteExclusions() = setOf(
"infinite_loop_detection_evaluatingTheSameSchemaLocationAgainstTheSameDataLocationTwiceIsNotASignOfAnInfiniteLoop_passingCase",
"infinite_loop_detection_evaluatingTheSameSchemaLocationAgainstTheSameDataLocationTwiceIsNotASignOfAnInfiniteLoop_failingCase",
"definitions_validateDefinitionAgainstMetaschema_validDefinitionSchema",
"definitions_validateDefinitionAgainstMetaschema_invalidDefinitionSchema",
"items_itemsAndSubitems_fewerItemsIsValid",
"items_itemsAndSubitems_tooManyItems",
"items_itemsAndSubitems_tooManySub_items",
"items_itemsAndSubitems_validItems",
"items_itemsAndSubitems_wrongItem",
"items_itemsAndSubitems_wrongSub_item",
"refRemote_______refTo______refFindsLocation_independent______id_non_numberIsInvalid",
"refRemote_______refTo______refFindsLocation_independent______id_numberIsValid",
"refRemote_baseURIChange_ChangeFolderInSubschema_numberIsValid",
"refRemote_baseURIChange_ChangeFolderInSubschema_stringIsInvalid",
"refRemote_baseURIChange_ChangeFolder_numberIsValid",
"refRemote_baseURIChange_ChangeFolder_stringIsInvalid",

/**
* These excludes are all tests which require fetching remote schemas. We do not want to support fetching
* right now and so require that schemas be [bundled](https://json-schema.org/blog/posts/bundling-json-schema-compound-documents)
* before they are passed to Kson.
*
* We test bundled versions of these test in [org.kson.schema.JsonSchemaTestBundledRemotes].
*/
"refRemote_baseURIChange_baseURIChangeRefInvalid",
"refRemote_baseURIChange_baseURIChangeRefValid",
"refRemote_baseURIChange_ChangeFolder_numberIsValid",
"refRemote_baseURIChange_ChangeFolder_stringIsInvalid",
"refRemote_baseURIChange_ChangeFolderInSubschema_numberIsValid",
"refRemote_baseURIChange_ChangeFolderInSubschema_stringIsInvalid",
"refRemote_fragmentWithinRemoteRef_remoteFragmentInvalid",
"refRemote_fragmentWithinRemoteRef_remoteFragmentValid",
"refRemote_location_independentIdentifierInRemoteRef_integerIsValid",
"refRemote_location_independentIdentifierInRemoteRef_stringIsInvalid",
"refRemote_refWithinRemoteRef_refWithinRefInvalid",
"refRemote_refWithinRemoteRef_refWithinRefValid",
"refRemote_remoteRefWithRefToDefinitions_invalid",
"refRemote_remoteRefWithRefToDefinitions_valid",
"refRemote_remoteRef_remoteRefInvalid",
"refRemote_remoteRef_remoteRefValid",
"refRemote_remoteRefWithRefToDefinitions_invalid",
"refRemote_remoteRefWithRefToDefinitions_valid",
"refRemote_retrievedNestedRefsResolveRelativeToTheirURINot______id_numberIsInvalid",
"refRemote_retrievedNestedRefsResolveRelativeToTheirURINot______id_stringIsValid",
"refRemote_rootRefInRemoteRef_nullIsValid",
"refRemote_rootRefInRemoteRef_objectIsInvalid",
"refRemote_rootRefInRemoteRef_stringIsValid",
"ref_______idMustBeResolvedAgainstNearestParent_NotJustImmediateParent_non_numberIsInvalid",
"ref_______idMustBeResolvedAgainstNearestParent_NotJustImmediateParent_numberIsValid",
"ref_______idWithFileURIStillResolvesPointers_Windows_non_numberIsInvalid",
"ref_______idWithFileURIStillResolvesPointers_Windows_numberIsValid",
"ref_______idWithFileURIStillResolvesPointers__nix_non_numberIsInvalid",
"ref_______idWithFileURIStillResolvesPointers__nix_numberIsValid",
"ref_______refPreventsASibling______idFromChangingTheBaseUri_______refResolvesTo_definitions_base_foo_DataDoesNotValidate",
"ref_______refPreventsASibling______idFromChangingTheBaseUri_______refResolvesTo_definitions_base_foo_DataValidates",
"ref_______refToBooleanSchemaFalse_anyValueIsInvalid",
"ref_______refToBooleanSchemaTrue_anyValueIsValid",
"ref_emptyTokensIn______refJson_pointer_non_numberIsInvalid",
"ref_emptyTokensIn______refJson_pointer_numberIsValid",
"ref_escapedPointerRef_percentInvalid",
"ref_escapedPointerRef_percentValid",
"ref_escapedPointerRef_slashInvalid",
"ref_escapedPointerRef_slashValid",
"ref_escapedPointerRef_tildeInvalid",
"ref_escapedPointerRef_tildeValid",
"ref_location_independentIdentifierWithBaseURIChangeInSubschema_match",
"ref_location_independentIdentifierWithBaseURIChangeInSubschema_mismatch",
"ref_location_independentIdentifier_match",
"ref_location_independentIdentifier_mismatch",
"ref_naiveReplacementOf______refWithItsDestinationIsNotCorrect_doNotEvaluateThe______refInsideTheEnum_DefinitionExactMatch",
"ref_naiveReplacementOf______refWithItsDestinationIsNotCorrect_doNotEvaluateThe______refInsideTheEnum_MatchingAnyString",
"ref_naiveReplacementOf______refWithItsDestinationIsNotCorrect_matchTheEnumExactly",
"ref_nestedRefs_nestedRefInvalid",
"ref_nestedRefs_nestedRefValid",
"ref_propertyNamed______refThatIsNotAReference_propertyNamed______refInvalid",
"ref_propertyNamed______refThatIsNotAReference_propertyNamed______refValid",
"ref_propertyNamed______ref_ContainingAnActual______ref_propertyNamed______refInvalid",
"ref_propertyNamed______ref_ContainingAnActual______ref_propertyNamed______refValid",
"ref_recursiveReferencesBetweenSchemas_invalidTree",
"ref_recursiveReferencesBetweenSchemas_validTree",
"ref_refOverridesAnySiblingKeywords_refInvalid",
"ref_refOverridesAnySiblingKeywords_refValid",
"ref_refOverridesAnySiblingKeywords_refValid_MaxItemsIgnored",
"ref_refToElse_aNon_integerIsInvalidDueToThe______ref",
"ref_refToElse_anIntegerIsValid",
"ref_refToIf_aNon_integerIsInvalidDueToThe______ref",
"ref_refToIf_anIntegerIsValid",
"ref_refToThen_aNon_integerIsInvalidDueToThe______ref",
"ref_refToThen_anIntegerIsValid",
"ref_refWithAbsolute_path_reference_aStringIsValid",
"ref_refWithAbsolute_path_reference_anIntegerIsInvalid",
"ref_referenceAnAnchorWithANon_relativeURI_match",
"ref_referenceAnAnchorWithANon_relativeURI_mismatch",
"ref_refsWithQuote_objectWithNumbersIsValid",
"ref_refsWithQuote_objectWithStringsIsInvalid",
"ref_refsWithRelativeUrisAndDefs_invalidOnInnerField",
"ref_refsWithRelativeUrisAndDefs_invalidOnOuterField",
"ref_refsWithRelativeUrisAndDefs_validOnBothFields",
"ref_relativePointerRefToArray_matchArray",
"ref_relativePointerRefToArray_mismatchArray",
"ref_relativePointerRefToObject_match",
"ref_relativePointerRefToObject_mismatch",
"ref_relativeRefsWithAbsoluteUrisAndDefs_invalidOnInnerField",
"ref_relativeRefsWithAbsoluteUrisAndDefs_invalidOnOuterField",
"ref_relativeRefsWithAbsoluteUrisAndDefs_validOnBothFields",
"ref_remoteRef_ContainingRefsItself_remoteRefInvalid",
"ref_remoteRef_ContainingRefsItself_remoteRefValid",
"ref_rootPointerRef_match",
"ref_rootPointerRef_mismatch",
"ref_rootPointerRef_recursiveMatch",
"ref_rootPointerRef_recursiveMismatch",
"ref_simpleURNBaseURIWithJSONPointer_aNon_stringIsInvalid",
"ref_simpleURNBaseURIWithJSONPointer_aStringIsValid",
"ref_simpleURNBaseURIWith______refViaTheURN_invalidUnderTheURNIDedSchema",
"ref_simpleURNBaseURIWith______refViaTheURN_validUnderTheURNIDedSchema",
"ref_uRNBaseURIWithNSS_aNon_stringIsInvalid",
"ref_uRNBaseURIWithNSS_aStringIsValid",
"ref_uRNBaseURIWithQ_component_aNon_stringIsInvalid",
"ref_uRNBaseURIWithQ_component_aStringIsValid",
"ref_uRNBaseURIWithR_component_aNon_stringIsInvalid",
"ref_uRNBaseURIWithR_component_aStringIsValid",
"ref_uRNBaseURIWithURNAndAnchorRef_aNon_stringIsInvalid",
"ref_uRNBaseURIWithURNAndAnchorRef_aStringIsValid",
"ref_uRNBaseURIWithURNAndJSONPointerRef_aNon_stringIsInvalid",
"ref_uRNBaseURIWithURNAndJSONPointerRef_aStringIsValid",
"refRemote_______refTo______refFindsLocation_independent______id_non_numberIsInvalid",
"refRemote_______refTo______refFindsLocation_independent______id_numberIsValid",
)
7 changes: 6 additions & 1 deletion lib-kotlin/src/commonMain/kotlin/org/kson/Kson.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,13 @@ object Kson {
*/
fun parseSchema(schemaKson: String): SchemaResult {
val schemaParseResult = KsonCore.parseSchema(schemaKson)
val messages = publishMessages(schemaParseResult.messages)
val jsonSchema = schemaParseResult.jsonSchema
?: return SchemaResult.Failure(publishMessages(schemaParseResult.messages))
?: return SchemaResult.Failure(messages)

if (messages.isNotEmpty()) {
return SchemaResult.Failure(messages)
}

return SchemaResult.Success(SchemaValidator(jsonSchema))
}
Expand Down
2 changes: 1 addition & 1 deletion src/commonMain/kotlin/org/kson/KsonCore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ object KsonCore {
}

val messageSink = MessageSink()
val jsonSchema = SchemaParser.parseSchemaElement(ksonValue, messageSink)
val jsonSchema = SchemaParser.parseSchemaRoot(ksonValue, messageSink)
return SchemaParseResult(jsonSchema, messageSink.loggedMessages())
}

Expand Down
52 changes: 51 additions & 1 deletion src/commonMain/kotlin/org/kson/parser/messages/Message.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ enum class MessageSeverity {
* Instances of [Message] are created with [MessageType.create]. [Message]s can be created during Parsing or
* post-processing. Post-processing messages are created by any of the validators, for example
* [org.kson.validation.IndentValidator] or [org.kson.schema.JsonSchemaValidator].
*
*
* Core parse messages (created during lexing/parsing) are wrapped in [CoreParseMessage] when they pass through
* [org.kson.parser.KsonMarker.error]. All other messages from validators and post-processors remain unwrapped.
*/
Expand Down Expand Up @@ -729,6 +729,56 @@ enum class MessageType(
val keyName = parsedArgs.getArg("Key name")
return "Duplicate key \"$keyName\" in object"
}
},
JSON_POINTER_BAD_START {
override fun expectedArgs(): List<String> {
return listOf("Bad Start Character")
}

override fun doFormat(parsedArgs: ParsedErrorArgs): String {
val badStartChar = parsedArgs.getArg("Bad Start Character")
return "JSON Pointer must start with '/' but found '$badStartChar' at position 0"
}
},
JSON_POINTER_INVALID_CHARACTER {
override fun expectedArgs(): List<String> {
return listOf("Invalid Character")
}

override fun doFormat(parsedArgs: ParsedErrorArgs): String {
val invalidChar = parsedArgs.getArg("Invalid Character")
return "Invalid character in reference token: '$invalidChar'"
}
},
JSON_POINTER_INVALID_ESCAPE {
override fun expectedArgs(): List<String> {
return listOf("Invalid Escape Character")
}

override fun doFormat(parsedArgs: ParsedErrorArgs): String {
val invalidEscapeChar = parsedArgs.getArg("Invalid Escape Character")
return "Invalid escape sequence: '~$invalidEscapeChar'. " +
"Valid escape sequences are '~0' for '~' and '~1' for '/'"
}
},
JSON_POINTER_INCOMPLETE_ESCAPE {
override fun expectedArgs(): List<String> {
return emptyList()
}

override fun doFormat(parsedArgs: ParsedErrorArgs): String {
return "Incomplete escape sequence '~' at end of token. Must be '~0' or '~1'. "
}
},
SCHEMA_REF_RESOLUTION_FAILED {
override fun expectedArgs(): List<String> {
return listOf("Reference")
}

override fun doFormat(parsedArgs: ParsedErrorArgs): String {
val reference = parsedArgs.getArg("Reference")
return "Failed to resolve schema reference: $reference"
}
};

/**
Expand Down
Loading