edu.harvard.med.screensaver.io.screenresults.ScreenResultParserTest.java Source code

Java tutorial

Introduction

Here is the source code for edu.harvard.med.screensaver.io.screenresults.ScreenResultParserTest.java

Source

// $HeadURL:
// http://seanderickson1@forge.abcd.harvard.edu/svn/screensaver/branches/iccbl/2.3.1-dev/test/edu/harvard/med/screensaver/io/screenresults/ScreenResultParserTest.java
// $
// $Id$
//
// Copyright  2006, 2010, 2011, 2012 by the President and Fellows of Harvard College.
//
// Screensaver is an open-source project developed by the ICCB-L and NSRB labs
// at Harvard Medical School. This software is distributed under the terms of
// the GNU General Public License.

package edu.harvard.med.screensaver.io.screenresults;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import com.google.common.collect.Sets;
import jxl.BooleanFormulaCell;
import jxl.CellType;
import jxl.NumberFormulaCell;
import jxl.Sheet;
import jxl.read.biff.BiffException;
import jxl.write.WriteException;
import org.apache.commons.lang.math.IntRange;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import edu.harvard.med.screensaver.io.ParseError;
import edu.harvard.med.screensaver.io.workbook2.Cell;
import edu.harvard.med.screensaver.io.workbook2.Workbook;
import edu.harvard.med.screensaver.io.workbook2.WorkbookParseError;
import edu.harvard.med.screensaver.io.workbook2.Worksheet;
import edu.harvard.med.screensaver.model.libraries.WellKey;
import edu.harvard.med.screensaver.model.screenresults.AssayWellControlType;
import edu.harvard.med.screensaver.model.screenresults.ConfirmedPositiveValue;
import edu.harvard.med.screensaver.model.screenresults.DataColumn;
import edu.harvard.med.screensaver.model.screenresults.PartitionedValue;
import edu.harvard.med.screensaver.model.screenresults.ResultValue;
import edu.harvard.med.screensaver.model.screenresults.ScreenResult;
import edu.harvard.med.screensaver.model.screens.AssayReadoutType;
import edu.harvard.med.screensaver.model.screens.Screen;
import edu.harvard.med.screensaver.test.AbstractSpringTest;
import edu.harvard.med.screensaver.test.MakeDummyEntities;
import edu.harvard.med.screensaver.util.StringUtils;

public class ScreenResultParserTest extends AbstractSpringTest {
    private static final Logger log = Logger.getLogger(ScreenResultParserTest.class);

    @Autowired
    protected ScreenResultParser mockScreenResultParser;

    private Resource SCREEN_RESULT_115_TEST_WORKBOOK_FILE;
    private Resource SCREEN_RESULT_117_TEST_WORKBOOK_FILE;
    private Resource SCREEN_RESULT_115_30_DATA_COLUMNS_TEST_WORKBOOK_FILE;
    private Resource SCREEN_RESULT_MISSING_DERIVED_FROM_WORKBOOK_FILE;
    private Resource ERRORS_TEST_WORKBOOK_FILE;
    private Resource FORMULA_VALUE_TEST_WORKBOOK_FILE;
    private Resource BLANK_ROWS_TEST_WORKBOOK_FILE;
    private Resource HIT_COUNT_TEST_WORKBOOK_FILE;

    @Override
    protected void setUp() throws Exception {
        SCREEN_RESULT_115_TEST_WORKBOOK_FILE = new ClassPathResource("screenresults/ScreenResultTest115.xls");
        SCREEN_RESULT_117_TEST_WORKBOOK_FILE = new ClassPathResource("screenresults/ScreenResultTest117.xls");
        SCREEN_RESULT_115_30_DATA_COLUMNS_TEST_WORKBOOK_FILE = new ClassPathResource(
                "screenresults/ScreenResultTest115_30DataColumns.xls");
        SCREEN_RESULT_MISSING_DERIVED_FROM_WORKBOOK_FILE = new ClassPathResource(
                "screenresults/ScreenResultTest115-missing-derived-from.xls");
        ERRORS_TEST_WORKBOOK_FILE = new ClassPathResource("screenresults/ScreenResultErrorsTest.xls");
        FORMULA_VALUE_TEST_WORKBOOK_FILE = new ClassPathResource("screenresults/formula_value.xls");
        BLANK_ROWS_TEST_WORKBOOK_FILE = new ClassPathResource("screenresults/ScreenResultTest115_blank_rows.xls");
        HIT_COUNT_TEST_WORKBOOK_FILE = new ClassPathResource("screenresults/ScreenResultHitCountTest.xls");
    }

    /**
     * This is the primary test of the ScreenResultParser.
     */
    public void testParseScreenResult() throws Exception {
        ScreenResult screenResult = mockScreenResultParser.parse(MakeDummyEntities.makeDummyScreen(115),
                SCREEN_RESULT_115_TEST_WORKBOOK_FILE.getFile());
        assertEquals(Collections.EMPTY_LIST, mockScreenResultParser.getErrors());
        doTestScreenResult115ParseResult(screenResult);
    }

    /**
     * Tests basic usage of the JXL API to read an Excel spreadsheet, but does
     * not test Screensaver-related functionality. Basically, just a check that
     * the technology we're using actually works. Somewhat useful to keep around
     * in case we upgrade jar version, etc.
     */
    public void testReadExcelSpreadsheet() throws Exception {
        String[] expectedSheetNames = new String[] { "Screen Info", "Data Columns", "PL_00001", "PL_00003",
                "PL_00002" };
        String[] expectedHeaderRowValues = new String[] { "Plate", "Well", "Type", "Exclude", "Luminescence",
                "Luminescence", "FI_A", "FI_B", "Average", "PositiveIndicator2", "PositiveIndicator3",
                "PositiveIndicator4" };

        jxl.Workbook wb = jxl.Workbook.getWorkbook(SCREEN_RESULT_115_TEST_WORKBOOK_FILE.getFile());
        int nSheets = wb.getNumberOfSheets();
        assertEquals("worksheet count", expectedSheetNames.length, nSheets);
        for (int i = 0; i < nSheets; i++) {
            String sheetName = wb.getSheet(i).getName();
            assertEquals("worksheet " + i + " name", expectedSheetNames[i], sheetName);
            jxl.Sheet sheet = wb.getSheet(i);
            if (i >= 2) {
                for (int iCell = 0; iCell < sheet.getRow(0).length; ++iCell) {
                    jxl.Cell cell = sheet.getCell(iCell, 0);
                    assertEquals(expectedHeaderRowValues[iCell], cell.getContents());
                }
            }
        }
    }

    public void testParseNumericFormulaCellValue() throws Exception {

        jxl.Workbook wb = jxl.Workbook.getWorkbook(FORMULA_VALUE_TEST_WORKBOOK_FILE.getFile());
        Sheet sheet = wb.getSheet(0);
        jxl.Cell numericFormulaCell = sheet.getCell(3, 1);
        assertEquals("cell type", CellType.NUMBER_FORMULA, numericFormulaCell.getType());
        double numericValue = ((NumberFormulaCell) numericFormulaCell).getValue();
        assertEquals("numeric value", 2.133, numericValue, 0.0001);
        String formula = ((NumberFormulaCell) numericFormulaCell).getFormula();
        assertEquals("formula", "B2+C2", formula);
        assertEquals("numeric decimal places", 4,
                ((NumberFormulaCell) numericFormulaCell).getNumberFormat().getMaximumFractionDigits());

        Workbook workbook = new Workbook(FORMULA_VALUE_TEST_WORKBOOK_FILE.getFile());
        Worksheet worksheet = workbook.getWorksheet(0);
        Cell cell = worksheet.getCell(3, 1, false);
        assertTrue(!cell.isEmpty());
        Double parsedNumericValue = cell.getDouble();
        assertEquals("parse numeric value", numericValue, parsedNumericValue.doubleValue(), 0.0001);

        // test numeric decimal places (TODO: should probably be a separate unit test)
        Cell numericFormatFormulaCell = worksheet.getCell(3, 1, false);
        assertEquals("decimal places of numeric format on formula cell", 4,
                numericFormatFormulaCell.getDoublePrecision());
        //    Cell generalFormatFormulaCell = cellFactory.getCell((short) 4, (short) 1);
        //    assertEquals("precision of general format on formula cell", -1,
        //                 generalFormatFormulaCell.getDoublePrecision());
        //    Cell generalFormatNumericCell = cellFactory.getCell((short) 1, (short) 1);
        //    assertEquals("precision of general format on numeric cell", -1,
        //                 generalFormatNumericCell.getDoublePrecision());
        Cell numericFormatNumericCell = worksheet.getCell(2, 1);
        assertEquals("decimal places of numeric format on numeric cell", 3,
                numericFormatNumericCell.getDoublePrecision());
        Cell integerNumericFormatNumericCell = worksheet.getCell(5, 1);
        assertEquals("decimal places of integer number format on numeric cell", 0,
                integerNumericFormatNumericCell.getDoublePrecision());
        Cell percentageNumericCell = worksheet.getCell(6, 1);
        assertEquals("decimal places of percentage number format on numeric cell", 3,
                percentageNumericCell.getDoublePrecision());
    }

    public void testReadUndefinedCell() throws BiffException, IOException {
        jxl.Workbook wb = jxl.Workbook.getWorkbook(FORMULA_VALUE_TEST_WORKBOOK_FILE.getFile());
        Sheet sheet = wb.getSheet(0);
        try {
            sheet.getCell(0, 2);
            fail("expected ArrayIndexOutOfBoundsException");
        } catch (ArrayIndexOutOfBoundsException e) {
        }
        try {
            sheet.getCell(10, 0);
            fail("expected ArrayIndexOutOfBoundsException");
        } catch (ArrayIndexOutOfBoundsException e) {
        }
    }

    public void testDetectEmptyRow() throws Exception {
        mockScreenResultParser.parse(MakeDummyEntities.makeDummyScreen(115),
                BLANK_ROWS_TEST_WORKBOOK_FILE.getFile());
        if (mockScreenResultParser.getHasErrors()) {
            log.debug("parse errors:\n" + StringUtils.makeListString(mockScreenResultParser.getErrors(), "\n"));
        }
        assertFalse("screen result had errors: " + mockScreenResultParser.getErrors(),
                mockScreenResultParser.getHasErrors());
        assertEquals("assay well count", 4,
                (int) mockScreenResultParser.getParsedScreenResult().getAssayWells().size());
    }

    // Note: this test was added to highlight a failure of the POI/HSSF library
    // design, which did not allow you to read a boolean-typed formula cell value. 
    // We now longer use this library, but it can't hurt to retain the test.
    public void testReadBooleanFormulaCellValue() throws Exception {
        jxl.Workbook wb = jxl.Workbook.getWorkbook(FORMULA_VALUE_TEST_WORKBOOK_FILE.getFile());
        Sheet sheet = wb.getSheet(0);

        // test boolean formula cell w/'general' format
        jxl.Cell booleanFormulaCell = sheet.getCell(7, 1);
        assertEquals("cell type", CellType.BOOLEAN_FORMULA, booleanFormulaCell.getType());
        boolean booleanValue = ((BooleanFormulaCell) booleanFormulaCell).getValue();
        assertEquals("boolean value", true, booleanValue);
        String formula = ((BooleanFormulaCell) booleanFormulaCell).getFormula();
        assertEquals("formula", "G2>0.01", formula);

        // test boolean formula cell w/explicit 'boolean' format
        booleanFormulaCell = sheet.getCell(8, 1);
        assertEquals("cell type", CellType.BOOLEAN_FORMULA, booleanFormulaCell.getType());
        booleanValue = ((BooleanFormulaCell) booleanFormulaCell).getValue();
        assertEquals("boolean value", true, booleanValue);
        formula = ((BooleanFormulaCell) booleanFormulaCell).getFormula();
        assertEquals("formula", "G2>0.01", formula);

        // test boolean formula cell, read via our own Cell class
        //    ParseErrorManager errors = new ParseErrorManager();
        Workbook workbook = new Workbook(FORMULA_VALUE_TEST_WORKBOOK_FILE.getFile());
        Worksheet worksheet = workbook.getWorksheet(0);
        //    Cell.Factory cellFactory = new Cell.Factory(workbook, 0, errors);
        Cell cell = worksheet.getCell(7, 1, false);
        assertNotNull(cell);
        assertEquals("parse boolean value", booleanValue, cell.getBoolean().booleanValue());
    }

    /**
     * Tests that screen result errors are saved to an annotated error workbook.
     */
    public void testErrorReporting() throws IOException, BiffException, WriteException {
        mockScreenResultParser.parse(MakeDummyEntities.makeDummyScreen(115), ERRORS_TEST_WORKBOOK_FILE.getFile());
        List<WorkbookParseError> errors = mockScreenResultParser.getErrors();
        assertTrue(errors.contains(new ParseError("value required", "Data Columns:(B,3)")));
        assertTrue(errors.contains(new ParseError("illegal value", "Data Columns:(E,4)")));
        assertTrue(errors.contains(new ParseError(
                "unparseable value \"paradiso positive indicator\" (expected one of [Boolean Positive Indicator, Confirmed Positive Indicator, Numeric, Partition Positive Indicator, Text])",
                "Data Columns:(G,3)")));
        assertTrue(errors.contains(new ParseError(
                "invalid Data Column worksheet column label 'B' (expected one of [E, F])", "Data Columns:(D,10)")));
        assertTrue(errors.contains(
                new ParseError("invalid Data Column worksheet column label 'H' (expected one of [E, F, G])",
                        "Data Columns:(E,10)")));
        assertTrue(errors.contains(
                new ParseError("invalid Data Column worksheet column label 'D' (expected one of [E, F, G, H])",
                        "Data Columns:(F,10)")));
        assertTrue(errors
                .contains(new ParseError("unparseable value \"follow-up\" (expected one of [, follow up, primary])",
                        "Data Columns:(E,11)")));
    }

    /**
     * Tests that Cells are cloned when needed (a single Cell is generally
     * recycled, as an optimization). Note that this test assumes that the test
     * errorAnnotatedWorkbooks do not have more than 1 error per cell, which is a possibility in
     * the real world, but would break our naive test. (I suppose we could also
     * test simply that at least some of our ParseErrors' cells were different.)
     * 
     * @throws IOException
     */
    public void testRecycledCellUsage() throws IOException {
        mockScreenResultParser.parse(MakeDummyEntities.makeDummyScreen(115), ERRORS_TEST_WORKBOOK_FILE.getFile());
        Set<Cell> cellsWithErrors = new HashSet<Cell>();
        List<WorkbookParseError> errors = mockScreenResultParser.getErrors();
        for (WorkbookParseError error : errors) {
            assertFalse("every error assigned to distinct cell", cellsWithErrors.contains(error.getCell()));
            cellsWithErrors.add(error.getCell());
        }
    }

    public void testParserReuse() throws Exception {
        Screen screen = MakeDummyEntities.makeDummyScreen(115);
        mockScreenResultParser.parse(screen, ERRORS_TEST_WORKBOOK_FILE.getFile());
        List<WorkbookParseError> errors1 = mockScreenResultParser.getErrors();
        /*Screen*/ screen = MakeDummyEntities.makeDummyScreen(115);
        mockScreenResultParser.parse(screen, ERRORS_TEST_WORKBOOK_FILE.getFile());
        List<WorkbookParseError> errors2 = mockScreenResultParser.getErrors();
        assertTrue(errors1.size() > 0);
        assertTrue(errors2.size() > 0);
        assertEquals("errors not accumulating across multiple parse() calls", errors1, errors2);

        // now test reading yet another spreadsheet, for which we can test the parsed result
        testParseScreenResult();
    }

    public void testParseScreenResultIncremental() throws Exception {
        Screen screen = MakeDummyEntities.makeDummyScreen(115);
        mockScreenResultParser.parse(screen, SCREEN_RESULT_115_TEST_WORKBOOK_FILE.getFile(), new IntRange(1, 2),
                false);
        assertEquals(Collections.EMPTY_LIST, mockScreenResultParser.getErrors());
        assertEquals(640, screen.getScreenResult().getAssayWells().size());
        mockScreenResultParser.parse(screen, SCREEN_RESULT_115_TEST_WORKBOOK_FILE.getFile(), new IntRange(3, 3),
                false);
        assertEquals(Collections.EMPTY_LIST, mockScreenResultParser.getErrors());
        assertEquals(960, screen.getScreenResult().getAssayWells().size());

        doTestScreenResult115ParseResult(screen.getScreenResult());
        assertEquals(960, screen.getScreenResult().getAssayWells().size());
    }

    public void testMultiCharColumnLabels() throws IOException {
        Screen screen = MakeDummyEntities.makeDummyScreen(115);
        ScreenResult result = mockScreenResultParser.parse(screen,
                SCREEN_RESULT_115_30_DATA_COLUMNS_TEST_WORKBOOK_FILE.getFile());
        if (mockScreenResultParser.getHasErrors()) {
            log.debug("parse errors: " + mockScreenResultParser.getErrors());
        }
        assertFalse("screen result had no errors", mockScreenResultParser.getHasErrors());
        List<DataColumn> dataColumns = result.getDataColumnsList();
        assertEquals("DataColumn count", 30, dataColumns.size());
        for (int i = 0; i < 30 - 1; ++i) {
            DataColumn col = dataColumns.get(i);
            assertEquals("is derived from next", dataColumns.get(i + 1), col.getDerivedTypes().first());
            Map<WellKey, ResultValue> resultValues = col.getWellKeyToResultValueMap();
            assertEquals(col.getName() + " result value 0", 1000.0 + i,
                    resultValues.get(new WellKey(1, "A01")).getNumericValue());
            assertEquals(col.getName() + " result value 1", 2000.0 + i,
                    resultValues.get(new WellKey(1, "A02")).getNumericValue());
            assertEquals(col.getName() + " result value 2", 3000.0 + i,
                    resultValues.get(new WellKey(1, "A03")).getNumericValue());
        }
        assertTrue("last is not derived from any", dataColumns.get(30 - 1).getDerivedTypes().isEmpty());
    }

    public void testParseScreenResultWithChannels() throws Exception {
        /* including test for use "S", "time point ordinal" and "zdepth_ordinal" */
        ScreenResult screenResult = mockScreenResultParser.parse(MakeDummyEntities.makeDummyScreen(117),
                SCREEN_RESULT_117_TEST_WORKBOOK_FILE.getFile());
        assertEquals(Collections.EMPTY_LIST, mockScreenResultParser.getErrors());

        doTestScreenResult117ParseResult(screenResult);
    }

    public void testMissingDerivedFrom() throws IOException {
        Screen screen = MakeDummyEntities.makeDummyScreen(115);
        ScreenResult screenResult = mockScreenResultParser.parse(screen,
                SCREEN_RESULT_MISSING_DERIVED_FROM_WORKBOOK_FILE.getFile());
        assertEquals("screen result had no errors", Collections.<ParseError>emptyList(),
                mockScreenResultParser.getErrors());
        DataColumn col0 = screenResult.getDataColumnsList().get(0);
        assertTrue("col 0 is derived", col0.isDerived());
        assertEquals("col 0 'derived from' is empty", 0, col0.getTypesDerivedFrom().size());

        // regression test that normal derived from values work as expected
        DataColumn col1 = screenResult.getDataColumnsList().get(1);
        assertTrue("col 1 is derived", col1.isDerived());
        assertTrue("col 1 'types derived from' is not empty", col1.getTypesDerivedFrom().contains(col0));
        assertTrue("col 0 'derived types' is not empty", col0.getDerivedTypes().contains(col1));
    }

    // private methods

    private void doTestScreenResult115ParseResult(ScreenResult screenResult) {
        assertEquals("replicate count", 2, screenResult.getReplicateCount().intValue());

        //    for(DataColumn dc: screenResult.getDataColumns())
        //    {
        //      assertEquals("1a", dc.getCellLine());
        //    }

        ScreenResult expectedScreenResult = makeScreenResult();
        Map<Integer, DataColumn> expectedDataColumns = new HashMap<Integer, DataColumn>();

        DataColumn dataColumn;

        dataColumn = expectedScreenResult.createDataColumn("Luminescence1");
        dataColumn.setDescription("Desc1");
        dataColumn.forReplicate(1);
        //    dataColumn.forCellLine("1a");
        dataColumn.forTimePoint("0:10");
        dataColumn.setAssayReadoutType(AssayReadoutType.LUMINESCENCE);
        dataColumn.forPhenotype("Phenotype1");
        dataColumn.setComments("None");
        dataColumn.makeNumeric(0);
        expectedDataColumns.put(0, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("Luminescence2");
        dataColumn.setDescription("Desc2");
        dataColumn.forReplicate(2);
        //    dataColumn.forCellLine("1a");
        dataColumn.forTimePoint("0:10");
        dataColumn.setAssayReadoutType(AssayReadoutType.LUMINESCENCE);
        dataColumn.forPhenotype("Phenotype1");
        dataColumn.setFollowUpData(true);
        dataColumn.setComments("None");
        dataColumn.makeNumeric(0);
        expectedDataColumns.put(1, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("FI1");
        dataColumn.setDescription("Fold Induction");
        dataColumn.forReplicate(1);
        //    dataColumn.forCellLine("1a");
        dataColumn.makeDerived("Divide compound well by plate median", Sets.newHashSet(expectedDataColumns.get(0)));
        dataColumn.forPhenotype("Phenotype1");
        dataColumn.makeNumeric(2);
        expectedDataColumns.put(2, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("FI2");
        dataColumn.setDescription("Fold Induction");
        dataColumn.forReplicate(2);
        //    dataColumn.forCellLine("1a");
        dataColumn.makeDerived("Divide compound well by plate median", Sets.newHashSet(expectedDataColumns.get(1)));
        dataColumn.forPhenotype("Phenotype1");
        dataColumn.setFollowUpData(true);
        dataColumn.makeNumeric(2);
        expectedDataColumns.put(3, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("Average");
        dataColumn.makeDerived("Average", Sets.newHashSet(expectedDataColumns.get(2), expectedDataColumns.get(3)));
        dataColumn.makeNumeric(2);
        //    dataColumn.forCellLine("1a");
        expectedDataColumns.put(4, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("PositiveIndicator2");
        dataColumn.makeDerived("W<=1.6, M<=1.7, S<=1.8", Sets.newHashSet(expectedDataColumns.get(4)));
        //    dataColumn.forCellLine("1a");
        dataColumn.makePartitionPositiveIndicator();
        expectedDataColumns.put(5, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("PositiveIndicator3");
        dataColumn.makeDerived("PositiveIndicator2 is S", Sets.newHashSet(expectedDataColumns.get(5)));
        //    dataColumn.forCellLine("1a");
        dataColumn.makeBooleanPositiveIndicator();
        expectedDataColumns.put(6, dataColumn);

        dataColumn = expectedScreenResult.createDataColumn("PositiveIndicator4");
        dataColumn.setDescription("Desc8");
        //    dataColumn.forCellLine("1a");
        dataColumn.makeDerived("PositiveIndicator2 is M or S", Sets.newHashSet(expectedDataColumns.get(5)));
        dataColumn.makeConfirmedPositiveIndicator();
        expectedDataColumns.put(7, dataColumn);

        Integer[] expectedPlateNumbers = { 1, 1, 1, 1, 1, 1, 1, 1 };

        String[] expectedWellNames = { "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08" };

        AssayWellControlType[] expectedAssayWellControlTypes = { AssayWellControlType.ASSAY_POSITIVE_CONTROL, null,
                null, AssayWellControlType.ASSAY_CONTROL, null, null, AssayWellControlType.OTHER_CONTROL, null };

        boolean[][] expectedExcludeValues = { { false, false, false, false, false, false, false, false },
                { false, false, false, false, false, false, false, false },
                { false, true, false, true, false, false, false, false },
                { false, false, false, false, false, false, false, false },
                { false, false, false, false, false, false, false, false },
                { false, false, false, false, false, false, false, false },
                { true, true, true, true, true, true, true, true },
                { false, false, false, false, false, false, false, false } };

        Object[][] expectedValues = {
                { 1071894., 1196906., 0.98, 1.11, 1.045, PartitionedValue.NOT_POSITIVE, Boolean.TRUE,
                        ConfirmedPositiveValue.INCONCLUSIVE },
                { 1174576., 1469296., null, 5.8, 5.80, PartitionedValue.STRONG, Boolean.TRUE,
                        ConfirmedPositiveValue.CONFIRMED_POSITIVE },
                { 1294182., 1280934., 1.18, 1.19, 1.185, PartitionedValue.NOT_POSITIVE, Boolean.FALSE,
                        ConfirmedPositiveValue.CONFIRMED_POSITIVE },
                { 1158888., 1458878., 1.06, 1.35, 1.205, PartitionedValue.WEAK, Boolean.FALSE,
                        ConfirmedPositiveValue.CONFIRMED_POSITIVE },
                { 1385142., 1383446., 1.26, 1.28, 1.270, PartitionedValue.WEAK, Boolean.FALSE,
                        ConfirmedPositiveValue.FALSE_POSITIVE },
                { null, null, null, null, null, PartitionedValue.NOT_POSITIVE, Boolean.FALSE,
                        ConfirmedPositiveValue.FALSE_POSITIVE },
                { 1666646., 1154436., 1.52, 1.07, 1.295, PartitionedValue.WEAK, Boolean.FALSE,
                        ConfirmedPositiveValue.INCONCLUSIVE },
                { null, null, null, null, null, PartitionedValue.NOT_POSITIVE, Boolean.FALSE,
                        ConfirmedPositiveValue.FALSE_POSITIVE } };

        SortedSet<DataColumn> dataColumns = screenResult.getDataColumns();
        int iCol = 0;
        for (DataColumn actualCol : dataColumns) {
            DataColumn expectedCol = expectedDataColumns.get(iCol);
            if (expectedCol != null) {
                assertTrue("DataColumn " + iCol, expectedCol.isEquivalent(actualCol));

                // compare result values
                assertEquals(960, actualCol.getResultValues().size());
                int iWell = 0;
                Map<WellKey, ResultValue> resultValues = actualCol.getWellKeyToResultValueMap();
                for (WellKey wellKey : new TreeSet<WellKey>(resultValues.keySet())) {
                    ResultValue rv = resultValues.get(wellKey);
                    assertEquals("col " + iCol + " well #" + iWell + " plate name", expectedPlateNumbers[iWell],
                            new Integer(wellKey.getPlateNumber()));
                    assertEquals("col " + iCol + " well #" + iWell + " well name", expectedWellNames[iWell],
                            wellKey.getWellName());
                    assertEquals("col " + iCol + " well #" + iWell + " well type",
                            expectedAssayWellControlTypes[iWell], rv.getAssayWellControlType());
                    assertEquals("col " + iCol + " well #" + iWell + ", wellkey: " + wellKey + ", exclude",
                            expectedExcludeValues[iWell][iCol], rv.isExclude());
                    if (expectedValues[iWell][iCol] == null) {
                        assertTrue("col " + iCol + " well #" + iWell + " result value is null", rv.isNull());
                    } else {
                        assertEquals("col " + iCol + " well #" + iWell + " result value",
                                expectedValues[iWell][iCol], rv.getTypedValue());
                    }
                    ++iWell;
                    if (iWell == expectedPlateNumbers.length) {
                        break;
                    }
                }
            }
            ++iCol;
        }
    }

    private void doTestScreenResult117ParseResult(ScreenResult screenResult) {
        assertEquals("replicate count", 2, screenResult.getReplicateCount().intValue());

        ScreenResult expectedScreenResult = makeScreenResult();
        Map<Integer, DataColumn> expectedDataColumns = new HashMap<Integer, DataColumn>();

        DataColumn col;

        col = expectedScreenResult.createDataColumn("r1c1").makeNumeric(0).forReplicate(1).forChannel(1)
                .forTimePointOrdinal(1).forZdepthOrdinal(4);
        col.setAssayReadoutType(AssayReadoutType.LUMINESCENCE);
        expectedDataColumns.put(0, col);

        col = expectedScreenResult.createDataColumn("r1c2").makeNumeric(0).forReplicate(1).forChannel(2)
                .forTimePointOrdinal(2).forZdepthOrdinal(3);
        col.setAssayReadoutType(AssayReadoutType.LUMINESCENCE);
        expectedDataColumns.put(1, col);

        col = expectedScreenResult.createDataColumn("r2c1").makeNumeric(0).forReplicate(2).forChannel(1)
                .forTimePointOrdinal(3).forZdepthOrdinal(2);
        col.setAssayReadoutType(AssayReadoutType.LUMINESCENCE);
        expectedDataColumns.put(2, col);

        col = expectedScreenResult.createDataColumn("r2c2").makeNumeric(0).forReplicate(2).forChannel(2)
                .forTimePointOrdinal(4).forZdepthOrdinal(1);
        col.setAssayReadoutType(AssayReadoutType.LUMINESCENCE);
        expectedDataColumns.put(3, col);

        //check the second data column
        Integer[] expectedPlateNumbers = { 1, 1, 1, 2, 2, 2 };
        //
        String[] expectedWellNames = { "A01", "A02", "A03", "A01", "A02", "A03" };
        //
        AssayWellControlType[] expectedAssayWellTypes = { AssayWellControlType.ASSAY_POSITIVE_CONTROL, null, null,
                AssayWellControlType.ASSAY_CONTROL_SHARED, null, null };

        boolean[][] expectedExcludeValues = { { false, false, false, false }, { false, false, false, false },
                { false, false, false, false }, { false, false, false, false }, { false, false, true, false },
                { false, false, false, false } };

        Object[][] expectedValues = { { 1071894., 100., 7654321., 90. }, { 1234567., 110., 6543210., 120. },
                { 1174576., 120., 5432109., 80. }, { 1071896., 101., 7654320., 89. },
                { 1234563., 113., 6543217., 126. }, { 1174572., 125., 5432105., 89. } };

        SortedSet<DataColumn> dataColumns = screenResult.getDataColumns();
        int iCol = 0;
        for (DataColumn actualCol : dataColumns) {
            DataColumn expectedCol = expectedDataColumns.get(iCol);
            if (expectedCol != null) {
                //        setDefaultValues(expectedCol);
                //        setDefaultValues(actualCol);
                log.info("expectedCol = " + expectedCol.getName());
                log.info("actualCol = " + actualCol.getName());
                assertTrue("DataColumn " + iCol, expectedCol.isEquivalent(actualCol));

                // TODO compare result values
                assertEquals(6, actualCol.getResultValues().size());
                int iWell = 0;
                Map<WellKey, ResultValue> resultValues = actualCol.getWellKeyToResultValueMap();
                for (WellKey wellKey : new TreeSet<WellKey>(resultValues.keySet())) {
                    ResultValue rv = resultValues.get(wellKey);
                    assertEquals("col " + iCol + " well #" + iWell + " plate name", expectedPlateNumbers[iWell],
                            new Integer(wellKey.getPlateNumber()));
                    assertEquals("col " + iCol + " well #" + iWell + " well name", expectedWellNames[iWell],
                            wellKey.getWellName());
                    assertEquals("col " + iCol + " well #" + iWell + " well type", expectedAssayWellTypes[iWell],
                            rv.getAssayWellControlType());
                    assertEquals("col " + iCol + " well #" + iWell + " well type",
                            expectedExcludeValues[iWell][iCol], rv.isExclude());
                    if (expectedValues[iWell][iCol] == null) {
                        assertTrue("col " + iCol + " well #" + iWell + " result value is null", rv.isNull());
                    } else {
                        if (expectedCol.isNumeric()) {
                            double expectedNumericValue = (Double) expectedValues[iWell][iCol];
                            assertEquals("col " + iCol + " well #" + iWell + " result value (numeric)",
                                    expectedNumericValue, rv.getNumericValue(),
                                    Math.pow(1, -col.getDecimalPlaces()));
                        } else {
                            assertEquals("col " + iCol + " well #" + iWell + " result value (non-numeric)",
                                    expectedValues[iWell][iCol].toString(), rv.getValue());
                        }
                    }
                    ++iWell;
                    if (iWell == expectedPlateNumbers.length) {
                        break;
                    }
                }
            }
            ++iCol;
        }
    }

    // testing  utility methods

    public static ScreenResult makeScreenResult() {
        Screen screen = makeScreen();
        ScreenResult screenResult = screen.createScreenResult();
        return screenResult;
    }

    private static Screen makeScreen() {
        return MakeDummyEntities.makeDummyScreen(1);
    }

}