ome.io.nio.utests.GetPixelBufferUnitTest.java Source code

Java tutorial

Introduction

Here is the source code for ome.io.nio.utests.GetPixelBufferUnitTest.java

Source

/*
 *   $Id$
 *
 *   Copyright 2011 Glencoe Software, Inc. All rights reserved.
 *   Use is subject to license terms supplied in LICENSE.txt
 */
package ome.io.nio.utests;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import loci.formats.FormatException;

import ome.conditions.MissingPyramidException;
import ome.conditions.ResourceError;
import ome.io.bioformats.BfPyramidPixelBuffer;
import ome.io.messages.MissingPyramidMessage;
import ome.io.nio.PixelBuffer;
import ome.io.nio.PixelsService;
import ome.model.core.Pixels;
import ome.model.enums.PixelsType;

import org.apache.commons.io.FileUtils;
import org.jmock.Mock;
import org.jmock.MockObjectTestCase;
import org.jmock.core.stub.DefaultResultStub;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Tests the logic of
 * {@link PixelsService#getPixelBuffer(Pixels, String, OriginalFileMetadataProvider, boolean)}
 * which has gotten far more complex with the introduction pyramids to support
 * big images.
 *
 * @since 4.3
 */
public class GetPixelBufferUnitTest extends MockObjectTestCase {

    private String root;

    private ome.model.core.Pixels pixels;

    private PixelBuffer pixelBuffer;

    private MockPixelsService service;

    @BeforeMethod
    private void setup() {
        root = PathUtil.getInstance().getTemporaryDataFilePath();
        pixels = new Pixels();

        pixels.setId(1L);
        pixels.setSizeX(1);
        pixels.setSizeY(1);
        pixels.setSizeZ(1);
        pixels.setSizeC(1);
        pixels.setSizeT(1);

        PixelsType type = new PixelsType();
        type.setValue("uint16");
        pixels.setPixelsType(type);

        service = new MockPixelsService(root);
    }

    @AfterMethod
    public void tearDown() throws IOException {
        FileUtils.deleteDirectory(new File(root));
    }

    @Test
    public void testWhenPyramidRequiredOnlyCreatePyramidCalled() {
        service.stubPyramid(true);
        service.isRequirePyramid = true;
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /**
     * Since no pyramid is required and no ROMIO pixels file exists,
     * then a new one will be created (i.e. pixel buffer is READ-WRITE).
     */
    @Test
    public void testWhenPyramidNotRequiredCreateRomioCalled() {
        service.stubRomio(true);
        service.isRequirePyramid = false;
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /**
     * This test creates the ROMIO pixels file while requiring a pyramid which
     * forces an exception to be thrown.
     */
    @Test(expectedExceptions = MissingPyramidException.class)
    public void testPyramidMessagePublishedIfPixelsExist() throws Exception {
        service.stubPyramid(false);
        service.isRequirePyramid = true;
        service.retry = false;
        String path = touchRomio();
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(1, service.events.size());
    }

    @Test
    public void testPyramidMessageNotPublishedIfPyramidExists() throws Exception {
        service.stubPyramid(false);
        service.isRequirePyramid = true;
        service.retry = null; // no messages!
        touchRomio();
        touchPyramid();
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /**
     * This tests whether or not a pyramid is used even when it is not required.
     */
    @Test
    public void testReusePyramid() throws Exception {
        service.stubPyramid(false);
        service.isRequirePyramid = false;
        String path = service.getPixelsPath(pixels.getId());
        touchPyramid();
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /**
     * By passing a null to getPixelBuffer for the string, one sets useRomio.
     * Here we pass an absolute path, asking to NOT use romio.
     */
    @Test(expectedExceptions = MissingPyramidException.class)
    public void testDontUseRomio() {
        service.stubBf();
        service.isRequirePyramid = false; // Ignored; Original implies pyramid
        service.retry = false;
        service.path = new File(root, "bf.tiff").getAbsolutePath();
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /** OriginalFile != null implies pyramid required currently. */
    @Test(expectedExceptions = MissingPyramidException.class)
    public void testHandleOriginalFileReturnsSomething() {
        service.stubBf();
        service.isRequirePyramid = false; // ignored.
        service.retry = false;
        service.path = "/tmp/foo";
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(1, service.events.size());
    }

    /**
     * If handleOriginalFile returns a null, then a PB must be created. In this
     * case, another method must be allowed. Here, it is createPyramid.
     */
    @Test
    public void testHandleOriginalFileReturnsNullForPyramid() {
        service.realPyramid(null);
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /**
     * Like {@link #testHandleOriginalFileReturnsNullForPyramid()} but here no
     * pyramid is required, so createRomio is called WITH allowModification set.
     */
    @Test
    public void testHandleOriginalFileReturnsNullForRomio() {
        service.stubRomio(true);
        service.retry = false;
        service.isRequirePyramid = false;
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    /**
     * This is the traditional case: a ROMIO pixels file exists and there is not
     * pyramid, nor is one required and bypassOriginalFile is true, i.e. don't
     * look up anything. Returned PixelBuffer should be READ-ONLY.
     */
    @Test
    public void testPixelsFileExistsNoPyramid() throws Exception {
        service.stubRomio(false);
        service.isRequirePyramid = false;
        touchRomio();
        pixelBuffer = service.getPixelBuffer(pixels);
        assertEquals(0, service.events.size());
    }

    //
    // #4731 makePyramid
    //

    @Test
    public void testMakePyramidNoRomioNoArchived() throws Exception {
        service.stubPyramid(true); // 5189. allow creation for close to touch.
        assertEquals(null, service.makePyramid(pixels));
    }

    @Test
    public void testMakePyramidRomio() throws Exception {
        touchRomio();
        service.stubPyramid(true);
        service.stubRomio(false);
        service.makePyramid(pixels);
    }

    //
    // #5189 Fatal error issues to prevent loops
    //

    /**
     * Here a pyramid cannot be created because no romio file exists. After
     * the invocation, the pyramid file should exist but be empty.
     */
    @Test
    public void testFatalErrorInGeneratingPyramidNoRomio() throws Exception {
        service.stubPyramid(true);
        service.stubRomio(false);
        assertEquals(null, sizePyramid());
        File pyramid = filePyramid();
        if (!pyramid.getParentFile().exists()) {
            pyramid.getParentFile().mkdirs();
        }
        service.pyramid.buffer = new BfPyramidPixelBuffer(pixels, pyramid.getAbsolutePath(), true);
        try {
            service.makePyramid(pixels);
            assertEquals(0, sizePyramid().longValue());

            assertCorruptPyramid(pyramid);
        } finally {
            service.pyramid.buffer.close();
        }
    }

    /**
     * Here we try the creation with a corrupt romio file.
     */
    @Test
    public void testFatalErrorInGeneratingPyramidWithRomio() throws Exception {
        touchRomio();
        service.stubPyramid(true);
        Mock m = service.mockRomio(false);
        m.expects(once()).method("close");
        m.expects(atLeastOnce()).method("getSizeX").will(returnValue(pixels.getSizeX()));
        m.expects(atLeastOnce()).method("getSizeY").will(returnValue(pixels.getSizeY()));
        m.expects(atLeastOnce()).method("getSizeZ").will(returnValue(pixels.getSizeZ()));
        m.expects(atLeastOnce()).method("getSizeC").will(returnValue(pixels.getSizeC()));
        m.expects(atLeastOnce()).method("getSizeT").will(returnValue(pixels.getSizeT()));
        m.expects(once()).method("getTile").will(throwException(new IOException("MOCK")));

        assertEquals(null, sizePyramid());
        File pyramid = filePyramid();
        service.pyramid.buffer = new BfPyramidPixelBuffer(pixels, pyramid.getAbsolutePath(), true);
        try {
            service.makePyramid(pixels);
            assertEquals(0, sizePyramid().longValue());

            assertCorruptPyramid(pyramid);
        } finally {
            service.pyramid.buffer.close();
        }
    }

    private void assertCorruptPyramid(File pyramid) throws IOException, FormatException {

        BfPyramidPixelBuffer pb = null;
        try {
            pb = new BfPyramidPixelBuffer(pixels, pyramid.getAbsolutePath(), false);
            assertEquals(false, pb.isWrite());
            pb.getTileSize();
            fail("Should have thrown a resource error");
        } catch (ResourceError re) {
            // ok
        } finally {
            pb.close();
        }
    }

    //
    // Helpers
    //

    private String makePyramidPath(String path) {
        String pyramidPath = path + PixelsService.PYRAMID_SUFFIX;
        return pyramidPath;
    }

    private File filePyramid() {
        String path = service.getPixelsPath(pixels.getId());
        String pyramidPath = makePyramidPath(path);
        File pyramidFile = new File(pyramidPath);
        return pyramidFile;
    }

    /** Returns null if the file does not exist */
    private Long sizePyramid() throws Exception {
        File pyramidFile = filePyramid();
        if (pyramidFile.exists()) {
            return pyramidFile.length();
        } else {
            return null;
        }
    }

    private void touchPyramid() throws IOException {
        File pyramidFile = filePyramid();
        FileUtils.touch(pyramidFile);
    }

    private File fileRomio() throws Exception {
        String path = service.getPixelsPath(pixels.getId());
        return new File(path);

    }

    private String touchRomio() throws Exception {
        File romioFile = fileRomio();
        FileUtils.touch(romioFile);
        return romioFile.getAbsolutePath();
    }

    /**
     * Container for the return value of MockPixelsService.
     */
    static class Buffer {

        Mock mock;

        /**
         * Returned value from all the create methods. Typically ignored.
         * Setting it to null, however, will influence the outcome of calls to
         * {@link #handleOriginalFile(Pixels, OriginalFileMetadataProvider)}.
         */
        PixelBuffer buffer;

        /**
         * If null, then modification is not checked. Otherwise, must match
         * the value passed in.
         */
        Boolean modification;

        Buffer() {
            this(true);
        }

        Buffer(boolean stub) {
            mock = new Mock(PixelBuffer.class);
            buffer = (PixelBuffer) mock.proxy();
            if (stub) {
                mock.setDefaultStub(new DefaultResultStub());
            }
        }
    }

    static class MockPixelsService extends PixelsService {

        /** Whether or not this image is "big" */
        boolean isRequirePyramid = true;

        /** If null, then createPyramid is not allowed */
        Buffer pyramid;

        /** If null, then createRomio is not allowed */
        Buffer romio;

        /** If null, then createBf is not allowed */
        Buffer bf;

        /**
         * If null, then no call should be made to
         * {@link #handleMissingPyramid(Pixels, String)}. Otherwise, the value
         * will determine whether or not
         * {@link MissingPyramidMessage#setRetry()} is called.
         */
        Boolean retry = null;

        /**
         * Path to be returned by invocations of {@link #getOriginalFilePath(Pixels)}
         */
        String path = null;

        /**
         * Storage of all events which are raised.
         */
        final List<ApplicationEvent> events = new ArrayList<ApplicationEvent>();

        MockPixelsService(String root) {
            super(root);
            setApplicationEventPublisher(new ApplicationEventPublisher() {
                public void publishEvent(ApplicationEvent arg0) {
                    MissingPyramidMessage mpm = (MissingPyramidMessage) arg0;
                    events.add(mpm);
                    if (retry == null) {
                        fail("Should not publish message");
                    } else if (retry) {
                        mpm.setRetry();
                    }
                }
            });
        }

        public boolean requiresPixelsPyramid(Pixels pixels) {
            return isRequirePyramid;
        }

        @Override
        protected BfPyramidPixelBuffer createPyramidPixelBuffer(Pixels pixels, String filePath, boolean write) {
            return (BfPyramidPixelBuffer) get("createPyramid", pyramid, write);
        }

        @Override
        protected PixelBuffer createBfPixelBuffer(String filePath, int series) {
            return get("createBf", bf, null);
        }

        @Override
        protected PixelBuffer createRomioPixelBuffer(String pixelsFilePath, Pixels pixels,
                boolean allowModification) {
            return get("createRomio", romio, allowModification);
        }

        @Override
        protected String getOriginalFilePath(Pixels pixels) {
            return path;
        }

        //
        // Test only methods
        //

        protected PixelBuffer get(String method, Buffer buffer, Boolean modification) {
            if (buffer == null) {
                fail(method + " should not be called");
            }
            if (buffer.modification != null) {
                assertEquals(buffer.modification, modification);
            }
            return buffer.buffer;
        }

        protected void realPyramid(PixelBuffer pb) {
            pyramid = new Buffer();
            pyramid.buffer = pb;
        }

        protected void stubPyramid(boolean write) {
            pyramid = new Buffer();
            pyramid.modification = write;
        }

        protected void stubRomio(boolean write) {
            romio = new Buffer();
            romio.modification = write;
        }

        protected Mock mockRomio(boolean write) {
            romio = new Buffer(false);
            return romio.mock;
        }

        protected void stubBf() {
            bf = new Buffer();
        }

    }

}