From e410337ff6c96b6dcc677e1c9a7e8e8db0cc33fc Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:27:25 +0300 Subject: [PATCH 1/9] Update the test fixture to be more multi-use applicable --- ...uctArray.test.ts => NullBehaviors.test.ts} | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) rename core/backend/src/test/element/{NullStructArray.test.ts => NullBehaviors.test.ts} (54%) diff --git a/core/backend/src/test/element/NullStructArray.test.ts b/core/backend/src/test/element/NullBehaviors.test.ts similarity index 54% rename from core/backend/src/test/element/NullStructArray.test.ts rename to core/backend/src/test/element/NullBehaviors.test.ts index 04ddd82e1bb9..7928b75267c8 100644 --- a/core/backend/src/test/element/NullStructArray.test.ts +++ b/core/backend/src/test/element/NullBehaviors.test.ts @@ -4,21 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; import { Id64, Id64String } from "@itwin/core-bentley"; -import { - BriefcaseIdValue, Code, ColorDef, GeometricElementProps, IModel, - SubCategoryAppearance, -} from "@itwin/core-common"; -import { _nativeDb, IModelDb, IModelJsFs, SnapshotDb, SpatialCategory } from "../../core-backend"; +import { Code, ColorDef, GeometricElementProps, IModel, SubCategoryAppearance } from "@itwin/core-common"; +import { IModelDb, IModelJsFs, SnapshotDb, SpatialCategory } from "../../core-backend"; import { IModelTestUtils } from "../IModelTestUtils"; +interface TestSchemaLocation { + city: string; + zip: number; +} + interface TestElement extends GeometricElementProps { - addresses: [null, {city: "Pune", zip: 28}]; + addresses: TestSchemaLocation[]; + favoriteNumbers: number[]; } function initElemProps( _iModelName: IModelDb, modId: Id64String, catId: Id64String, autoHandledProp: any): GeometricElementProps { // Create props const elementProps: GeometricElementProps = { - classFullName: "Test:Foo", + classFullName: "Test:TestClass", model: modId, category: catId, code: Code.createEmpty(), @@ -28,36 +31,33 @@ function initElemProps( _iModelName: IModelDb, modId: Id64String, catId: Id64Str return elementProps; } -describe("Insert Null elements in Struct Array, and ensure they are returned while querying rows", () => { +describe("Various ECProperties null behavior handling cases test fixture", () => { const testSchema = ` - - - - - - - - bis:PhysicalElement - - - - + + + + + + + + bis:PhysicalElement + + + `; - const schemaFileName = "NullStructElementTest.01.00.00.xml"; - const iModelFileName = "NullStructElementTest.bim"; - const categoryName = "NullStructElement"; - const subDirName = "NullStructElement"; + const schemaFileName = "NullBehaviorsTest.01.00.00.xml"; + const iModelFileName = "NullBehaviorsTest.bim"; + const subDirName = "NullBehaviors"; const iModelPath = IModelTestUtils.prepareOutputFile(subDirName, iModelFileName); + const categoryName = "NullBehaviorsCategory"; before(async () => { - // write schema to disk as we do not have api to import xml directly const testSchemaPath = IModelTestUtils.prepareOutputFile(subDirName, schemaFileName); IModelJsFs.writeFileSync(testSchemaPath, testSchema); - const imodel = SnapshotDb.createEmpty(iModelPath, { rootSubject: { name: "InsertNullStructArrayTest" } }); + const imodel = SnapshotDb.createEmpty(iModelPath, { rootSubject: { name: "NullBehaviorsTest" } }); await imodel.importSchemas([testSchemaPath]); - imodel[_nativeDb].resetBriefcaseId(BriefcaseIdValue.Unassigned); IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true); let spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName); @@ -69,29 +69,30 @@ describe("Insert Null elements in Struct Array, and ensure they are returned whi imodel.close(); }); - it("Test for struct array to contain null structs", async () => { - const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "roundtrip_correct_data.bim"); + it("validates arrays to contain null values", async () => { + const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "struct_array_contain_nulls.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); - const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName); + const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName)!; const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true); - // create element with auto handled properties - const expectedValue = initElemProps( imodel, newModelId, spatialCategoryId!, { - addresses: [null, {city: "Pune", zip: 28}], + const expectedProps = initElemProps(imodel, newModelId, spatialCategoryId, { + addresses: [null, { city: "Pune", zip: 28 }], + favoriteNumbers: [1, 44, 31, null, 81, 19], }) as TestElement; - // insert a element - const geomElement = imodel.elements.createElement(expectedValue); + // Insert an element containing a struct array with at least one null value + const geomElement = imodel.elements.createElement(expectedProps); const id = imodel.elements.insertElement(geomElement.toJSON()); assert.isTrue(Id64.isValidId64(id), "insert worked"); imodel.saveChanges(); - // verify inserted element properties + // Verify the properties of the inserted element const actualValue = imodel.elements.getElementProps(id); expect(actualValue.addresses.length).to.equal(2); - expect(actualValue.addresses[0]).to.be.empty; + expect(actualValue.addresses).to.equal([undefined, { city: "Pune", zip: 28 }]); + expect(actualValue.favoriteNumbers.length).to.equal(6); + expect(actualValue.favoriteNumbers).to.equal([1, 44, 31, undefined, 81, 19]); imodel.close(); }); - }); From 3966842ee6ca27682e8ac4176a14164d22fc0b63 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:09:01 +0300 Subject: [PATCH 2/9] use deep equal --- core/backend/src/test/element/NullBehaviors.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/backend/src/test/element/NullBehaviors.test.ts b/core/backend/src/test/element/NullBehaviors.test.ts index 7928b75267c8..ef6b1b10efcc 100644 --- a/core/backend/src/test/element/NullBehaviors.test.ts +++ b/core/backend/src/test/element/NullBehaviors.test.ts @@ -89,9 +89,9 @@ describe("Various ECProperties null behavior handling cases test fixture", () => // Verify the properties of the inserted element const actualValue = imodel.elements.getElementProps(id); expect(actualValue.addresses.length).to.equal(2); - expect(actualValue.addresses).to.equal([undefined, { city: "Pune", zip: 28 }]); + expect(actualValue.addresses).to.deep.equal([undefined, { city: "Pune", zip: 28 }]); expect(actualValue.favoriteNumbers.length).to.equal(6); - expect(actualValue.favoriteNumbers).to.equal([1, 44, 31, undefined, 81, 19]); + expect(actualValue.favoriteNumbers).to.deep.equal([1, 44, 31, undefined, 81, 19]); imodel.close(); }); From ce4561055b26550f678cf9716719d0811493f0e3 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 10 Jul 2024 10:57:28 +0300 Subject: [PATCH 3/9] append ecsql null behavior docs samples --- docs/learning/ECSQLNullBehaviors.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/learning/ECSQLNullBehaviors.md b/docs/learning/ECSQLNullBehaviors.md index 3f593575d002..8b57862e4159 100644 --- a/docs/learning/ECSQLNullBehaviors.md +++ b/docs/learning/ECSQLNullBehaviors.md @@ -12,6 +12,7 @@ Documentation schema sample: + @@ -27,7 +28,11 @@ Attempting to update any of the properties (`intProp`, `arrBoolProp`, `structPro ## Setting some complex property children to null -To be added +This section would cover partially setting a complex property (point2d, struct, array) to null. + +- A `point2d` type property must always have all its coordinates present, hence updating a point2d to be partially null is not a valid input and would result in the property not being updated. This is why updating `p2dProp` to `{ x: null, y: 2.5 }` would be invalid and take no effect on the element's property. +- A `struct` type property can have all of their children properties set to null. The child property would end up being cleared from the parent object with the remaining properties remaining intact. If all of the struct's children become null, the struct itself is considered to be null. This means that if `structProp` would include `{ doubleProp: 41.0, stringProp: null }`, the string property would be cleared with the doubleProp successfully updated to the expected value. +- An `array` type property can store null values as any other value of the respective type. It would happen to be represented alongside the ordinary properties as null. However, similarly to structs, if all of the array's entries are nulls, the array itself is considered to be null. E.g. updating `arrBoolProp` to `[false, true, null, false]` is a perfectly valid data entry for a boolean type array property. ## Setting all complex property children to null or making it empty From fc388737dd4bc8dc5682f8ba4ed850c365ab816c Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:03:12 +0300 Subject: [PATCH 4/9] add struct array type to the docs sample --- docs/learning/ECSQLNullBehaviors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/learning/ECSQLNullBehaviors.md b/docs/learning/ECSQLNullBehaviors.md index 8b57862e4159..bfec4e1b364a 100644 --- a/docs/learning/ECSQLNullBehaviors.md +++ b/docs/learning/ECSQLNullBehaviors.md @@ -32,7 +32,7 @@ This section would cover partially setting a complex property (point2d, struct, - A `point2d` type property must always have all its coordinates present, hence updating a point2d to be partially null is not a valid input and would result in the property not being updated. This is why updating `p2dProp` to `{ x: null, y: 2.5 }` would be invalid and take no effect on the element's property. - A `struct` type property can have all of their children properties set to null. The child property would end up being cleared from the parent object with the remaining properties remaining intact. If all of the struct's children become null, the struct itself is considered to be null. This means that if `structProp` would include `{ doubleProp: 41.0, stringProp: null }`, the string property would be cleared with the doubleProp successfully updated to the expected value. -- An `array` type property can store null values as any other value of the respective type. It would happen to be represented alongside the ordinary properties as null. However, similarly to structs, if all of the array's entries are nulls, the array itself is considered to be null. E.g. updating `arrBoolProp` to `[false, true, null, false]` is a perfectly valid data entry for a boolean type array property. +- An `array` type (primitive or struct) property can store null values as any other value of the respective type. It would happen to be represented alongside the ordinary properties as null. However, similarly to structs, if all of the array's entries are nulls, the array itself is considered to be null. E.g. updating `arrBoolProp` to `[false, true, null, false]` is a perfectly valid data entry for a boolean type array property. ## Setting all complex property children to null or making it empty From ce28366b1ea60f728fcd4dcfeca2e5dfef5f8790 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:18:30 +0300 Subject: [PATCH 5/9] Remove docs parts about all of the properties of the struct/array to be null as it's not yet revised --- docs/learning/ECSQLNullBehaviors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/learning/ECSQLNullBehaviors.md b/docs/learning/ECSQLNullBehaviors.md index bfec4e1b364a..006c7bc1d840 100644 --- a/docs/learning/ECSQLNullBehaviors.md +++ b/docs/learning/ECSQLNullBehaviors.md @@ -31,8 +31,8 @@ Attempting to update any of the properties (`intProp`, `arrBoolProp`, `structPro This section would cover partially setting a complex property (point2d, struct, array) to null. - A `point2d` type property must always have all its coordinates present, hence updating a point2d to be partially null is not a valid input and would result in the property not being updated. This is why updating `p2dProp` to `{ x: null, y: 2.5 }` would be invalid and take no effect on the element's property. -- A `struct` type property can have all of their children properties set to null. The child property would end up being cleared from the parent object with the remaining properties remaining intact. If all of the struct's children become null, the struct itself is considered to be null. This means that if `structProp` would include `{ doubleProp: 41.0, stringProp: null }`, the string property would be cleared with the doubleProp successfully updated to the expected value. -- An `array` type (primitive or struct) property can store null values as any other value of the respective type. It would happen to be represented alongside the ordinary properties as null. However, similarly to structs, if all of the array's entries are nulls, the array itself is considered to be null. E.g. updating `arrBoolProp` to `[false, true, null, false]` is a perfectly valid data entry for a boolean type array property. +- A `struct` type property can have all of their children properties set to null. The child property would end up being cleared from the parent object with the remaining properties remaining intact. This means that if `structProp` would include `{ doubleProp: 41.0, stringProp: null }`, the string property would be cleared with the doubleProp successfully updated to the expected value. +- An `array` type (primitive or struct) property can store null values as any other value of the respective type. It would happen to be represented alongside the ordinary properties as null. E.g. updating `arrBoolProp` to `[false, true, null, false]` is a perfectly valid data entry for a boolean type array property. ## Setting all complex property children to null or making it empty From c829786274e9710fdcd5ace5a89ff9eecad0fd72 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:55:17 +0300 Subject: [PATCH 6/9] rush change --- .../Sarunas-array-null-values_2024-07-17-12-55.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@itwin/core-backend/Sarunas-array-null-values_2024-07-17-12-55.json diff --git a/common/changes/@itwin/core-backend/Sarunas-array-null-values_2024-07-17-12-55.json b/common/changes/@itwin/core-backend/Sarunas-array-null-values_2024-07-17-12-55.json new file mode 100644 index 000000000000..99b35bb89b62 --- /dev/null +++ b/common/changes/@itwin/core-backend/Sarunas-array-null-values_2024-07-17-12-55.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/core-backend" +} \ No newline at end of file From 8a56abb56c2aa37578ec18189460f328c5ee95ca Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:26:33 +0300 Subject: [PATCH 7/9] Bump test schema version to latest --- core/backend/src/test/element/NullBehaviors.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/backend/src/test/element/NullBehaviors.test.ts b/core/backend/src/test/element/NullBehaviors.test.ts index ef6b1b10efcc..40b366ba08ec 100644 --- a/core/backend/src/test/element/NullBehaviors.test.ts +++ b/core/backend/src/test/element/NullBehaviors.test.ts @@ -33,7 +33,7 @@ function initElemProps( _iModelName: IModelDb, modId: Id64String, catId: Id64Str describe("Various ECProperties null behavior handling cases test fixture", () => { const testSchema = ` - + @@ -69,7 +69,7 @@ describe("Various ECProperties null behavior handling cases test fixture", () => imodel.close(); }); - it("validates arrays to contain null values", async () => { + it.only("validates arrays to contain null values", async () => { const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "struct_array_contain_nulls.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName)!; From 8221e1bf0a8f8f883ad4178188c8e82bf6140dbc Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:28:51 +0300 Subject: [PATCH 8/9] Remove only on a test --- core/backend/src/test/element/NullBehaviors.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/backend/src/test/element/NullBehaviors.test.ts b/core/backend/src/test/element/NullBehaviors.test.ts index 40b366ba08ec..1ffce9d3966b 100644 --- a/core/backend/src/test/element/NullBehaviors.test.ts +++ b/core/backend/src/test/element/NullBehaviors.test.ts @@ -69,7 +69,7 @@ describe("Various ECProperties null behavior handling cases test fixture", () => imodel.close(); }); - it.only("validates arrays to contain null values", async () => { + it("validates arrays to contain null values", async () => { const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "struct_array_contain_nulls.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName)!; From 20a09bb8dfb7ded38c6c2f1de83da1e8f08950f8 Mon Sep 17 00:00:00 2001 From: "Sarunas.Griskus" <86420873+6ar8nas@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:29:24 +0300 Subject: [PATCH 9/9] rename bim file --- core/backend/src/test/element/NullBehaviors.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/backend/src/test/element/NullBehaviors.test.ts b/core/backend/src/test/element/NullBehaviors.test.ts index 1ffce9d3966b..424f306234d5 100644 --- a/core/backend/src/test/element/NullBehaviors.test.ts +++ b/core/backend/src/test/element/NullBehaviors.test.ts @@ -70,7 +70,7 @@ describe("Various ECProperties null behavior handling cases test fixture", () => }); it("validates arrays to contain null values", async () => { - const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "struct_array_contain_nulls.bim"); + const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "array_contain_nulls.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName)!; const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true);