Fix FtpServerOutboundTests race condition

The `ArrayList` cannot be used in the test-case for checking
its size because the property is not `volatile`: even if
content of the list is what we expect (test logs confirms that),
the non-`volatile` property doesn't give us an actual value

* Use `CopyOnWriteArrayList` for this concurrent test to
be sure that internal `array.length` reflects the reality
after each interaction with the list
This commit is contained in:
Artem Bilan
2020-08-03 10:06:37 -04:00
parent a62d75f1f0
commit f26d4c0a5a

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.ftp.outbound;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -31,13 +32,12 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -63,7 +63,6 @@ import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.InputStreamCallback;
import org.springframework.integration.file.remote.MessageSessionCallback;
import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Option;
@@ -211,17 +210,9 @@ public class FtpServerOutboundTests extends FtpTestSupport {
@Test
public void testInt2866InvalidLocalDirectoryExpression() {
try {
this.invalidDirExpression.send(new GenericMessage<Object>("/ftpSource/ ftpSource1.txt"));
fail("Exception expected.");
}
catch (Exception e) {
Throwable cause = e.getCause();
assertThat(cause).isNotNull();
cause = cause.getCause();
assertThat(cause).isInstanceOf(IllegalArgumentException.class);
assertThat(cause.getMessage()).startsWith("Failed to make local directory");
}
assertThatCode(() -> this.invalidDirExpression.send(new GenericMessage<Object>("/ftpSource/ ftpSource1.txt")))
.hasRootCauseInstanceOf(IllegalArgumentException.class)
.hasStackTraceContaining("Failed to make local directory");
}
@Test
@@ -384,19 +375,19 @@ public class FtpServerOutboundTests extends FtpTestSupport {
}
@Test
public void testRawGETWithTemplate() throws Exception {
RemoteFileTemplate<FTPFile> template = new RemoteFileTemplate<FTPFile>(this.ftpSessionFactory);
public void testRawGETWithTemplate() {
RemoteFileTemplate<FTPFile> template = new RemoteFileTemplate<>(this.ftpSessionFactory);
template.setFileNameExpression(new SpelExpressionParser().parseExpression("payload"));
template.setBeanFactory(mock(BeanFactory.class));
template.afterPropertiesSet();
final ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
assertThat(template.get(new GenericMessage<String>("ftpSource/ ftpSource1.txt"),
(InputStreamCallback) stream -> FileCopyUtils.copy(stream, baos1))).isTrue();
assertThat(template.get(new GenericMessage<>("ftpSource/ ftpSource1.txt"),
stream -> FileCopyUtils.copy(stream, baos1))).isTrue();
assertThat(new String(baos1.toByteArray())).isEqualTo("source1");
final ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
assertThat(template.get(new GenericMessage<String>("ftpSource/ftpSource2.txt"),
(InputStreamCallback) stream -> FileCopyUtils.copy(stream, baos2))).isTrue();
assertThat(template.get(new GenericMessage<>("ftpSource/ftpSource2.txt"),
stream -> FileCopyUtils.copy(stream, baos2))).isTrue();
assertThat(new String(baos2.toByteArray())).isEqualTo("source2");
}
@@ -407,7 +398,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
session = TestUtils.getPropertyValue(session, "targetSession", Session.class);
FTPClient client = spy(TestUtils.getPropertyValue(session, "client", FTPClient.class));
new DirectFieldAccessor(session).setPropertyValue("client", client);
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
this.inboundMPut.send(new GenericMessage<>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertThat(out).isNotNull();
@@ -423,7 +414,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
@Test
public void testInt3088MPutRecursive() {
this.inboundMPutRecursive.send(new GenericMessage<File>(getSourceLocalDirectory()));
this.inboundMPutRecursive.send(new GenericMessage<>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertThat(out).isNotNull();
@@ -442,7 +433,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
@Test
public void testInt3088MPutRecursiveFiltered() {
this.inboundMPutRecursiveFiltered.send(new GenericMessage<File>(getSourceLocalDirectory()));
this.inboundMPutRecursiveFiltered.send(new GenericMessage<>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertThat(out).isNotNull();
@@ -574,7 +565,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
throw new IOException("Failed to send localSource2");
}).when(session).write(Mockito.any(InputStream.class), Mockito.contains("localSource2"));
try {
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
this.inboundMPut.send(new GenericMessage<>(getSourceLocalDirectory()));
fail("expected exception");
}
catch (PartialSuccessException e) {
@@ -598,7 +589,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
throw new IOException("Failed to send subLocalSource2");
}).when(session).write(Mockito.any(InputStream.class), Mockito.contains("subLocalSource2"));
try {
this.inboundMPutRecursive.send(new GenericMessage<File>(getSourceLocalDirectory()));
this.inboundMPutRecursive.send(new GenericMessage<>(getSourceLocalDirectory()));
fail("expected exception");
}
catch (PartialSuccessException e) {
@@ -637,7 +628,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
@Test
public void testMessageSessionCallback() {
this.inboundCallback.send(new GenericMessage<String>("foo"));
this.inboundCallback.send(new GenericMessage<>("foo"));
Message<?> receive = this.output.receive(10000);
assertThat(receive).isNotNull();
assertThat(receive.getPayload()).isEqualTo("FOO");
@@ -650,7 +641,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
((FTPClient) session.getClientInstance()).changeWorkingDirectory("ftpSource");
session.close();
this.inboundLs.send(new GenericMessage<String>("foo"));
this.inboundLs.send(new GenericMessage<>("foo"));
Message<?> receive = this.output.receive(10000);
assertThat(receive).isNotNull();
assertThat(receive.getPayload()).isInstanceOf(List.class);
@@ -792,7 +783,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
implements MessageSessionCallback<FTPFile, Object> {
@Override
public Object doInSession(Session<FTPFile> session, Message<?> requestMessage) throws IOException {
public Object doInSession(Session<FTPFile> session, Message<?> requestMessage) {
return ((String) requestMessage.getPayload()).toUpperCase();
}
@@ -801,7 +792,7 @@ public class FtpServerOutboundTests extends FtpTestSupport {
@Component
public static class Config {
final List<ApacheMinaFtpEvent> events = Collections.synchronizedList(new ArrayList<>());
final List<ApacheMinaFtpEvent> events = new CopyOnWriteArrayList<>();
private volatile String targetLocalDirectoryName;