integration.delete.HierarchyDeleteTest.java Source code

Java tutorial

Introduction

Here is the source code for integration.delete.HierarchyDeleteTest.java

Source

/*
 * $Id$
 *
 *   Copyright 2010 Glencoe Software, Inc. All rights reserved.
 *   Use is subject to license terms supplied in LICENSE.txt
 */
package integration.delete;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import integration.AbstractServerTest;
import integration.DeleteServiceTest;
import ome.parameters.Parameters;
import omero.RLong;
import omero.RType;
import omero.ServerError;
import omero.rtypes;
import omero.api.IAdminPrx;
import omero.api.IQueryPrx;
import omero.api.IUpdatePrx;
import omero.api.ServiceFactoryPrx;
import omero.cmd.Delete;
import omero.model.Annotation;
import omero.model.CommentAnnotationI;
import omero.model.Dataset;
import omero.model.DatasetI;
import omero.model.Experimenter;
import omero.model.ExperimenterGroup;
import omero.model.ExperimenterGroupI;
import omero.model.Image;
import omero.model.ImageAnnotationLink;
import omero.model.ImageAnnotationLinkI;
import omero.model.Plate;
import omero.model.PlateAcquisition;
import omero.model.PlateAcquisitionI;
import omero.model.PlateI;
import omero.model.Project;
import omero.model.ProjectI;
import omero.model.Roi;
import omero.model.Screen;
import omero.model.ScreenI;
import omero.model.Well;
import omero.model.WellI;
import omero.model.WellSample;
import omero.model.WellSampleI;
import omero.sys.ParametersI;

import org.apache.commons.collections.CollectionUtils;
import org.testng.Assert;
import org.testng.annotations.Test;
import static org.testng.AssertJUnit.*;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;

/**
 * Tests for deleting hierarchies and the effects that that should have under
 * double- and multiple-linking.
 *
 * @see ticket:3031
 * @see ticket:2994
 * @since 4.2.1
 */
@Test(groups = "ticket:2615")
public class HierarchyDeleteTest extends AbstractServerTest {

    private final static omero.RString t3031 = omero.rtypes.rstring("#3031");

    /**
     * Test to delete a dataset containing an image also contained in another
     * dataset. The second dataset and the image should not be deleted.
     *
     * @throws Exception
     *             Thrown if an error occurred.
     */
    @Test(groups = { "ticket:3031", "broken" })
    public void testDeletingDataset() throws Exception {

        newUserAndGroup("rwrw--");

        Dataset ds1 = new DatasetI();
        ds1.setName(t3031);

        Dataset ds2 = new DatasetI();
        ds2.setName(t3031);

        Image i1 = (Image) iUpdate.saveAndReturnObject(mmFactory.createImage());
        i1.unload();

        ds1.linkImage(i1);
        ds1 = (Dataset) iUpdate.saveAndReturnObject(ds1);
        ds2.linkImage(i1);
        ds2 = (Dataset) iUpdate.saveAndReturnObject(ds2);

        // http://trac.openmicroscopy.org.uk/ome/ticket/3031#comment:7
        // This image is only singly linked and should be removed.

        Image i2 = (Image) iUpdate.saveAndReturnObject(mmFactory.createImage());
        i2.unload();

        ds2.linkImage(i2);
        ds2 = (Dataset) iUpdate.saveAndReturnObject(ds2);

        delete(client, new Delete(DeleteServiceTest.REF_DATASET, ds2.getId().getValue(), null));

        assertDoesNotExist(ds2);
        assertDoesNotExist(i2);
        assertExists(ds1);
        assertExists(i1);

    }

    /**
     * Test to delete a dataset containing an image also contained in another
     * dataset. The second dataset and the image with an annotation should not
     * be deleted.
     *
     * @throws Exception
     *             Thrown if an error occurred.
     */
    @Test(groups = { "ticket:3031", "broken" })
    public void testDeletingDatasetDoesntRemoveImageAnnotation() throws Exception {

        newUserAndGroup("rwrw--");

        Dataset ds1 = new DatasetI();
        ds1.setName(t3031);

        Dataset ds2 = new DatasetI();
        ds2.setName(t3031);

        Image i = (Image) iUpdate.saveAndReturnObject(mmFactory.createImage());
        i.unload();

        ds1.linkImage(i);
        ds1 = (Dataset) iUpdate.saveAndReturnObject(ds1);
        ds2.linkImage(i);
        ds2 = (Dataset) iUpdate.saveAndReturnObject(ds2);

        Annotation a = (Annotation) iUpdate.saveAndReturnObject(new CommentAnnotationI());

        ImageAnnotationLink link = new ImageAnnotationLinkI();
        link.setChild((Annotation) a.proxy());
        link.setParent((Image) i.proxy());
        iUpdate.saveAndReturnObject(link);

        delete(client, new Delete(DeleteServiceTest.REF_DATASET, ds2.getId().getValue(), null));

        assertDoesNotExist(ds2);
        assertExists(ds1);
        assertExists(i);
        assertExists(a);
    }

    /**
     * Test to delete a dataset containing an image also contained in another
     * dataset. The second dataset and the image with ROI should not be deleted.
     *
     * @throws Exception
     *             Thrown if an error occurred.
     */
    @Test(groups = { "ticket:3031", "ticket:3032", "broken" })
    public void testDeletingDatasetDoesntRemoveImageRoi() throws Exception {

        newUserAndGroup("rwrw--");

        Dataset ds1 = new DatasetI();
        ds1.setName(t3031);

        Dataset ds2 = new DatasetI();
        ds2.setName(t3031);

        Image i = (Image) iUpdate.saveAndReturnObject(mmFactory.createImageWithRoi());
        Roi roi = i.copyRois().get(0);
        i.unload();

        ds1.linkImage(i);
        ds1 = (Dataset) iUpdate.saveAndReturnObject(ds1);
        ds2.linkImage(i);
        ds2 = (Dataset) iUpdate.saveAndReturnObject(ds2);

        delete(client, new Delete(DeleteServiceTest.REF_DATASET, ds2.getId().getValue(), null));

        assertDoesNotExist(ds2);
        assertExists(ds1);
        assertExists(i);
        assertExists(roi);
    }

    /**
     * Test to delete a project containing a dataset also contained in another
     * project. The second project and the dataset should not be deleted.
     *
     * @throws Exception
     *             Thrown if an error occurred.
     */
    @Test(groups = "ticket:3031")
    public void testDeletingProject() throws Exception {

        newUserAndGroup("rwrw--");

        Project p1 = new ProjectI();
        p1.setName(t3031);

        Project p2 = new ProjectI();
        p2.setName(t3031);

        Dataset d = new DatasetI();
        d.setName(t3031);
        d = (Dataset) iUpdate.saveAndReturnObject(d);
        d.unload();

        p1.linkDataset(d);
        p1 = (Project) iUpdate.saveAndReturnObject(p1);
        p2.linkDataset(d);
        p2 = (Project) iUpdate.saveAndReturnObject(p2);

        delete(client, new Delete(DeleteServiceTest.REF_PROJECT, p2.getId().getValue(), null));

        assertDoesNotExist(p2);
        assertExists(p1);
        assertExists(d);
    }

    /**
     * Test to delete a screen containing a plate also contained in another
     * screen. The second screen and the plate should not be deleted.
     *
     * @throws Exception
     *             Thrown if an error occurred.
     */
    @Test(groups = "ticket:3031")
    public void testDeletingScreen() throws Exception {

        newUserAndGroup("rwrw--");

        Screen s1 = new ScreenI();
        s1.setName(t3031);

        Screen s2 = new ScreenI();
        s2.setName(t3031);

        Plate p = mmFactory.createPlate(1, 1, 1, 1, false);
        p = (Plate) iUpdate.saveAndReturnObject(p);
        p.unload();

        s1.linkPlate(p);
        s1 = (Screen) iUpdate.saveAndReturnObject(s1);

        s2.linkPlate(p);
        s2 = (Screen) iUpdate.saveAndReturnObject(s2);

        delete(client, new Delete(DeleteServiceTest.REF_SCREEN, s2.getId().getValue(), null));

        assertDoesNotExist(s2);
        assertExists(s1);
        assertExists(p);
    }

    /**
     * Test that the correct wells, samples and images are deleted when runs of a plate are deleted.
     * @throws Exception unexpected
     */
    @Test(groups = "ticket:10564")
    public void testDeletingMultipleRunsWithOrphanedSamples() throws Exception {

        /* Create a new group. */

        final IAdminPrx rootAdminSvc = root.getSession().getAdminService();

        final String normalGroupName = UUID.randomUUID().toString();
        ExperimenterGroup normalGroup = new ExperimenterGroupI();
        normalGroup.setName(omero.rtypes.rstring(normalGroupName));
        normalGroup = rootAdminSvc.getGroup(rootAdminSvc.createGroup(normalGroup));

        /* Create a new user in that group. */

        final String userName = UUID.randomUUID().toString();
        final Experimenter experimenter = createExperimenterI(userName, "a", "user");
        rootAdminSvc.createUser(experimenter, normalGroupName);

        /* Create a session for the new user and obtain its query and update services. */

        final omero.client client = newOmeroClient();
        final ServiceFactoryPrx services = client.createSession(userName, normalGroupName);
        final IQueryPrx iQuery = services.getQueryService();
        final IUpdatePrx iUpdate = services.getUpdateService();

        /* Set up the size of the test.
         * The number of wells depends upon the number of runs because each well has a non-empty
         * set of well samples, each of which may be tied to some run or to no run. Many well samples
         * are created, to tie wells to every combination of runs and just to the plate. */

        final int numberOfRuns = 3;
        final int numberOfWells = (2 << numberOfRuns) - 1;

        /* Create the plate. */

        final Plate plate = new PlateI();
        plate.setName(rtypes.rstring(UUID.randomUUID().toString()));
        plate.setRows(rtypes.rint(1));
        plate.setColumns(rtypes.rint(numberOfWells));

        /* Create the wells of the plate. */

        final List<Well> wells = new ArrayList<Well>(numberOfWells);
        for (int columnNo = 1; columnNo <= numberOfWells; columnNo++) {
            final Well well = new WellI();
            well.setRow(rtypes.rint(1));
            well.setColumn(rtypes.rint(columnNo));
            plate.addWell(well);
            wells.add(well);
        }

        /* Create the runs of the plate. */

        final List<PlateAcquisition> runs = new ArrayList<PlateAcquisition>(numberOfRuns);
        for (int runNo = 1; runNo <= numberOfRuns; runNo++) {
            final PlateAcquisition run = new PlateAcquisitionI();
            run.setName(rtypes.rstring(UUID.randomUUID().toString()));
            plate.addPlateAcquisition(run);
            runs.add(run);
        }

        /* Create well samples to make each well a different combination
         * of being tied to runs and to only the plate. */

        /* Note that this runNo is 0-indexed instead of 1-indexed:
         * the last number represents just the plate rather than a specific run. */

        for (int wellIndex = 0; wellIndex < numberOfWells; wellIndex++) {
            for (int runNo = 0; runNo <= numberOfRuns; runNo++) {
                if ((wellIndex & (1 << runNo)) == 0) {
                    final WellSample sample = new WellSampleI();
                    sample.setImage(this.mmFactory.createImage());
                    wells.get(wellIndex).addWellSample(sample);
                    if (runNo < numberOfRuns) {
                        runs.get(runNo).addWellSample(sample);
                    }
                }
            }
        }

        /* Persist the assembled plate. */

        final long plateId = iUpdate.saveAndReturnObject(plate).getId().getValue();

        /* Create indices of the resulting entity IDs for the plate and how they are linked. */

        final Set<Long> runIds = new HashSet<Long>();
        final Multimap<Long, Long> runsToSamples = HashMultimap.create();
        final Multimap<Long, Long> wellsToRuns = HashMultimap.create();
        final Map<Long, Long> samplesToWells = new HashMap<Long, Long>();
        final Map<Long, Long> samplesToImages = new HashMap<Long, Long>();

        /* Index runs, wells and samples. */

        for (final List<RType> mapping : iQuery.projection(
                "SELECT sample.plateAcquisition.id, well.id, sample.id FROM Well well, WellSample sample "
                        + "WHERE well.id = sample.well.id AND well.plate.id = :" + Parameters.ID,
                new ParametersI().addId(plateId))) {
            final Long runId = mapping.get(0) == null ? null : ((RLong) mapping.get(0)).getValue();
            final long wellId = ((RLong) mapping.get(1)).getValue();
            final long sampleId = ((RLong) mapping.get(2)).getValue();
            if (runId != null) {
                runIds.add(runId);
            }
            runsToSamples.put(runId, sampleId);
            wellsToRuns.put(wellId, runId);
            samplesToWells.put(sampleId, wellId);
        }

        /* Index samples and images. */

        for (final List<RType> mapping : iQuery
                .projection("SELECT sample.id, sample.image.id FROM WellSample sample " + "WHERE sample.id IN (:"
                        + Parameters.IDS + ")", new ParametersI().addIds(samplesToWells.keySet()))) {
            final long sampleId = ((RLong) mapping.get(0)).getValue();
            final long imageId = ((RLong) mapping.get(1)).getValue();
            samplesToImages.put(sampleId, imageId);
        }

        /* Ensure that the wells are indeed tied to every combination of the expected number of runs. */

        final Set<Collection<Long>> runCombinations = new HashSet<Collection<Long>>(numberOfWells);
        for (final Collection<Long> runCombination : wellsToRuns.asMap().values()) {
            Assert.assertTrue(runCombinations.add(runCombination),
                    "expecting each sample to be in a unique combination of runs");
        }
        Assert.assertEquals(runCombinations.size(), numberOfWells,
                "expecting each run combination to be represented in a well");
        Assert.assertEquals(runIds.size(), numberOfRuns, "expecting the plate to have the correct number of runs");

        /* Before deleting, note the IDs of all the entities that once existed,
         * so that their deletion (rather than just unlinking) may be verified. */

        final ImmutableSet<Long> allRuns = ImmutableSet.copyOf(runIds);
        final ImmutableSet<Long> allWells = ImmutableSet.copyOf(samplesToWells.values());
        final ImmutableSet<Long> allSamples = ImmutableSet.copyOf(samplesToWells.keySet());
        final ImmutableSet<Long> allImages = ImmutableSet.copyOf(samplesToImages.values());

        /* Delete all the plate's runs one by one, ensuring that entities are deleted accordingly. */

        while (true) {
            /* Choose the next run to delete. */

            final Iterator<Long> runIdsIterator = runIds.iterator();
            if (!runIdsIterator.hasNext()) {
                break;
            }
            final long runIdToDelete = runIdsIterator.next();
            runIdsIterator.remove();

            /* Follow links to determine which entities are expected to remain afterward. */

            final Set<Long> expectedRuns = new HashSet<Long>();
            final Set<Long> expectedWells = new HashSet<Long>();
            final Set<Long> expectedSamples = new HashSet<Long>();
            final Set<Long> expectedImages = new HashSet<Long>();

            expectedRuns.addAll(runIds);
            expectedSamples.addAll(runsToSamples.get(null)); /* well samples not in runs */
            for (final Long runId : expectedRuns) {
                expectedSamples.addAll(runsToSamples.get(runId));
            }
            for (final Long sampleId : expectedSamples) {
                expectedWells.add(samplesToWells.get(sampleId));
                expectedImages.add(samplesToImages.get(sampleId));
            }

            /* Delete the run. */

            delete(client, new Delete(DeleteServiceTest.REF_PLATE_ACQUISITION, runIdToDelete, null));

            /* Verify that exactly the expected entities remain. */

            checkIds(iQuery, "PlateAcquisition", allRuns, expectedRuns);
            checkIds(iQuery, "Well", allWells, expectedWells);
            checkIds(iQuery, "WellSample", allSamples, expectedSamples);
            checkIds(iQuery, "Image", allImages, expectedImages);
        }

        /* Delete the plate. */

        delete(client, new Delete(DeleteServiceTest.REF_PLATE, plateId, null));

        /* Verify that no entities remain. */

        checkIds(iQuery, "PlateAcquisition", allRuns, Collections.<Long>emptySet());
        checkIds(iQuery, "Well", allWells, Collections.<Long>emptySet());
        checkIds(iQuery, "WellSample", allSamples, Collections.<Long>emptySet());
        checkIds(iQuery, "Image", allImages, Collections.<Long>emptySet());

        /* Close the new user's session. */

        client.closeSession();
    }

    /**
     * Verify that the persisted entities are exactly as expected from among a superset.
     * @param iQuery the query service to use
     * @param entityClass the name of the entity class to query
     * @param allIds the IDs of the entities that may exist
     * @param expectedIds the IDs of the entities that should exist, a subset of <code>allIds</code>
     * @throws ServerError if the query fails
     */
    private void checkIds(IQueryPrx iQuery, String entityClass, Collection<Long> allIds,
            Collection<Long> expectedIds) throws ServerError {
        final Set<Long> actualIds = new HashSet<Long>(expectedIds.size());
        for (final List<RType> wrappedId : iQuery.projection(
                "SELECT id FROM " + entityClass + " WHERE id IN (:" + Parameters.IDS + ")",
                new ParametersI().addIds(allIds))) {
            actualIds.add(((RLong) wrappedId.get(0)).getValue());
        }
        Assert.assertTrue(CollectionUtils.isEqualCollection(actualIds, expectedIds),
                "persisted entities not as expected: " + entityClass);
    }
}