package de.unigoettingen.sub.commons.ocrComponents.cli; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ConcurrentModificationException; import java.util.Properties; import org.junit.Before; import org.junit.Test; import de.uni_goettingen.sub.commons.ocr.abbyy.server.hotfolder.HotfolderMockProvider; import de.uni_goettingen.sub.commons.ocr.abbyy.server.hotfolder.ServerHotfolder; import de.unigoettingen.sub.commons.ocr.util.FileAccess; import de.unigoettingen.sub.commons.ocr.util.FileAccessMockProvider; public class CliIntegrationTest { private ByteArrayOutputStream baos; private Main mainSut; private FileAccess fileAccessMock; private ServerHotfolder hotfolderMock; @Before public void beforeEachTest() { mainSut = new Main(); baos = new ByteArrayOutputStream(); PrintStream out = new PrintStream(baos); mainSut.redirectSystemOutputTo(out); fileAccessMock = mock(FileAccess.class); FileAccessMockProvider.mock = fileAccessMock; hotfolderMock = mock(ServerHotfolder.class, withSettings().serializable()); HotfolderMockProvider.mock = hotfolderMock; } @Test public void shouldComplainAboutInput() throws UnsupportedEncodingException { when(fileAccessMock.isReadableFolder("/tmp/in")).thenReturn(false); when(fileAccessMock.isWritableFolder("/tmp/out")).thenReturn(true); mainSut.execute(validOptions()); String outString = new String(baos.toByteArray()); assertThat(outString, containsString("Illegal options: Input folder not found or it is not readable")); } @Test public void shouldComplainAboutOutput() throws UnsupportedEncodingException { when(fileAccessMock.isReadableFolder("/tmp/in")).thenReturn(true); when(fileAccessMock.isWritableFolder("/tmp/out")).thenReturn(false); mainSut.execute(validOptions()); String outString = new String(baos.toByteArray()); assertThat(outString, containsString("Illegal options: Output folder not found or it is not writable")); } @Test(expected=ConcurrentModificationException.class) public void shouldComplainAboutExistingLockFile() throws URISyntaxException, IOException { prepareFileAccessMockForSuccess(); when(hotfolderMock.exists(new URI("http://localhost:9001/server.lock"))).thenReturn(true); mainSut.execute(validOptions()); } @Test public void shouldDeleteLockFile() throws URISyntaxException, IOException { prepareFileAccessMockForSuccess(); prepareHotfolderMockForSuccess(); String[] opts = validOptions(); opts[17] = "lock.overwrite=true"; mainSut.execute(opts); verify(hotfolderMock, times(2)).deleteIfExists(new URI("http://localhost:9001/server.lock")); } @Test public void shouldPassUserCredentials() throws URISyntaxException, IOException { prepareFileAccessMockForSuccess(); prepareHotfolderMockForSuccess(); mainSut.execute(validOptions()); verify(hotfolderMock, times(2)).configureConnection("http://localhost:9001/", "me", "pass");; } @Test public void shouldCompleteSuccessfully() throws IOException, URISyntaxException { prepareFileAccessMockForSuccess(); prepareHotfolderMockForSuccess(); mainSut.execute(validOptions()); String outString = new String(baos.toByteArray()); assertThat(outString, containsString("Finished OCR.")); verify(hotfolderMock).download(new URI("http://localhost:9001/output/in.xml"), new File("/tmp/out/in.xml").toURI()); } @Test public void shouldCompleteSuccessfullyWithMultiuser() throws IOException, URISyntaxException { prepareFileAccessMockForSuccess(); prepareHotfolderMockForSuccess(); String[] opts = validOptions(); opts[15] = "abbyy-multiuser"; mainSut.execute(opts); String outString = new String(baos.toByteArray()); assertThat(outString, containsString("Finished OCR")); verify(hotfolderMock).download(new URI("http://localhost:9001/output/in.xml"), new File("/tmp/out/in.xml").toURI()); } @Test public void shouldSplitAndMerge() throws IOException, URISyntaxException { prepareFileAccessMockForSuccess(); prepareHotfolderMockForSuccess(); File[] images = new File[]{new File("/tmp/in/01.tif"), new File("/tmp/in/02.tif")}; when(fileAccessMock.getAllImagesFromFolder(any(File.class), any(String[].class))).thenReturn(images); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in_1of2.xml.result.xml"))).thenReturn(true); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in_1of2.xml"))).thenReturn(true); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in_2of2.xml.result.xml"))).thenReturn(false, true); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in_2of2.xml"))).thenReturn(false, true); when(fileAccessMock.inputStreamForFile(new File("/tmp/out/in_1of2.xml.result.xml"))).thenReturn(resultXml()); when(fileAccessMock.inputStreamForFile(new File("/tmp/out/in_2of2.xml.result.xml"))).thenReturn(resultXml()); when(fileAccessMock.inputStreamForFile(new File("/tmp/out/in_1of2.xml"))).thenReturn(abbyyXml()); when(fileAccessMock.inputStreamForFile(new File("/tmp/out/in_2of2.xml"))).thenReturn(abbyyXml()); when(fileAccessMock.outputStreamForFile(any(File.class))).thenReturn(new ByteArrayOutputStream()); String[] opts = validOptions(); opts[17] = "books.split=true"; mainSut.execute(opts); String outString = new String(baos.toByteArray()); assertThat(outString, containsString("Finished OCR")); verify(hotfolderMock).exists(new URI("http://localhost:9001/output/in_1of2.xml")); // times(2) because of thenReturn(false, true) above verify(hotfolderMock, times(2)).exists(new URI("http://localhost:9001/output/in_2of2.xml")); verify(fileAccessMock).outputStreamForFile(new File("/tmp/out/in.xml")); } @Test public void shouldReportTimeout() throws IOException, URISyntaxException { prepareFileAccessMockForSuccess(); prepareHotfolderMockForSuccess(); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in.xml"))).thenReturn(false); mainSut.execute(validOptions()); // TODO: try to propagate the TimeoutException from the thread, also in the other tests verify(hotfolderMock, never()).download(new URI("http://localhost:9001/output/in.xml"), new File("/tmp/out/in.xml").toURI()); } private InputStream resultXml() { String xml = "<XmlResult/>"; return new ByteArrayInputStream(xml.getBytes()); } private InputStream abbyyXml() { String xml = "<document/>"; return new ByteArrayInputStream(xml.getBytes()); } private void prepareFileAccessMockForSuccess() { when(fileAccessMock.isReadableFolder("/tmp/in")).thenReturn(true); when(fileAccessMock.isWritableFolder("/tmp/out")).thenReturn(true); when(fileAccessMock.getAllFolders(anyString(), any(String[].class))).thenReturn(new File[]{new File("/tmp/in")}); when(fileAccessMock.getAllImagesFromFolder(any(File.class), any(String[].class))).thenReturn(new File[]{new File("/tmp/in/01.tif")}); when(fileAccessMock.getPropertiesFromFile(anyString())).thenReturn(validFileProps()); } private void prepareHotfolderMockForSuccess() throws IOException, URISyntaxException { when(hotfolderMock.createTmpFile(anyString())).thenReturn(mock(OutputStream.class, withSettings().serializable())); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in.xml.result.xml"))).thenReturn(true); when(hotfolderMock.exists(new URI("http://localhost:9001/output/in.xml"))).thenReturn(true); } private String[] validOptions() { return new String[]{"-indir", "/tmp/in", "-informats", "tif,jpg", "-texttype", "NORMAL", "-langs", "de,en", "-outdir", "/tmp/out", "-outformats", "XML", "-prio", "2", "-engine", "abbyy", "-props", "user=me,password=pass"}; } private Properties validFileProps() { Properties fileProps = new Properties(); fileProps.setProperty("serverUrl", "http://localhost:9001/"); fileProps.setProperty("outputFolder", "output"); fileProps.setProperty("resultXmlFolder", "output"); fileProps.setProperty("maxImagesInSubprocess", "1"); fileProps.setProperty("maxParallelProcesses", "3"); fileProps.setProperty("maxServerSpace", "1000000000"); fileProps.setProperty("minMillisPerFile", "10"); fileProps.setProperty("maxMillisPerFile", "100"); fileProps.setProperty("checkInterval", "1"); return fileProps; } }