Skip to content

Commit b1f5918

Browse files
add first partitioned mincut test
1 parent 617fc01 commit b1f5918

File tree

4 files changed

+176
-7
lines changed

4 files changed

+176
-7
lines changed

frontend/javascripts/test/helpers/apiHelpers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,10 @@ export async function setupWebknossosForTesting(
462462
setSceneController({
463463
name: "This is a dummy scene controller so that getSceneController works in the tests.",
464464
// @ts-ignore
465-
segmentMeshController: { meshesGroupsPerSegmentId: {} },
465+
segmentMeshController: {
466+
meshesGroupsPerSegmentId: {},
467+
updateActiveUnmappedSegmentIdHighlighting: vi.fn(),
468+
},
466469
});
467470

468471
try {

frontend/javascripts/test/sagas/proofreading/proofreading_mesh_interaction.spec.ts

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { setupWebknossosForTesting, type WebknossosTestContext } from "test/help
33
import { getMappingInfo } from "viewer/model/accessors/dataset_accessor";
44
import {
55
minCutAgglomerateWithPositionAction,
6+
minCutPartitionsAction,
67
proofreadMergeAction,
8+
toggleSegmentInPartitionAction,
79
} from "viewer/model/actions/proofread_actions";
810
import {
911
setActiveCellAction,
@@ -29,6 +31,7 @@ import type { Vector3 } from "viewer/constants";
2931
import type { MinCutTargetEdge } from "admin/rest_api";
3032
import _ from "lodash";
3133
import { delay } from "typed-redux-saga";
34+
import { updateUserSettingAction } from "viewer/model/actions/settings_actions";
3235

3336
describe("Proofreading (with mesh actions)", () => {
3437
beforeEach<WebknossosTestContext>(async (context) => {
@@ -182,7 +185,7 @@ describe("Proofreading (with mesh actions)", () => {
182185
await task.toPromise();
183186
}, 8000);
184187

185-
const mockEdgesForAgglomerateMinCut = (mocks: WebknossosTestContext["mocks"]) =>
188+
const mockEdgesForNormalAgglomerateMinCut = (mocks: WebknossosTestContext["mocks"]) =>
186189
vi.mocked(mocks.getEdgesForAgglomerateMinCut).mockImplementation(
187190
async (
188191
_tracingStoreUrl: string,
@@ -278,7 +281,7 @@ describe("Proofreading (with mesh actions)", () => {
278281
[1338, 1],
279282
]);
280283

281-
mockEdgesForAgglomerateMinCut(mocks);
284+
mockEdgesForNormalAgglomerateMinCut(mocks);
282285

283286
const { annotation } = Store.getState();
284287
const { tracingId } = annotation.volumes[0];
@@ -355,7 +358,7 @@ describe("Proofreading (with mesh actions)", () => {
355358
},
356359
]);
357360

358-
mockEdgesForAgglomerateMinCut(mocks);
361+
mockEdgesForNormalAgglomerateMinCut(mocks);
359362

360363
const { annotation } = Store.getState();
361364
const { tracingId } = annotation.volumes[0];
@@ -402,4 +405,168 @@ describe("Proofreading (with mesh actions)", () => {
402405

403406
await task.toPromise();
404407
}, 8000);
408+
409+
const mockEdgesForPartitionedAgglomerateMinCut = (mocks: WebknossosTestContext["mocks"]) =>
410+
vi.mocked(mocks.getEdgesForAgglomerateMinCut).mockImplementation(
411+
async (
412+
_tracingStoreUrl: string,
413+
_tracingId: string,
414+
segmentsInfo: {
415+
partition1: NumberLike[];
416+
partition2: NumberLike[];
417+
mag: Vector3;
418+
agglomerateId: NumberLike;
419+
editableMappingId: string;
420+
},
421+
): Promise<Array<MinCutTargetEdge>> => {
422+
const { agglomerateId, partition1, partition2 } = segmentsInfo;
423+
if (
424+
agglomerateId === 1 &&
425+
_.isEqual(partition1, [1, 2]) &&
426+
_.isEqual(partition2, [1337, 1338])
427+
) {
428+
return [
429+
{
430+
position1: [1, 1, 1],
431+
position2: [1338, 1338, 1338],
432+
segmentId1: 1,
433+
segmentId2: 1338,
434+
},
435+
{
436+
position1: [3, 3, 3],
437+
position2: [1337, 1337, 1337],
438+
segmentId1: 3,
439+
segmentId2: 1337,
440+
},
441+
];
442+
}
443+
throw new Error("Unexpected min cut request");
444+
},
445+
);
446+
447+
function* simulatePartitionedSplitAgglomeratesViaMeshes(
448+
context: WebknossosTestContext,
449+
): Generator<any, void, any> {
450+
const { api } = context;
451+
const { tracingId } = yield select((state: WebknossosState) => state.annotation.volumes[0]);
452+
const expectedInitialMapping = new Map([
453+
[1, 1],
454+
[2, 1],
455+
[3, 1],
456+
[4, 4],
457+
[5, 4],
458+
[6, 6],
459+
[7, 6],
460+
]);
461+
462+
yield call(initializeMappingAndTool, context, tracingId);
463+
const mapping0 = yield select(
464+
(state) =>
465+
getMappingInfo(state.temporaryConfiguration.activeMappingByLayer, tracingId).mapping,
466+
);
467+
expect(mapping0).toEqual(expectedInitialMapping);
468+
469+
// Set up the merge-related segment partners. Normally, this would happen
470+
// due to the user's interactions.
471+
yield put(updateSegmentAction(6, { somePosition: [1337, 1337, 1337] }, tracingId));
472+
yield put(setActiveCellAction(6, undefined, null, 1337));
473+
474+
yield call(createEditableMapping);
475+
476+
// After making the mapping editable, it should not have changed (as no other user did any update actions in between).
477+
const mapping1 = yield select(
478+
(state) =>
479+
getMappingInfo(state.temporaryConfiguration.activeMappingByLayer, tracingId).mapping,
480+
);
481+
expect(mapping1).toEqual(expectedInitialMapping);
482+
// setOthersMayEditForAnnotationAction must be after making the mapping editable as this action is not supported to be integrated.
483+
// TODOM: Support integrating this action, if it originates from this user.
484+
yield put(setOthersMayEditForAnnotationAction(true));
485+
486+
//Activate Multi-split tool
487+
yield put(updateUserSettingAction("isMultiSplitActive", true));
488+
// Select partition 1
489+
yield put(toggleSegmentInPartitionAction(1, 1, 1));
490+
yield put(toggleSegmentInPartitionAction(2, 1, 1));
491+
// Select partition 2
492+
yield put(toggleSegmentInPartitionAction(1337, 2, 1));
493+
yield put(toggleSegmentInPartitionAction(1338, 2, 1));
494+
// Execute the actual merge and wait for the finished mapping.
495+
yield put(minCutPartitionsAction());
496+
yield take("FINISH_MAPPING_INITIALIZATION");
497+
// Checking optimistic merge is not necessary as no "foreign" update was injected.
498+
yield call(() => api.tracing.save()); // Also pulls newest version from backend.
499+
}
500+
501+
it("should perform partitioned min-cut correctly", async (context: WebknossosTestContext) => {
502+
const { mocks } = context;
503+
// Initial mapping should be
504+
// [[1, 1],
505+
// [2, 1],
506+
// [3, 1],
507+
// [4, 4],
508+
// [5, 4],
509+
// [6, 6],
510+
// [7, 6],
511+
// [1337, 1],
512+
// [1338, 1]]
513+
// Thus, there should be the following circle of edges: 1-2-3-1337-1338-1.
514+
const _backendMock = mockInitialBucketAndAgglomerateData(context, [
515+
[1, 1338],
516+
[3, 1337],
517+
]);
518+
519+
mockEdgesForPartitionedAgglomerateMinCut(mocks);
520+
521+
const { annotation } = Store.getState();
522+
const { tracingId } = annotation.volumes[0];
523+
524+
// TODOM: Test is failing because interfering things are problematic
525+
const task = startSaga(function* task(): Generator<any, void, any> {
526+
yield simulatePartitionedSplitAgglomeratesViaMeshes(context);
527+
528+
const mergeSaveActionBatch = context.receivedDataPerSaveRequest.at(-1)![0]?.actions;
529+
530+
expect(mergeSaveActionBatch).toEqual([
531+
{
532+
name: "splitAgglomerate",
533+
value: {
534+
actionTracingId: "volumeTracingId",
535+
agglomerateId: 1,
536+
segmentId1: 1,
537+
segmentId2: 1338,
538+
},
539+
},
540+
{
541+
name: "splitAgglomerate",
542+
value: {
543+
actionTracingId: "volumeTracingId",
544+
agglomerateId: 1,
545+
segmentId1: 3,
546+
segmentId2: 1337,
547+
},
548+
},
549+
]);
550+
const finalMapping = yield select(
551+
(state) =>
552+
getMappingInfo(state.temporaryConfiguration.activeMappingByLayer, tracingId).mapping,
553+
);
554+
555+
expect(finalMapping).toEqual(
556+
new Map([
557+
[1, 1],
558+
[2, 1],
559+
[3, 1],
560+
[4, 4],
561+
[5, 4],
562+
[6, 6],
563+
[7, 6],
564+
[1337, 1339],
565+
[1338, 1339], // TODO: check why this is loaded
566+
]),
567+
);
568+
});
569+
570+
await task.toPromise();
571+
});
405572
});

frontend/javascripts/test/sagas/proofreading/proofreading_multi_user.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ describe("Proofreading (Multi User)", () => {
218218
await task.toPromise();
219219
}, 8000);
220220

221-
it("should split two agglomerates after incorporating a new merge action from backend", async (context: WebknossosTestContext) => {
221+
it("should cut agglomerate from all neighbors after incorporating a new merge action from backend", async (context: WebknossosTestContext) => {
222222
const { api } = context;
223223
const backendMock = mockInitialBucketAndAgglomerateData(context);
224224

frontend/javascripts/test/sagas/proofreading/proofreading_skeleton_interaction.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -762,9 +762,8 @@ describe("Proofreading (With Agglomerate Skeleton interactions)", () => {
762762
// TODO: code interleaving test where version is injected after agglomerate trees are created and before the merge.
763763
});
764764

765-
// TODOM: Write test for backend manipulating same agglomerate skeleton
765+
// TODOM: Write test for backend manipulating same agglomerate skeleton - should this be synched? check with others
766766

767-
// TODOM: write tests for cutFromAllNeighbours -> new test file
768767
// TODOM: write tests for partitionedMinCut -> new test file
769768
// TODOM: Write a test that pulling update fro the backend does not yield diff tracing changes that would be pushed to the backend in a live collab scenario!
770769
// TODOM: Write test without injected version and test via spy or so that no rebasing was tried and thus only the update actions were sent to the server!

0 commit comments

Comments
 (0)