UNPKG

@itwin/core-backend

Version:
819 lines • 54.6 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; import { DbResult, Id64 } from "@itwin/core-bentley"; import { BriefcaseIdValue, Code, ColorDef, ElementGeometry, IModel, QueryRowFormat, SubCategoryAppearance, } from "@itwin/core-common"; import { Angle, Arc3d, Cone, IModelJson as GeomJson, LineSegment3d, Point2d, Point3d } from "@itwin/core-geometry"; import { _nativeDb, IModelDb, IModelJsFs, PhysicalModel, PhysicalObject, SnapshotDb, SpatialCategory } from "../../core-backend"; import { IModelTestUtils } from "../IModelTestUtils"; import { EntityClass, RelationshipClass } from "@itwin/ecschema-metadata"; function verifyPrimitiveBase(actualValue, expectedValue) { if (expectedValue.i !== undefined) assert.equal(actualValue.i, expectedValue.i, "'integer' type property did not roundtrip as expected"); if (expectedValue.l !== undefined) assert.equal(actualValue.l, expectedValue.l, "'long' type property did not roundtrip as expected"); if (expectedValue.d !== undefined) assert.equal(actualValue.d, expectedValue.d, "'double' type property did not roundtrip as expected"); if (expectedValue.b !== undefined) assert.equal(actualValue.b, expectedValue.b, "'boolean' type property did not roundtrip as expected"); if (expectedValue.dt !== undefined) assert.equal(actualValue.dt, expectedValue.dt, "'dateTime' type property did not roundtrip as expected"); if (expectedValue.s !== undefined) assert.equal(actualValue.s, expectedValue.s, "'string' type property did not roundtrip as expected"); if (expectedValue.p2d) { assert.equal(actualValue.p2d?.x, expectedValue.p2d.x, "'Point2d.x' type property did not roundtrip as expected"); assert.equal(actualValue.p2d?.y, expectedValue.p2d.y, "'Point2d.y' type property did not roundtrip as expected"); } else if (expectedValue.p2d === null) { assert.equal(actualValue.p2d, expectedValue.p2d, "'Point2d' type property did not roundtrip as expected."); } if (expectedValue.p3d) { assert.equal(actualValue.p3d?.x, expectedValue.p3d.x, "'Point3d.x' type property did not roundtrip as expected"); assert.equal(actualValue.p3d?.y, expectedValue.p3d.y, "'Point3d.y' type property did not roundtrip as expected"); assert.equal(actualValue.p3d?.z, expectedValue.p3d.z, "'Point3d.z' type property did not roundtrip as expected"); } else if (expectedValue.p3d === null) { assert.equal(actualValue.p3d, expectedValue.p3d, "'Point3d' type property did not roundtrip as expected."); } if (expectedValue.bin) { assert.isTrue(blobEqual(actualValue.bin, expectedValue.bin), "'binary' type property did not roundtrip as expected"); } else if (expectedValue.bin === null) { assert.equal(actualValue.bin, expectedValue.bin, "'binary' type property did not roundtrip as expected."); } if (expectedValue.g) { expect(actualValue.g, "'geometry' type property did not roundtrip as expected.").to.deep.equal(expectedValue.g); } else if (expectedValue.g === null) { assert.equal(actualValue.g, expectedValue.g, "'geometry' type property did not roundtrip as expected."); } } function verifyPrimitiveArrayBase(actualValue, expectedValue) { if (expectedValue.array_bin) { assert.equal(actualValue.array_bin.length, expectedValue.array_bin.length, "'binary[].length' array length mismatch"); expectedValue.array_bin.forEach((value, index) => { if (value) { assert.isTrue(blobEqual(actualValue.array_bin[index], value), "'binary[]' type property did not roundtrip as expected"); } else if (value === null) { assert.equal(actualValue.array_bin[index], value, "'binary[]' type property did not roundtrip as expected"); } }); } else if (expectedValue.array_bin === null) { assert.equal(actualValue.array_bin, expectedValue.array_bin, "'binary[]' type property did not roundtrip as expected."); } if (expectedValue.array_i) { assert.equal(actualValue.array_i.length, expectedValue.array_i.length, "'integer[].length' array length mismatch"); expectedValue.array_i.forEach((value, index) => { assert.equal(actualValue.array_i[index], value, "'integer[]' type property did not roundtrip as expected"); }); } else if (expectedValue.array_i === null) { assert.equal(actualValue.array_i, expectedValue.array_i, "'integer[]' type property did not roundtrip as expected."); } if (expectedValue.array_l) { assert.equal(actualValue.array_l.length, expectedValue.array_l.length, "'long[].length' array length mismatch"); expectedValue.array_l.forEach((value, index) => { assert.equal(actualValue.array_l[index], value, "'long[]' type property did not roundtrip as expected"); }); } else if (expectedValue.array_l === null) { assert.equal(actualValue.array_l, expectedValue.array_l, "'long[]' type property did not roundtrip as expected."); } if (expectedValue.array_d) { assert.equal(actualValue.array_d.length, expectedValue.array_d.length, "'double[].length' array length mismatch"); expectedValue.array_d.forEach((value, index) => { assert.equal(actualValue.array_d[index], value, "'double[]' type property did not roundtrip as expected"); }); } else if (expectedValue.array_d === null) { assert.equal(actualValue.array_d, expectedValue.array_d, "'double[]' type property did not roundtrip as expected."); } if (expectedValue.array_b) { assert.equal(actualValue.array_b.length, expectedValue.array_b.length, "'boolean[].length' array length mismatch"); expectedValue.array_b.forEach((value, index) => { assert.equal(actualValue.array_b[index], value, "'boolean[]' type property did not roundtrip as expected"); }); } else if (expectedValue.array_b === null) { assert.equal(actualValue.array_b, expectedValue.array_b, "'boolean[]' type property did not roundtrip as expected."); } if (expectedValue.array_dt) { assert.equal(actualValue.array_dt.length, expectedValue.array_dt.length, "'dateTime[].length' array length mismatch"); expectedValue.array_dt.forEach((value, index) => { assert.equal(actualValue.array_dt[index], value, "'dateTime[]' type property did not roundtrip as expected"); }); } else if (expectedValue.array_dt === null) { assert.equal(actualValue.array_dt, expectedValue.array_dt, "'dateTime[]' type property did not roundtrip as expected."); } if (expectedValue.array_g) { assert.equal(actualValue.array_g.length, expectedValue.array_g.length, "'geometry[].length' array length mismatch"); expectedValue.array_g.forEach((value, index) => { if (value) { expect(actualValue.array_g[index], "'geometry[]' type property did not roundtrip as expected").to.deep.equal(value); } else if (value === null) { assert.equal(actualValue.array_g[index], value, "'geometry[]' type property did not roundtrip as expected"); } }); } else if (expectedValue.array_g === null) { assert.equal(actualValue.array_g, expectedValue.array_g, "'geometry[]' type property did not roundtrip as expected."); } if (expectedValue.array_s) { assert.equal(actualValue.array_s.length, expectedValue.array_s.length, "'string[].length' array length mismatch"); expectedValue.array_s.forEach((value, index) => { assert.equal(actualValue.array_s[index], value, "'string[]' type property did not roundtrip as expected"); }); } else if (expectedValue.array_s === null) { assert.equal(actualValue.array_s, expectedValue.array_s, "'string[]' type property did not roundtrip as expected."); } if (expectedValue.array_p2d) { assert.equal(actualValue.array_p2d.length, expectedValue.array_p2d.length, "'point2d[].length' array length mismatch"); expectedValue.array_p2d.forEach((value, index) => { if (value) { assert.equal(actualValue.array_p2d[index].x, value.x, "'point2d[].x' type property did not roundtrip as expected"); assert.equal(actualValue.array_p2d[index].y, value.y, "'point2d[].y' type property did not roundtrip as expected"); } else if (value === null) { assert.equal(actualValue.array_p2d[index], value, "'point2d[]' type property did not roundtrip as expected."); } }); } else if (expectedValue.array_p2d === null) { assert.equal(actualValue.array_p2d, expectedValue.array_p2d, "'point2d[]' type property did not roundtrip as expected."); } if (expectedValue.array_p3d) { assert.equal(actualValue.array_p3d.length, expectedValue.array_p3d.length, "'point3d[].length' array length mismatch"); expectedValue.array_p3d.forEach((value, index) => { if (value) { assert.equal(actualValue.array_p3d[index].x, value.x, "'point3d[].x' type property did not roundtrip as expected"); assert.equal(actualValue.array_p3d[index].y, value.y, "'point3d[].y' type property did not roundtrip as expected"); assert.equal(actualValue.array_p3d[index].z, value.z, "'point3d[].z' type property did not roundtrip as expected"); } else if (value === null) { assert.equal(actualValue.array_p3d[index], value, "'point3d[]' type property did not roundtrip as expected."); } }); } else if (expectedValue.array_p3d === null) { assert.equal(actualValue.array_p3d, expectedValue.array_p3d, "'point3d[]' type property did not roundtrip as expected."); } } function verifyPrimitive(actualValue, expectedValue) { verifyPrimitiveBase(actualValue, expectedValue); if (expectedValue.st) { verifyPrimitive(actualValue.st, expectedValue.st); verifyPrimitiveArray(actualValue.st, expectedValue.st); } else if (expectedValue.st === null) { assert.equal(actualValue.st, expectedValue.st, "'ComplexStruct' type property did not roundtrip as expected."); } } function verifyPrimitiveArray(actualValue, expectedValue) { verifyPrimitiveArrayBase(actualValue, expectedValue); if (expectedValue.array_st) { assert.equal(actualValue.array_st.length, expectedValue.array_st.length, "'struct[].length' array length mismatch"); actualValue.array_st.forEach((lhs, i) => { verifyPrimitiveBase(lhs, expectedValue.array_st[i]); verifyPrimitiveArrayBase(lhs, expectedValue.array_st[i]); }); } else if (expectedValue.array_st === null) { assert.equal(actualValue.array_st, expectedValue.array_st, "'ComplexStruct[]' type property did not roundtrip as expected."); } } function verifySystemProperty(actualValue, expectedValue) { assert.deepEqual(actualValue, expectedValue, "System property did not roundtrip as expected"); } function verifyTestElement(actualValue, expectedValue) { verifyPrimitive(actualValue, expectedValue); verifyPrimitiveArray(actualValue, expectedValue); } function verifyTestElementAspect(actualValue, expectedValue) { verifyPrimitive(actualValue, expectedValue); verifyPrimitiveArray(actualValue, expectedValue); } function initElemProps(className, _iModelName, modId, catId, autoHandledProp) { // add Geometry const geomArray = [ Arc3d.createXY(Point3d.create(0, 0), 5), Arc3d.createXY(Point3d.create(5, 5), 2), Arc3d.createXY(Point3d.create(-5, -5), 20), ]; const geometryStream = []; for (const geom of geomArray) { const arcData = GeomJson.Writer.toIModelJson(geom); geometryStream.push(arcData); } // Create props const elementProps = { classFullName: `ElementRoundTripTest:${className}`, model: modId, category: catId, code: Code.createEmpty(), geom: geometryStream, }; if (autoHandledProp) Object.assign(elementProps, autoHandledProp); return elementProps; } function initElementAspectProps(className, _iModelName, elId, autoHandledProp) { // Create props const elementProps = { classFullName: `ElementRoundTripTest:${className}`, element: { id: elId }, }; if (autoHandledProp) Object.assign(elementProps, autoHandledProp); return elementProps; } function blobEqual(lhs, rhs) { if (!(lhs instanceof Uint8Array) || !(rhs instanceof Uint8Array)) throw new Error("expecting uint8array"); if (lhs.byteLength !== rhs.byteLength) return false; for (let i = 0; i < lhs.byteLength; i++) { if (lhs[i] !== rhs[i]) return false; } return true; } function initElementRefersToElementsProps(className, _iModelName, elId1, elId2, autoHandledProp) { // Create props const result = { classFullName: `ElementRoundTripTest:${className}`, sourceId: elId1, targetId: elId2, }; if (autoHandledProp) Object.assign(result, autoHandledProp); return result; } describe("Element and ElementAspect roundtrip test for all type of properties", () => { const testSchema = `<?xml version="1.0" encoding="UTF-8"?> <ECSchema schemaName="ElementRoundTripTest" alias="ts" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2"> <ECSchemaReference name="BisCore" version="01.00.04" alias="bis"/> <ECSchemaReference name="CoreCustomAttributes" version="01.00.03" alias="CoreCA"/> <ECEntityClass typeName="TestElement" modifier="None"> <BaseClass>bis:PhysicalElement</BaseClass> <BaseClass>IPrimitive</BaseClass> <BaseClass>IPrimitiveArray</BaseClass> </ECEntityClass> <ECEntityClass typeName="TestElementAspect" modifier="None"> <BaseClass>bis:ElementUniqueAspect</BaseClass> <BaseClass>IPrimitiveAspect</BaseClass> <BaseClass>IPrimitiveArrayAspect</BaseClass> </ECEntityClass> <ECRelationshipClass typeName="TestElementRefersToElements" strength="referencing" modifier="Sealed"> <BaseClass>bis:ElementRefersToElements</BaseClass> <Source multiplicity="(0..*)" roleLabel="refers to" polymorphic="true"> <Class class="TestElement"/> </Source> <Target multiplicity="(0..*)" roleLabel="is referenced by" polymorphic="true"> <Class class="TestElement"/> </Target> <ECProperty propertyName="i" typeName="int"/> <ECProperty propertyName="l" typeName="long"/> <ECProperty propertyName="d" typeName="double"/> <ECProperty propertyName="b" typeName="boolean"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="s" typeName="string"/> <ECProperty propertyName="bin" typeName="binary"/> <ECProperty propertyName="p2d" typeName="point2d"/> <ECProperty propertyName="p3d" typeName="point3d"/> <ECProperty propertyName="g" typeName="Bentley.Geometry.Common.IGeometry"/> <!--<ECStructProperty propertyName="st" typeName="ComplexStruct"/>--> <ECArrayProperty propertyName="array_i" typeName="int" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_l" typeName="long" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_d" typeName="double" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_b" typeName="boolean" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_dt" typeName="dateTime" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_s" typeName="string" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_bin" typeName="binary" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p2d" typeName="point2d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p3d" typeName="point3d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_g" typeName="Bentley.Geometry.Common.IGeometry" minOccurs="0" maxOccurs="unbounded"/> <!--<ECStructArrayProperty propertyName="array_st" typeName="ComplexStruct" minOccurs="0" maxOccurs="unbounded"/>--> </ECRelationshipClass> <ECEntityClass typeName="IPrimitive" modifier="Abstract"> <ECCustomAttributes> <IsMixin xmlns="CoreCustomAttributes.01.00.03"> <AppliesToEntityClass>bis:PhysicalElement</AppliesToEntityClass> </IsMixin> </ECCustomAttributes> <ECProperty propertyName="i" typeName="int"/> <ECProperty propertyName="l" typeName="long"/> <ECProperty propertyName="d" typeName="double"/> <ECProperty propertyName="b" typeName="boolean"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="s" typeName="string"/> <ECProperty propertyName="bin" typeName="binary"/> <ECProperty propertyName="p2d" typeName="point2d"/> <ECProperty propertyName="p3d" typeName="point3d"/> <ECProperty propertyName="g" typeName="Bentley.Geometry.Common.IGeometry"/> <ECStructProperty propertyName="st" typeName="ComplexStruct"/> </ECEntityClass> <ECEntityClass typeName="IPrimitiveAspect" modifier="Abstract"> <ECCustomAttributes> <IsMixin xmlns="CoreCustomAttributes.01.00.03"> <AppliesToEntityClass>bis:ElementUniqueAspect</AppliesToEntityClass> </IsMixin> </ECCustomAttributes> <ECProperty propertyName="i" typeName="int"/> <ECProperty propertyName="l" typeName="long"/> <ECProperty propertyName="d" typeName="double"/> <ECProperty propertyName="b" typeName="boolean"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="s" typeName="string"/> <ECProperty propertyName="bin" typeName="binary"/> <ECProperty propertyName="p2d" typeName="point2d"/> <ECProperty propertyName="p3d" typeName="point3d"/> <ECProperty propertyName="g" typeName="Bentley.Geometry.Common.IGeometry"/> <ECStructProperty propertyName="st" typeName="ComplexStruct"/> </ECEntityClass> <ECEntityClass typeName="IPrimitiveArray" modifier="Abstract"> <ECCustomAttributes> <IsMixin xmlns="CoreCustomAttributes.01.00.03"> <AppliesToEntityClass>bis:PhysicalElement</AppliesToEntityClass> </IsMixin> </ECCustomAttributes> <ECArrayProperty propertyName="array_i" typeName="int" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_l" typeName="long" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_d" typeName="double" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_b" typeName="boolean" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_dt" typeName="dateTime" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_s" typeName="string" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_bin" typeName="binary" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p2d" typeName="point2d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p3d" typeName="point3d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_g" typeName="Bentley.Geometry.Common.IGeometry" minOccurs="0" maxOccurs="unbounded"/> <ECStructArrayProperty propertyName="array_st" typeName="ComplexStruct" minOccurs="0" maxOccurs="unbounded"/> </ECEntityClass> <ECEntityClass typeName="IPrimitiveArrayAspect" modifier="Abstract"> <ECCustomAttributes> <IsMixin xmlns="CoreCustomAttributes.01.00.03"> <AppliesToEntityClass>bis:ElementUniqueAspect</AppliesToEntityClass> </IsMixin> </ECCustomAttributes> <ECArrayProperty propertyName="array_i" typeName="int" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_l" typeName="long" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_d" typeName="double" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_b" typeName="boolean" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_dt" typeName="dateTime" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_s" typeName="string" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_bin" typeName="binary" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p2d" typeName="point2d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p3d" typeName="point3d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_g" typeName="Bentley.Geometry.Common.IGeometry" minOccurs="0" maxOccurs="unbounded"/> <ECStructArrayProperty propertyName="array_st" typeName="ComplexStruct" minOccurs="0" maxOccurs="unbounded"/> </ECEntityClass> <ECStructClass typeName="ComplexStruct" modifier="None"> <ECProperty propertyName="i" typeName="int"/> <ECProperty propertyName="l" typeName="long"/> <ECProperty propertyName="d" typeName="double"/> <ECProperty propertyName="b" typeName="boolean"/> <ECProperty propertyName="dt" typeName="dateTime"/> <ECProperty propertyName="s" typeName="string"/> <ECProperty propertyName="bin" typeName="binary"/> <ECProperty propertyName="p2d" typeName="point2d"/> <ECProperty propertyName="p3d" typeName="point3d"/> <ECProperty propertyName="g" typeName="Bentley.Geometry.Common.IGeometry"/> <ECArrayProperty propertyName="array_i" typeName="int" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_l" typeName="long" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_d" typeName="double" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_b" typeName="boolean" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_dt" typeName="dateTime" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_s" typeName="string" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_bin" typeName="binary" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p2d" typeName="point2d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_p3d" typeName="point3d" minOccurs="0" maxOccurs="unbounded"/> <ECArrayProperty propertyName="array_g" typeName="Bentley.Geometry.Common.IGeometry" minOccurs="0" maxOccurs="unbounded"/> </ECStructClass> </ECSchema>`; const schemaFileName = "ElementRoundTripTest.01.00.00.xml"; const iModelFileName = "ElementRoundTripTest.bim"; const categoryName = "RoundTripCategory"; const subDirName = "ElementRoundTrip"; const iModelPath = IModelTestUtils.prepareOutputFile(subDirName, iModelFileName); const primInst1 = { i: 101, l: 12334343434, d: 1023.34, b: true, dt: "2017-01-01T00:00:00.000", s: "Test string Inst1", bin: new Uint8Array([1, 2, 3]), g: GeomJson.Writer.toIModelJson(Cone.createAxisPoints(Point3d.create(0, 0, 0), Point3d.create(0, 0, 1), 0.5, 0.5, false)), p2d: new Point2d(1.034, 2.034), p3d: new Point3d(-1.0, 2.3, 3.0001), }; const primInst2 = { i: 4322, l: 98283333, d: -2343.342, b: false, dt: "2010-01-01T11:11:11.000", s: "Test string Inst2", bin: new Uint8Array([11, 21, 31, 34, 53, 21, 14, 14, 55, 22]), g: GeomJson.Writer.toIModelJson(Cone.createAxisPoints(Point3d.create(0, 1, 0), Point3d.create(0, 0, 1), 0.5, 0.5, false)), p2d: new Point2d(1111.11, 2222.22), p3d: new Point3d(-111.11, -222.22, -333.33), }; const primArrInst1 = { array_i: [101, 202, -345], array_l: [12334343434, 3434343434, 12], array_d: [1023.34, 3023.34, -3432.033], array_b: [true, false, true, false], array_dt: ["2017-01-01T00:00:00.000", "2018-01-01T00:00:00.000"], array_s: ["Test string 1", "Test string 2", "Test string 3"], array_bin: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 2, 3, 3, 4, 55, 6, 65])], array_p2d: [new Point2d(1, 2), new Point2d(2, 4)], array_p3d: [new Point3d(1, 2, 3), new Point3d(4, 5, 6)], array_g: [ GeomJson.Writer.toIModelJson(Cone.createAxisPoints(Point3d.create(0, 1, 0), Point3d.create(0, 0, 2), 0.5, 0.5, false)), GeomJson.Writer.toIModelJson(Cone.createAxisPoints(Point3d.create(0, 1, 1), Point3d.create(0, 0, 2), 0.5, 0.5, false)), ], }; const primArrInst2 = { array_i: [0, 1, 2, 3, 4, 5, 6666], array_l: [-23422, -343343434, -12333434, 23423423], array_d: [-21023.34, -33023.34, -34432.033], array_b: [false, true], array_dt: ["2017-01-01T00:00:00.000", "2018-01-01T00:00:00.000", "2011-01-01T00:00:00.000"], array_s: ["Test string 1 - inst2", "Test string 2 - inst2", "Test string 3 - inst2"], array_bin: [new Uint8Array([1, 2, 3, 3, 4]), new Uint8Array([0, 0, 0, 0]), new Uint8Array([1, 2, 3, 4])], array_p2d: [new Point2d(-123, 244.23232), new Point2d(232, 324.2323), new Point2d(322, 2324.23322)], array_p3d: [new Point3d(133, 2333, 333), new Point3d(4123, 5123, 6123)], array_g: [ GeomJson.Writer.toIModelJson(Cone.createAxisPoints(Point3d.create(0, 1, 0), Point3d.create(0, 0, 2), 0.5, 0.5, false)), GeomJson.Writer.toIModelJson(Cone.createAxisPoints(Point3d.create(0, 1, 1), Point3d.create(0, 0, 2), 0.5, 0.5, false)), ], }; 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: "RoundTripTest" } }); await imodel.importSchemas([testSchemaPath]); imodel[_nativeDb].resetBriefcaseId(BriefcaseIdValue.Unassigned); IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true); let spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName); if (undefined === spatialCategoryId) spatialCategoryId = SpatialCategory.insert(imodel, IModel.dictionaryId, categoryName, new SubCategoryAppearance({ color: ColorDef.create("rgb(255,0,0)").toJSON() })); imodel.saveChanges(); imodel.close(); }); it("Roundtrip all type of properties via ElementApi, ConcurrentQuery and ECSqlStatement via insert and update", async () => { const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "roundtrip_correct_data.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); 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("TestElement", imodel, newModelId, spatialCategoryId, { ...primInst1, ...primArrInst1, st: { ...primArrInst2, ...primInst1 }, array_st: [{ ...primInst1, ...primArrInst2 }, { ...primInst2, ...primArrInst1 }], }); // insert a element const geomElement = imodel.elements.createElement(expectedValue); const id = imodel.elements.insertElement(geomElement.toJSON()); assert.isTrue(Id64.isValidId64(id), "insert worked"); imodel.saveChanges(); const expectedSystemProperty = { id, className: `ElementRoundTripTest.TestElement`, model: { id: newModelId, relClassName: `BisCore.ModelContainsElements`, } }; // verify inserted element properties const actualValue = imodel.elements.getElementProps(id); verifyTestElement(actualValue, expectedValue); // verify via concurrent query let rowCount = 0; for await (const row of imodel.createQueryReader("SELECT * FROM ts.TestElement", undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) { verifyTestElement(row.toRow(), expectedValue); rowCount++; } assert.equal(rowCount, 1); // verify via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElement", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow(); verifyTestElement(stmtRow, expectedValue); }); // Verify system properties via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("select ECInstanceId, ECClassId, Model from ts.TestElement", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow(), expectedSystemProperty); }); const testElementMetaData = imodel.schemaContext.getSchemaItemSync("ElementRoundTripTest", "TestElement", EntityClass); assert.isDefined(testElementMetaData); const relClassMetaData = imodel.schemaContext.getSchemaItemSync("BisCore", "ModelContainsElements", RelationshipClass); assert.isDefined(relClassMetaData); // Verify system properties via concurrent query let reader = imodel.createQueryReader("SELECT ECInstanceId, ec_classname(ECClassId, 's.c') as className, Model.Id, ec_classname(Model.RelECClassId, 's.c') as relClassName FROM ts.TestElement", undefined, { rowFormat: QueryRowFormat.UseECSqlPropertyNames }); assert.isTrue(await reader.step()); assert.equal(reader.current.ECInstanceId, id); assert.equal(reader.current.className, testElementMetaData?.fullName); assert.equal(reader.current.Id, newModelId); assert.equal(reader.current.relClassName, relClassMetaData?.fullName); assert.isFalse(await reader.step()); // update the element autohandled properties Object.assign(actualValue, { ...primInst2, ...primArrInst2, st: { ...primArrInst1, ...primInst2 }, array_st: [{ ...primInst2, ...primArrInst2 }, { ...primInst1, ...primArrInst1 }], }); // update element imodel.elements.updateElement(actualValue); imodel.saveChanges(); // verify updated values const updatedValue = imodel.elements.getElementProps(id); verifyTestElement(updatedValue, actualValue); // verify via concurrent query rowCount = 0; for await (const row of imodel.createQueryReader("SELECT * FROM ts.TestElement", undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) { verifyTestElement(row.toRow(), actualValue); rowCount++; } assert.equal(rowCount, 1); // verify via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElement", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow(); verifyTestElement(stmtRow, actualValue); }); // Verify system properties via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("select ECInstanceId, ECClassId, Model from ts.TestElement", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow(), expectedSystemProperty); }); // Verify system properties via concurrent query reader = imodel.createQueryReader("SELECT ECInstanceId, ec_classname(ECClassId, 's.c') as className, Model.Id, ec_classname(Model.RelECClassId, 's.c') as relClassName FROM ts.TestElement", undefined, { rowFormat: QueryRowFormat.UseECSqlPropertyNames }); assert.isTrue(await reader.step()); assert.equal(reader.current.ECInstanceId, id); assert.equal(reader.current.className, testElementMetaData?.fullName); assert.equal(reader.current.Id, newModelId); assert.equal(reader.current.relClassName, relClassMetaData?.fullName); assert.isFalse(await reader.step()); imodel.close(); }); async function verifyElementAspect(elementAspectId, elementAspect, elementId, expectedAspectFullName, iModel) { // Verify updated values const updatedAspectValue = iModel.elements.getAspects(elementId, expectedAspectFullName).map((x) => x.toJSON()); assert.equal(updatedAspectValue.length, 1); verifyTestElementAspect(updatedAspectValue[0], elementAspect); // Verify via a concurrent query let rowCount = 0; for await (const row of iModel.createQueryReader("SELECT * FROM ts.TestElementAspect", undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) { verifyTestElementAspect(row.toRow(), elementAspect); rowCount++; } assert.equal(rowCount, 1); // Verify via an ECSql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await iModel.withPreparedStatement("SELECT * FROM ts.TestElementAspect", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow(); verifyTestElementAspect(stmtRow, elementAspect); }); const expectedSystemProperty = { id: elementAspectId, className: `ElementRoundTripTest.TestElementAspect`, element: { id: elementId, relClassName: `BisCore.ElementOwnsUniqueAspect`, }, }; const aspectMetaData = await iModel.schemaContext.getSchemaItem("ElementRoundTripTest.TestElementAspect", EntityClass); assert.isDefined(aspectMetaData); const relMetaData = await iModel.schemaContext.getSchemaItem("BisCore.ElementOwnsUniqueAspect", RelationshipClass); assert.isDefined(relMetaData); // Verify via a concurrent query const reader = iModel.createQueryReader("SELECT ECInstanceId, ec_classname(ECClassId, 's.c') as className, Element.Id, ec_classname(Element.RelECClassId, 's.c') as relClassName FROM ts.TestElementAspect", undefined, { rowFormat: QueryRowFormat.UseECSqlPropertyNames }); assert.isTrue(await reader.step()); assert.equal(reader.current.ECInstanceId, elementAspectId); assert.equal(reader.current.className, aspectMetaData?.fullName); assert.equal(reader.current.Id, elementId); assert.equal(reader.current.relClassName, relMetaData?.fullName); assert.isFalse(await reader.step()); // Verify via an ECSql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await iModel.withPreparedStatement("SELECT ECInstanceId, ECClassId, Element FROM ts.TestElementAspect", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow(), expectedSystemProperty); }); return updatedAspectValue; } it("Roundtrip all type of properties via ElementAspectApi, ConcurrentQuery and ECSqlStatement via insert and update", async () => { const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "roundtrip_apsect_correct_data.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true); // Create an element to use with the ElementAspects const expectedValue = initElemProps("TestElement", imodel, newModelId, spatialCategoryId, {}); // Insert an element const geomElement = imodel.elements.createElement(expectedValue); const elId = imodel.elements.insertElement(geomElement.toJSON()); assert.isTrue(Id64.isValidId64(elId), "Element insertion succeeded"); const expectedAspectValue = initElementAspectProps("TestElementAspect", imodel, elId, { ...primInst1, ...primArrInst1, st: { ...primArrInst2, ...primInst1 }, array_st: [{ ...primInst1, ...primArrInst2 }, { ...primInst2, ...primArrInst1 }], }); // Insert an element aspect const elementAspectId = imodel.elements.insertAspect(expectedAspectValue); imodel.saveChanges(); // Verify inserted element aspect properties const actualAspectValue = await verifyElementAspect(elementAspectId, expectedAspectValue, elId, expectedAspectValue.classFullName, imodel); // Update the element's autohandled properties Object.assign(actualAspectValue[0], { ...primInst2, ...primArrInst2, st: { ...primArrInst1, ...primInst2 }, array_st: [{ ...primInst2, ...primArrInst2 }, { ...primInst1, ...primArrInst1 }], }); // Update the element imodel.elements.updateAspect(actualAspectValue[0]); imodel.saveChanges(); // Verify updated element aspect properties await verifyElementAspect(elementAspectId, actualAspectValue[0], elId, expectedAspectValue.classFullName, imodel); imodel.close(); }); function verifyTestElementRefersToElements(actualValue, expectedValue) { assert.equal(actualValue.sourceId, expectedValue.sourceId, "'sourceId' type property did not roundtrip as expected"); assert.equal(actualValue.targetId, expectedValue.targetId, "'targetId' type property did not roundtrip as expected"); verifyPrimitiveBase(actualValue, expectedValue); verifyPrimitiveArrayBase(actualValue, expectedValue); } it("Roundtrip all type of properties via ElementRefersToElements, ConcurrentQuery and ECSqlStatement via insert and update", async () => { const testFileName = IModelTestUtils.prepareOutputFile(subDirName, "roundtrip_relationships_correct_data.bim"); const imodel = IModelTestUtils.createSnapshotFromSeed(testFileName, iModelPath); const spatialCategoryId = SpatialCategory.queryCategoryIdByName(imodel, IModel.dictionaryId, categoryName); const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(imodel, Code.createEmpty(), true); // create elements to use const element1 = initElemProps("TestElement", imodel, newModelId, spatialCategoryId, {}); const element2 = initElemProps("TestElement", imodel, newModelId, spatialCategoryId, {}); const geomElement1 = imodel.elements.createElement(element1); const elId1 = imodel.elements.insertElement(geomElement1.toJSON()); assert.isTrue(Id64.isValidId64(elId1), "insert of element 1 worked"); const geomElement2 = imodel.elements.createElement(element2); const elId2 = imodel.elements.insertElement(geomElement2.toJSON()); assert.isTrue(Id64.isValidId64(elId2), "insert of element 2 worked"); // TODO: Skipping structs here, because of a bug that prevents querying from link tables that have an overflow table, by skipping the struct we reduce the amount of used columns const expectedRelationshipValue = initElementRefersToElementsProps("TestElementRefersToElements", imodel, elId1, elId2, { ...primInst1, ...primArrInst1, /* st: { ...primArrInst2, ...primInst1 }, array_st: [{ ...primInst1, ...primArrInst2 }, { ...primInst2, ...primArrInst1 }], */ }); const instance = expectedRelationshipValue; // imodel.relationships.createInstance(expectedRelationshipValue); const relationshipId = imodel.relationships.insertInstance(instance); // initElementRefersToElementsProps lies about return type. imodel.saveChanges(); // verify inserted properties const actualRelationshipValue = imodel.relationships.getInstance(expectedRelationshipValue.classFullName, relationshipId); assert.exists(actualRelationshipValue); verifyTestElementRefersToElements(actualRelationshipValue, expectedRelationshipValue); // verify via concurrent query let rowCount = 0; for await (const row of imodel.createQueryReader("SELECT * FROM ts.TestElementRefersToElements", undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) { const val = row.toRow(); verifyTestElementRefersToElements(val, expectedRelationshipValue); rowCount++; } assert.equal(rowCount, 1); // verify via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElementRefersToElements", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow(); verifyTestElementRefersToElements(stmtRow, expectedRelationshipValue); }); const expectedSystemProperties = { id: expectedRelationshipValue.id, className: `ElementRoundTripTest.TestElementRefersToElements`, sourceId: elId1, sourceClassName: `ElementRoundTripTest.TestElement`, targetId: elId2, targetClassName: `ElementRoundTripTest.TestElement`, }; const classMetaData = await imodel.schemaContext.getSchemaItem("ElementRoundTripTest.TestElementRefersToElements", RelationshipClass); assert.isDefined(classMetaData); const elementMetaData = await imodel.schemaContext.getSchemaItem("ElementRoundTripTest.TestElement", EntityClass); assert.isDefined(elementMetaData); // verify system properties via concurrent query let reader = imodel.createQueryReader("SELECT ECInstanceId, ec_classname(ECClassId, 's.c') as className, SourceECInstanceId, ec_classname(SourceECClassId, 's.c') as srcClassName, TargetECInstanceid, ec_classname(TargetECClassId, 's.c') as trgtClassName FROM ts.TestElementRefersToElements", undefined, { rowFormat: QueryRowFormat.UseECSqlPropertyNames }); assert.isTrue(await reader.step()); assert.equal(reader.current.ECInstanceId, relationshipId); assert.equal(reader.current.className, classMetaData?.fullName); assert.equal(reader.current.SourceECInstanceId, elId1); assert.equal(reader.current.srcClassName, elementMetaData?.fullName); assert.equal(reader.current.TargetECInstanceid, elId2); assert.equal(reader.current.trgtClassName, elementMetaData?.fullName); assert.isFalse(await reader.step()); // verify system properties via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT ECInstanceId, ECClassId, SourceECInstanceId, SourceECClassId, TargetECInstanceid, TargetECClassId FROM ts.TestElementRefersToElements", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow(), expectedSystemProperties); }); const updatedExpectedValue = actualRelationshipValue; // update the element autohandled properties Object.assign(updatedExpectedValue, { ...primInst2, ...primArrInst2, /* st: { ...primArrInst1, ...primInst2 }, array_st: [{ ...primInst2, ...primArrInst2 }, { ...primInst1, ...primArrInst1 }],*/ }); // update imodel.relationships.updateInstance(updatedExpectedValue.toJSON()); imodel.saveChanges(); // verify updated values const updatedValue = imodel.relationships.getInstance(expectedRelationshipValue.classFullName, relationshipId); verifyTestElementRefersToElements(updatedValue, updatedExpectedValue); // verify via concurrent query rowCount = 0; for await (const row of imodel.createQueryReader("SELECT * FROM ts.TestElementRefersToElements", undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames })) { verifyTestElementRefersToElements(row.toRow(), updatedExpectedValue); rowCount++; } assert.equal(rowCount, 1); // verify via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT * FROM ts.TestElementRefersToElements", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); const stmtRow = stmt.getRow(); verifyTestElementRefersToElements(stmtRow, updatedExpectedValue); }); // verify system properties via concurrent query reader = imodel.createQueryReader("SELECT ECInstanceId, ec_classname(ECClassId, 's.c') as className, SourceECInstanceId, ec_classname(SourceECClassId, 's.c') as srcClassName, TargetECInstanceid, ec_classname(TargetECClassId, 's.c') as trgtClassName FROM ts.TestElementRefersToElements", undefined, { rowFormat: QueryRowFormat.UseECSqlPropertyNames }); assert.isTrue(await reader.step()); assert.equal(reader.current.ECInstanceId, relationshipId); assert.equal(reader.current.className, classMetaData?.fullName); assert.equal(reader.current.SourceECInstanceId, elId1); assert.equal(reader.current.srcClassName, elementMetaData?.fullName); assert.equal(reader.current.TargetECInstanceid, elId2); assert.equal(reader.current.trgtClassName, elementMetaData?.fullName); assert.isFalse(await reader.step()); // verify system properties via ecsql statement // eslint-disable-next-line @typescript-eslint/no-deprecated await imodel.withPreparedStatement("SELECT ECInstanceId, ECClassId, SourceECInstanceId, SourceECClassId, TargetECInstanceid, TargetECClassId FROM ts.TestElementRefersToElements", async (stmt) => { assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW); verifySystemProperty(stmt.getRow(), expectedSystemProperties); }); imodel.close(); }); it("Roundtrip placement when geom is undefined", async () => { const placement = { origin: { x: 10, y: 20, z: 30 }, angles: { yaw: Angle.createDegrees(90), pitch: Angle.createDegrees(180), roll: Angle.createDegrees(270), }, bbox: { low: { x: -1, y: -2, z: -3 }, high: { x: 1, y: 2, z: 3 }, }, }; const insertAndVerifyPlacement = (name, extraProps = {}, { /** * setting some geometry will override the passed bounding box with a calculated one, * so we need to be able to override parts of the expected placement based on the geometry used */ expectedPlacementOverrides = {}, } = {}) => { const imodelPath = IModelTestUtils.prepareOutputFile(subDirName, `roundtrip_placement-${name}.bim`); let imodel = IModelTestUtils.createSnapshotFromSeed(imodelPath, iModelPath); const modelId = PhysicalModel.insert(imodel, IModelDb.rootSubjectId, "model"); const categoryId = SpatialCategory.insert(imodel, IModelDb.dictionaryId, "model", {}); const expectedPlacement = { ...placement, ...expectedPlacementOverrides }; const objId = imodel.elements.insertElement({ classFullName: PhysicalObject.classFullName, code: Code.createEmpty(), model: modelId, placement, category: categoryId, ...extraProps, }); imodel.saveChanges(); const inMemoryCopy = imodel.elements.getElement({ id: objId, wantGeometry: true }, PhysicalObject); expect(inMemoryCopy.placement).to.deep.advancedEqual(expectedPlacement); // reload db since there is a different path for loading properties not in memory that we want to force imodel.close(); imodel = SnapshotDb.openFile(imodelPath); const readFromDbCopy =