package com.constellio.sdk.tests; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.constellio.data.io.services.facades.OpenedResourcesWatcher; import com.constellio.data.io.streamFactories.StreamFactory; public class StreamsTestFeatures { private static final Logger LOGGER = LoggerFactory.getLogger(StreamsTestFeatures.class); public static final String SDK_STREAM = "SDK Stream"; List<Closeable> closeableThatMustBeClosed = new ArrayList<Closeable>(); List<Closeable> closeablesToClose = new ArrayList<Closeable>(); List<String> unClosedResources = new ArrayList<>(); // List<Long> openedThreadsAtStartup = new ArrayList<>(); // // public StreamsTestFeatures() { // for (Thread thread : Thread.getAllStackTraces().keySet()) { // openedThreadsAtStartup.add(thread.getId()); // } // } public void beforeTest(SkipTestsRule skipTestsRule) { if (skipTestsRule == null || skipTestsRule.getCurrentTestClass() == null) { OpenedResourcesWatcher.openingStackHeader = "Where the resource was opened : "; } else { String testName = skipTestsRule.getCurrentTestClass().getSimpleName() + "." + skipTestsRule.getCurrentTestName(); OpenedResourcesWatcher.openingStackHeader = "Where the resource was opened (in test '" + testName + "') : s"; } } public void afterTest() { // List<String> openedThreads = new ArrayList<>(); // for (Thread thread : Thread.getAllStackTraces().keySet()) { // if (openedThreadsAtStartup.contains(thread.getId())) { // openedThreads.add(thread.getId() + " - " + thread.getName()); // } // } // assertThat(openedThreads).isEmpty(); // for (Thread thread : Thread.getAllStackTraces().keySet()) { // openedThreadsAtStartup.add(thread.getId()); // } for (Closeable closeable : closeablesToClose) { IOUtils.closeQuietly(closeable); } boolean someStreamFactoryStreamsNotClosed = false; for (Closeable closeable : closeableThatMustBeClosed) { try { verify(closeable).close(); } catch (IOException e) { throw new RuntimeException(e); } } if (someStreamFactoryStreamsNotClosed) { fail("Some opened stream has not been closed"); } synchronized (OpenedResourcesWatcher.class) { for (Map.Entry<String, Object> entry : OpenedResourcesWatcher.getOpenedResources().entrySet()) { Object value = entry.getValue(); if (value instanceof Thread) { if (!entry.getKey().contains(SDK_STREAM)) { unClosedResources.add(entry.getKey()); } Thread thread = (Thread) value; while (thread.isAlive()) { StringBuilder message = new StringBuilder(); message.append("Waiting for thread '" + thread.toString() + "' / '" + thread.getName() + "' to stop"); message.append("\n" + OpenedResourcesWatcher.getOpeningStackTraceOf(entry.getKey())); message.append("\n"); message.append("Current thread stack trace : "); for (StackTraceElement element : thread.getStackTrace()) { message.append("\n\t" + element.toString()); } LOGGER.info(message.toString()); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } //Close all streams before deleting temp files for (Map.Entry<String, Object> entry : OpenedResourcesWatcher.getOpenedResources().entrySet()) { Object value = entry.getValue(); if (value instanceof Closeable) { if (!entry.getKey().contains(SDK_STREAM)) { unClosedResources.add(entry.getKey()); } IOUtils.closeQuietly((Closeable) entry.getValue()); } } for (Map.Entry<String, Object> entry : OpenedResourcesWatcher.getOpenedResources().entrySet()) { Object value = entry.getValue(); if (value instanceof File) { if (!entry.getKey().contains(SDK_STREAM)) { unClosedResources.add(entry.getKey()); } OpenedResourcesWatcher.onClose(value); FileUtils.deleteQuietly((File) value); } } } } public List<String> getUnClosedResources() { return unClosedResources; } public <T extends Closeable> T closeAfterTest(T closeable) { closeablesToClose.add(closeable); T mockedStream = Mockito.spy(closeable); try { doThrow(new RuntimeException("The closeable must be closed at the same place it has been opened")).when(mockedStream) .close(); } catch (IOException e) { throw new RuntimeException(e); } return mockedStream; } public <T extends Closeable> StreamFactory<T> ensureAllCreatedCloseableAreClosed(final StreamFactory<T> nestedStreamFactory) { return new StreamFactory<T>() { @Override public T create(String name) throws IOException { return spy(nestedStreamFactory.create(name)); } }; } }