UNPKG

@itwin/core-backend

Version:
383 lines • 25 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { Id64 } from "@itwin/core-bentley"; import { Code, IModel, SubCategoryAppearance } from "@itwin/core-common"; import { Point2d } from "@itwin/core-geometry"; import { assert } from "chai"; import * as path from "path"; import * as sinon from "sinon"; import { DefinitionContainer, DefinitionModel, DocumentListModel, Drawing, DrawingCategory, DrawingGraphic, ElementGroupsMembers, ElementOwnsChildElements, ExternalSource, ExternalSourceGroup, PhysicalPartition, SnapshotDb, SpatialCategory, SubCategory, Subject } from "../../core-backend"; import { deleteElementSubTrees, deleteElementTree, ElementTreeBottomUp, ElementTreeWalkerScope } from "../../ElementTreeWalker"; import { IModelTestUtils } from "../IModelTestUtils"; import { KnownTestLocations } from "../KnownTestLocations"; // Test class that collects the results of a bottom-up tree walk class ElementTreeCollector extends ElementTreeBottomUp { subModels = []; definitionModels = []; elements = []; definitions = []; constructor(iModel) { super(iModel); } visitModel(model, _scope) { if (model instanceof DefinitionModel) this.definitionModels.push(model.id); else this.subModels.push(model.id); } visitElement(elementId, scope) { if (scope.inDefinitionModel) this.definitions.push(elementId); // may be some other kind of InformationContentElement - that's OK. else this.elements.push(elementId); } collect(topElement) { this.processElementTree(topElement, ElementTreeWalkerScope.createTopScope(this._iModel, topElement)); } } class SelectedElementCollector extends ElementTreeCollector { _elementsToReport; constructor(iModel, _elementsToReport) { super(iModel); this._elementsToReport = _elementsToReport; } shouldExploreModel(_model) { return true; } shouldVisitModel(_model) { return false; } shouldVisitElement(elementId) { return this._elementsToReport.includes(elementId); } } function doesElementExist(iModel, elementId) { return iModel.elements.tryGetElementProps(elementId) !== undefined; } function doesModelExist(iModel, mid) { return iModel.models.tryGetModelProps(mid) !== undefined; } function doesGroupRelationshipExist(iModel, source, target) { // eslint-disable-next-line @typescript-eslint/no-deprecated return iModel.withPreparedStatement(`select count(*) from ${ElementGroupsMembers.classFullName} where sourceecinstanceid=? and targetecinstanceid=?`, (stmt) => { stmt.bindId(1, source); stmt.bindId(2, target); stmt.step(); return stmt.getValue(0).getInteger() !== 0; }); } describe("ElementTreeWalker", () => { let iModel; let originalEnv; let repositoryLinkId; let jobSubjectId; let childSubject; let definitionModelId; let definitionContainerId; let drawingDefinitionModelId; let spatialCategoryId; let nestedSpatialCategoryId; let drawingCategoryId; let drawingSubCategory1Id; let drawingSubCategory2Id; let xsGroup; let xsElement; let documentListModelId; let drawingModelId; let drawingGraphicId1; let physicalModelId; let physicalObjectId1; let physicalObjectId2; let physicalObjectId3; before(async () => { originalEnv = { ...process.env }; IModelTestUtils.registerTestBimSchema(); }); after(() => { process.env = originalEnv; }); beforeEach(async () => { // Uncomment the following two lines to debug test failures // Logger.initializeToConsole(); // Logger.setLevel("core-backend.IModelDb.ElementTreeWalker", LogLevel.Trace); const iModelFileName = IModelTestUtils.prepareOutputFile("ElementTreeWalker", "Test.bim"); iModel = SnapshotDb.createEmpty(iModelFileName, { rootSubject: { name: "ElementTreeWalker Test" } }); const schemaPathname = path.join(KnownTestLocations.assetsDir, "TestBim.ecschema.xml"); await iModel.importSchemas([schemaPathname]); // will throw an exception if import fails /* [RepositoryModel] RepositoryLink Job Subject +- DefinitionParitition -- [DefinitionModel] | DrawingCategory | default SubCategory + 2 non-default SubCategories | ExternalSourceGroup | ExternalSource child1 +- DefinitionParitition -- [DefinitionModel] | SpatialCategory | DefinitionContainer | SpatialCategory | +- DocumentList -- [DocumentListModel] | Drawing -- [DrawingModel] | DrawingGraphic +- Child Subject | +- PhysicalPartition -- [PhysicalModel] PhysicalObject, PhysicalObject, PhysicalObject (grouped) */ repositoryLinkId = IModelTestUtils.insertRepositoryLink(iModel, "test link", "foo", "bar"); jobSubjectId = IModelTestUtils.createJobSubjectElement(iModel, "Job").insert(); childSubject = Subject.insert(iModel, jobSubjectId, "Child Subject"); definitionModelId = DefinitionModel.insert(iModel, jobSubjectId, "Definition"); spatialCategoryId = SpatialCategory.insert(iModel, definitionModelId, "SpatialCategory", new SubCategoryAppearance()); drawingDefinitionModelId = DefinitionModel.insert(iModel, jobSubjectId, "DrawingDefinition"); drawingCategoryId = DrawingCategory.insert(iModel, drawingDefinitionModelId, "DrawingCategory", new SubCategoryAppearance()); drawingSubCategory1Id = SubCategory.insert(iModel, drawingCategoryId, "SubCategory1", new SubCategoryAppearance()); drawingSubCategory2Id = SubCategory.insert(iModel, drawingCategoryId, "SubCategory2", new SubCategoryAppearance()); definitionContainerId = DefinitionContainer.insert(iModel, definitionModelId, Code.createEmpty()); nestedSpatialCategoryId = SpatialCategory.insert(iModel, definitionContainerId, "nested", {}); xsGroup = iModel.elements.insertElement({ classFullName: ExternalSourceGroup.classFullName, model: drawingDefinitionModelId, code: Code.createEmpty() }); xsElement = iModel.elements.insertElement({ classFullName: ExternalSource.classFullName, model: drawingDefinitionModelId, parent: new ElementOwnsChildElements(xsGroup), code: Code.createEmpty() }); documentListModelId = DocumentListModel.insert(iModel, jobSubjectId, "Document"); assert.isTrue(Id64.isValidId64(documentListModelId)); drawingModelId = Drawing.insert(iModel, documentListModelId, "Drawing"); const drawingGraphicProps1 = { classFullName: DrawingGraphic.classFullName, model: drawingModelId, category: drawingCategoryId, code: Code.createEmpty(), userLabel: "DrawingGraphic1", geom: IModelTestUtils.createRectangle(Point2d.create(1, 1)), placement: { origin: Point2d.create(2, 2), angle: 0 }, }; drawingGraphicId1 = iModel.elements.insertElement(drawingGraphicProps1); [, physicalModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(iModel, PhysicalPartition.createCode(iModel, childSubject, "Physical"), false, childSubject); const elementProps = { classFullName: "TestBim:TestPhysicalObject", model: physicalModelId, category: spatialCategoryId, code: Code.createEmpty(), }; const elementProps2 = { classFullName: "TestBim:TestPhysicalObject", model: physicalModelId, category: nestedSpatialCategoryId, code: Code.createEmpty(), }; physicalObjectId1 = iModel.elements.insertElement(iModel.elements.createElement(elementProps).toJSON()); physicalObjectId2 = iModel.elements.insertElement(iModel.elements.createElement(elementProps2).toJSON()); physicalObjectId3 = iModel.elements.insertElement(iModel.elements.createElement(elementProps).toJSON()); ElementGroupsMembers.create(iModel, physicalObjectId1, physicalObjectId2).insert(); ElementGroupsMembers.create(iModel, physicalObjectId1, physicalObjectId3).insert(); assert.isTrue(doesElementExist(iModel, repositoryLinkId)); assert.equal(iModel.elements.getElement(jobSubjectId).parent?.id, IModel.rootSubjectId); assert.equal(iModel.elements.getElement(definitionModelId).parent?.id, jobSubjectId); assert.equal(iModel.elements.getElement(definitionContainerId).model, definitionModelId); assert.equal(iModel.elements.getElement(spatialCategoryId).model, definitionModelId); assert.equal(iModel.elements.getElement(nestedSpatialCategoryId).model, definitionContainerId); assert.equal(iModel.elements.getElement(drawingDefinitionModelId).parent?.id, jobSubjectId); assert.equal(iModel.elements.getElement(drawingCategoryId).model, drawingDefinitionModelId); assert.equal(iModel.elements.getElement(drawingSubCategory1Id).parent?.id, drawingCategoryId); assert.equal(iModel.elements.getElement(drawingSubCategory2Id).parent?.id, drawingCategoryId); assert.equal(iModel.elements.getElement(xsGroup).model, drawingDefinitionModelId); assert.equal(iModel.elements.getElement(xsElement).parent?.id, xsGroup); assert.equal(iModel.elements.getElement(documentListModelId).parent?.id, jobSubjectId); assert.equal(iModel.elements.getElement(drawingModelId).model, documentListModelId); assert.equal(iModel.elements.getElement(drawingGraphicId1).model, drawingModelId); assert.equal(iModel.elements.getElement(physicalModelId).parent?.id, childSubject); assert.equal(iModel.elements.getElement(physicalObjectId1).model, physicalModelId); assert.equal(iModel.elements.getElement(physicalObjectId2).model, physicalModelId); assert.equal(iModel.elements.getElement(physicalObjectId3).model, physicalModelId); assert.isTrue(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId2)); assert.isTrue(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId3)); assert.isTrue(doesModelExist(iModel, definitionModelId)); assert.isTrue(doesModelExist(iModel, definitionContainerId)); assert.isTrue(doesModelExist(iModel, drawingDefinitionModelId)); assert.isTrue(doesModelExist(iModel, drawingModelId)); assert.isTrue(doesModelExist(iModel, physicalModelId)); }); afterEach(() => { sinon.restore(); iModel.close(); }); it("DFS search and deleteElementTree", () => { // First, check that DFS search visits elements and models in expected bottom-up order { const collector1 = new ElementTreeCollector(iModel); collector1.collect(jobSubjectId); assert.isTrue(collector1.subModels.includes(physicalModelId)); assert.isTrue(collector1.subModels.includes(drawingModelId)); assert.isTrue(collector1.subModels.includes(documentListModelId)); assert.isTrue(collector1.subModels.indexOf(drawingModelId) < collector1.subModels.indexOf(documentListModelId), "in bottom-up search, a child model should be visited before its parent model"); assert.isFalse(collector1.subModels.includes(definitionModelId)); assert.isTrue(collector1.definitionModels.includes(definitionModelId)); assert.isFalse(collector1.subModels.includes(definitionContainerId)); assert.isTrue(collector1.definitionModels.includes(definitionContainerId)); assert.isFalse(collector1.subModels.includes(drawingDefinitionModelId)); assert.isTrue(collector1.definitionModels.includes(drawingDefinitionModelId)); assert.isTrue(collector1.definitions.includes(drawingCategoryId)); assert.isTrue(collector1.definitions.includes(spatialCategoryId)); assert.isTrue(collector1.definitions.includes(nestedSpatialCategoryId)); assert.isFalse(collector1.elements.includes(drawingCategoryId)); assert.isFalse(collector1.elements.includes(spatialCategoryId)); assert.isFalse(collector1.elements.includes(nestedSpatialCategoryId)); assert.isTrue(collector1.elements.indexOf(physicalObjectId1) < collector1.elements.indexOf(physicalModelId), "in bottom-up search, an element in a model should be visited before its model's element"); assert.isTrue(collector1.elements.indexOf(drawingGraphicId1) < collector1.elements.indexOf(drawingModelId), "in bottom-up search, an element in a model should be visited before its model's element"); assert.isTrue(collector1.elements.indexOf(drawingModelId) < collector1.elements.indexOf(documentListModelId), "in bottom-up search, an element in a model should be visited before its model's element"); assert.isTrue(collector1.elements.indexOf(documentListModelId) < collector1.elements.indexOf(jobSubjectId), "in bottom-up search, a child element should be visited before its parent element"); assert.isTrue(collector1.elements.indexOf(definitionModelId) < collector1.elements.indexOf(jobSubjectId), "in bottom-up search, a child element should be visited before its parent element"); assert.isTrue(collector1.elements.indexOf(definitionContainerId) < collector1.elements.indexOf(definitionModelId), "in bottom-up search, a child element should be visited before its parent element"); assert.isTrue(collector1.elements.indexOf(drawingDefinitionModelId) < collector1.elements.indexOf(jobSubjectId), "in bottom-up search, a child element should be visited before its parent element"); assert.isTrue(collector1.elements.indexOf(childSubject) < collector1.elements.indexOf(jobSubjectId), "in bottom-up search, a child element should be visited before its parent element"); assert.isTrue(collector1.elements.indexOf(physicalModelId) < collector1.elements.indexOf(childSubject), "in bottom-up search, a child element should be visited before its parent element"); } // Exercise the search filters { const collector2 = new SelectedElementCollector(iModel, [drawingGraphicId1, spatialCategoryId, nestedSpatialCategoryId, physicalObjectId3]); collector2.collect(jobSubjectId); assert.isTrue(collector2.definitions.length === 2); assert.isTrue(collector2.definitions.includes(spatialCategoryId)); assert.isTrue(collector2.definitions.includes(nestedSpatialCategoryId)); assert.isTrue(collector2.elements.length === 2); assert.isTrue(collector2.elements.includes(drawingGraphicId1)); assert.isTrue(collector2.elements.includes(physicalObjectId3)); } // Test the deleteElementTree function deleteElementTree(iModel, jobSubjectId); assert.isTrue(doesModelExist(iModel, IModel.repositoryModelId)); assert.isTrue(doesModelExist(iModel, IModel.dictionaryId)); assert.isTrue(doesElementExist(iModel, repositoryLinkId), "RepositoryLink should not have been deleted, since it is not under Job Subject"); assert.isFalse(doesElementExist(iModel, definitionModelId)); assert.isFalse(doesElementExist(iModel, definitionContainerId)); assert.isFalse(doesElementExist(iModel, drawingDefinitionModelId)); assert.isFalse(doesElementExist(iModel, spatialCategoryId)); assert.isFalse(doesElementExist(iModel, nestedSpatialCategoryId)); assert.isFalse(doesElementExist(iModel, drawingCategoryId)); assert.isFalse(doesElementExist(iModel, xsGroup)); assert.isFalse(doesElementExist(iModel, xsElement)); assert.isFalse(doesElementExist(iModel, documentListModelId)); assert.isFalse(doesElementExist(iModel, drawingModelId)); assert.isFalse(doesElementExist(iModel, drawingGraphicId1)); assert.isFalse(doesElementExist(iModel, physicalModelId)); assert.isFalse(doesElementExist(iModel, physicalObjectId1)); assert.isFalse(doesElementExist(iModel, physicalObjectId2)); assert.isFalse(doesElementExist(iModel, physicalObjectId3)); assert.isFalse(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId2)); assert.isFalse(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId3)); assert.isFalse(doesElementExist(iModel, jobSubjectId)); assert.isFalse(doesModelExist(iModel, definitionModelId)); assert.isFalse(doesModelExist(iModel, drawingDefinitionModelId)); assert.isFalse(doesModelExist(iModel, drawingModelId)); assert.isFalse(doesModelExist(iModel, physicalModelId)); }); it("deleteElementSubTrees", () => { /* [RepositoryModel] RepositoryLink Job Subject +- DefinitionParitition -- [DefinitionModel] | DrawingCategory <-- PRUNE | default SubCategory + 2 non-default SubCategories | ExternalSourceGroup | ExternalSource child1 +- DefinitionParitition -- [DefinitionModel] | SpatialCategory | DefinitionContainer | SpatialCategory | +- DocumentList -- [DocumentListModel] | Drawing -- [DrawingModel] <-- PRUNE | DrawingGraphic " +- Child Subject | +- PhysicalPartition -- [PhysicalModel] PhysicalObject, PhysicalObject, PhysicalObject (grouped) ^-- PRUNE */ const toPrune = new Set(); toPrune.add(drawingModelId); toPrune.add(drawingCategoryId); toPrune.add(physicalObjectId3); deleteElementSubTrees(iModel, jobSubjectId, (elementId) => toPrune.has(elementId)); assert.isFalse(doesElementExist(iModel, drawingCategoryId)); assert.isFalse(doesElementExist(iModel, drawingSubCategory1Id)); assert.isFalse(doesElementExist(iModel, drawingSubCategory2Id)); assert.isFalse(doesElementExist(iModel, drawingModelId)); assert.isFalse(doesModelExist(iModel, drawingModelId)); assert.isFalse(doesElementExist(iModel, drawingGraphicId1)); // contents of drawing model should be gone assert.isFalse(doesElementExist(iModel, physicalObjectId3)); assert.isFalse(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId3)); assert.isTrue(doesElementExist(iModel, repositoryLinkId)); assert.isTrue(doesElementExist(iModel, definitionModelId)); assert.isTrue(doesElementExist(iModel, definitionContainerId)); assert.isTrue(doesElementExist(iModel, drawingDefinitionModelId)); assert.equal(iModel.elements.getElement(xsGroup).model, drawingDefinitionModelId); assert.equal(iModel.elements.getElement(xsElement).parent?.id, xsGroup); assert.isTrue(doesElementExist(iModel, spatialCategoryId)); assert.isTrue(doesElementExist(iModel, nestedSpatialCategoryId)); assert.isTrue(doesElementExist(iModel, documentListModelId)); assert.isTrue(doesElementExist(iModel, physicalModelId)); assert.isTrue(doesElementExist(iModel, physicalObjectId1)); assert.isTrue(doesElementExist(iModel, physicalObjectId2)); assert.isTrue(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId2)); assert.isTrue(doesElementExist(iModel, jobSubjectId)); assert.isTrue(doesModelExist(iModel, definitionModelId)); assert.isTrue(doesModelExist(iModel, definitionContainerId)); assert.isTrue(doesModelExist(iModel, drawingDefinitionModelId)); assert.isTrue(doesModelExist(iModel, physicalModelId)); assert.isTrue(doesModelExist(iModel, IModel.repositoryModelId)); assert.isTrue(doesModelExist(iModel, IModel.dictionaryId)); }); it("deleteDefinitionPartition", () => { /* [RepositoryModel] RepositoryLink Job Subject +- DefinitionParitition -- [DefinitionModel] <-- PRUNE | DrawingCategory | default SubCategory + 2 non-default SubCategories | ExternalSourceGroup | ExternalSource child1 +- DefinitionParitition -- [DefinitionModel] | SpatialCategory | DefinitionContainer | SpatialCategory | +- DocumentList -- [DocumentListModel] | Drawing -- [DrawingModel] | DrawingGraphic +- Child Subject | +- PhysicalPartition -- [PhysicalModel] PhysicalObject, PhysicalObject, PhysicalObject (grouped) */ const toPrune = new Set(); toPrune.add(drawingDefinitionModelId); toPrune.add(documentListModelId); // (also get rid of the elements that use the definitions) deleteElementSubTrees(iModel, jobSubjectId, (elementId) => toPrune.has(elementId)); assert.isFalse(doesElementExist(iModel, drawingDefinitionModelId)); assert.isFalse(doesModelExist(iModel, drawingDefinitionModelId)); assert.isFalse(doesElementExist(iModel, drawingCategoryId)); assert.isFalse(doesElementExist(iModel, drawingSubCategory1Id)); assert.isFalse(doesElementExist(iModel, drawingSubCategory2Id)); assert.isFalse(doesElementExist(iModel, xsGroup)); assert.isFalse(doesElementExist(iModel, xsElement)); assert.isFalse(doesElementExist(iModel, documentListModelId)); assert.isFalse(doesModelExist(iModel, documentListModelId)); assert.isFalse(doesElementExist(iModel, drawingModelId)); assert.isFalse(doesModelExist(iModel, drawingModelId)); assert.isFalse(doesElementExist(iModel, drawingGraphicId1)); assert.isTrue(doesElementExist(iModel, repositoryLinkId)); assert.isTrue(doesElementExist(iModel, definitionModelId)); assert.isTrue(doesElementExist(iModel, definitionContainerId)); assert.isTrue(doesElementExist(iModel, spatialCategoryId)); assert.isTrue(doesElementExist(iModel, nestedSpatialCategoryId)); assert.isTrue(doesElementExist(iModel, physicalModelId)); assert.isTrue(doesElementExist(iModel, physicalObjectId1)); assert.isTrue(doesElementExist(iModel, physicalObjectId2)); assert.isTrue(doesElementExist(iModel, physicalObjectId3)); assert.isTrue(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId2)); assert.isTrue(doesGroupRelationshipExist(iModel, physicalObjectId1, physicalObjectId3)); assert.isTrue(doesElementExist(iModel, jobSubjectId)); assert.isTrue(doesModelExist(iModel, definitionModelId)); assert.isTrue(doesModelExist(iModel, definitionContainerId)); assert.isTrue(doesModelExist(iModel, physicalModelId)); assert.isTrue(doesModelExist(iModel, IModel.repositoryModelId)); assert.isTrue(doesModelExist(iModel, IModel.dictionaryId)); }); }); //# sourceMappingURL=ElementTreeWalker.test.js.map