Skip to content

Commit

Permalink
#429: tests to check there is no leak when error occurred before sending
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Jun 27, 2018
1 parent e8a6c42 commit f9f8b91
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (c) 2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial creation
******************************************************************************/
package org.eclipse.californium.core.test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.eclipse.californium.core.coap.MessageObserverAdapter;
import org.eclipse.californium.core.coap.Response;

public class CountingMessageObserver extends MessageObserverAdapter {

public AtomicInteger loadCalls = new AtomicInteger();
public AtomicInteger errorCalls = new AtomicInteger();

@Override
public void onRetransmission() {
}

@Override
public void onResponse(Response response) {
int counter;
synchronized (this) {
counter = loadCalls.incrementAndGet();
notifyAll();
}
System.out.println("Received " + counter + ". Notification: " + response);
}

@Override
public void onSendError(Throwable error) {
int counter;
synchronized (this) {
counter = errorCalls.incrementAndGet();
notifyAll();
}
System.out.println(counter + " Errors!");
}

public boolean waitForLoadCalls(final int counter, final long timeout, final TimeUnit unit)
throws InterruptedException {
return waitForCalls(counter, timeout, unit, loadCalls);
}

public boolean waitForErrorCalls(final int counter, final long timeout, final TimeUnit unit)
throws InterruptedException {
return waitForCalls(counter, timeout, unit, errorCalls);
}

private synchronized boolean waitForCalls(final int counter, final long timeout, final TimeUnit unit,
AtomicInteger calls) throws InterruptedException {
if (0 < timeout) {
long end = System.nanoTime() + unit.toNanos(timeout);
while (calls.get() < counter) {
long left = TimeUnit.NANOSECONDS.toMillis(end - System.nanoTime());
if (0 < left) {
wait(left);
} else {
break;
}
}
}
return calls.get() >= counter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*******************************************************************************
* Copyright (c) 2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial creation
******************************************************************************/
package org.eclipse.californium.core.test;

import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.californium.core.coap.EmptyMessage;
import org.eclipse.californium.core.coap.MessageObserver;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.interceptors.MessageInterceptor;
import org.eclipse.californium.elements.EndpointContext;

public class ErrorInjector implements MessageInterceptor, MessageObserver{

private AtomicBoolean errorOnEstablishedContext = new AtomicBoolean(false);
private AtomicBoolean errorOnSent = new AtomicBoolean(false);
private AtomicBoolean errorOnReadyToSend = new AtomicBoolean(false);


public void setErrorOnEstablishedContext() {
errorOnEstablishedContext.set(true);
}

public void setErrorOnSent() {
errorOnSent.set(true);
}

public void setErrorOnReadyToSend() {
errorOnReadyToSend.set(true);
}

//************* Message Interceptor *******************//

@Override
public void sendRequest(Request request) {
request.addMessageObserver(this);
}

@Override
public void sendResponse(Response response) {
}

@Override
public void sendEmptyMessage(EmptyMessage message) {
}

@Override
public void receiveRequest(Request request) {
}

@Override
public void receiveResponse(Response response) {
}

@Override
public void receiveEmptyMessage(EmptyMessage message) {
}

//************* Message observer *******************//
@Override
public void onRetransmission() {
}

@Override
public void onResponse(Response response) {
}

@Override
public void onAcknowledgement() {
}

@Override
public void onReject() {
}

@Override
public void onTimeout() {
}

@Override
public void onCancel() {
}

@Override
public void onReadyToSend() {
if (errorOnReadyToSend.getAndSet(false)) {
throw new IllegalStateException("Simulate error before to sent");
}
}

@Override
public void onSent() {
if (errorOnReadyToSend.getAndSet(false)) {
throw new IllegalStateException("Simulate error on sent");
}
}

@Override
public void onSendError(Throwable error) {
}

@Override
public void onContextEstablished(EndpointContext endpointContext) {
if (errorOnEstablishedContext.getAndSet(false)) {
throw new IllegalStateException("Simulate error on context established");
}
}

@Override
public void onComplete() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.test.CountingMessageObserver;
import org.eclipse.californium.core.test.ErrorInjector;
import org.eclipse.californium.core.test.MessageExchangeStoreTool.CoapTestEndpoint;
import org.eclipse.californium.rule.CoapNetworkRule;
import org.junit.After;
Expand Down Expand Up @@ -1199,4 +1201,81 @@ public void testIncompleteBlock1AckNoResponse() throws Exception {

printServerLog(clientInterceptor);
}

/**
* Verify there is no leak if we failed before to sent block1 request
*/
@Test
public void testBlock1FailureBeforeToSend() throws Exception {
System.out.println("blockwise PUT failed before to send a block1 request");
reqtPayload = generateRandomPayload(300);
String path = "test";

// Add error injector to client endpoint to be able to simulate error
// before we really send a message.
ErrorInjector errorInjector = new ErrorInjector();
client.addInterceptor(errorInjector);

// Start block 1 transfer
Request request = createRequest(PUT, path, server);
request.setPayload(reqtPayload);
CountingMessageObserver counter = new CountingMessageObserver();
request.addMessageObserver(counter);
client.sendRequest(request);
server.expectRequest(CON, PUT, path).storeBoth("A").block1(0, true, 128).size1(reqtPayload.length())
.payload(reqtPayload.substring(0, 128)).go();
server.sendResponse(ACK, CONTINUE).loadBoth("A").block1(0, true, 128).go();
server.expectRequest(CON, PUT, path).storeBoth("B").block1(1, true, 128)
.payload(reqtPayload.substring(128, 256)).go();

// Simulate error before we send the next block1 request
errorInjector.setErrorOnReadyToSend();
server.sendResponse(ACK, CONTINUE).loadBoth("B").block1(1, true, 128).go();

// Wait for error
counter.waitForErrorCalls(1, 1000, TimeUnit.MILLISECONDS);

// We should get a error
assertEquals("An error is expected", 1, counter.errorCalls.get());

// @after check there is no leak
}

/**
* Verify there is no leak if we failed before to sent block2 request
*/
@Test
public void testBlock2FailureBeforeToSend() throws Exception {
System.out.println("blockwise GET failed before to send a block2 request:");
respPayload = generateRandomPayload(300);
String path = "test";

// Add error injector to client endpoint to be able to simulate error
// before we really send a message.
ErrorInjector errorInjector = new ErrorInjector();
client.addInterceptor(errorInjector);

// Start block 2 transfer
Request request = createRequest(GET, path, server);
CountingMessageObserver counter = new CountingMessageObserver();
request.addMessageObserver(counter);
client.sendRequest(request);
server.expectRequest(CON, GET, path).storeBoth("A").go();
server.sendResponse(ACK, CONTENT).loadBoth("A").block2(0, true, 128).size2(respPayload.length())
.payload(respPayload.substring(0, 128)).go();
server.expectRequest(CON, GET, path).storeBoth("B").block2(1, false, 128).go();

// Simulate error before we send the next block2 request
errorInjector.setErrorOnReadyToSend();
server.sendResponse(ACK, CONTENT).loadBoth("B").block2(1, true, 128).payload(respPayload.substring(128, 256))
.go();

// Wait for error
counter.waitForErrorCalls(1, 1000, TimeUnit.MILLISECONDS);

// We should get a error
assertEquals("An error is expected", 1, counter.errorCalls.get());

// @after check there is no leak
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@
import static org.eclipse.californium.core.test.lockstep.IntegrationTestTools.*;
import static org.eclipse.californium.core.test.MessageExchangeStoreTool.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.*;

import java.net.InetAddress;
import java.net.InetSocketAddress;
Expand All @@ -53,6 +51,8 @@
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.network.interceptors.MessageTracer;
import org.eclipse.californium.core.test.CountingMessageObserver;
import org.eclipse.californium.core.test.ErrorInjector;
import org.eclipse.californium.rule.CoapNetworkRule;
import org.junit.After;
import org.junit.AfterClass;
Expand Down Expand Up @@ -1179,4 +1179,81 @@ public void testBlockwiseObserveAndTimedoutNotification() throws Exception {

}

/**
* Verify there is no leak if we failed before to sent the CoAP request.
*/
@Test
public void testObserveFailureBeforeToSend() throws Exception {
System.out.println("Observe fails before we send request:");
respPayload = generateRandomPayload(10);
String path = "test";

// Add error injector to client endpoint to be able to simulate error
// before we really send the message.
ErrorInjector errorInjector = new ErrorInjector();
client.addInterceptor(errorInjector);
errorInjector.setErrorOnReadyToSend();

// Try to send request
Request request = createRequest(GET, path, server);
SynchronousNotificationListener notificationListener = new SynchronousNotificationListener(request);
client.addNotificationListener(notificationListener);
CountingMessageObserver counter = new CountingMessageObserver();
request.addMessageObserver(counter);
request.setObserve();
client.sendRequest(request);

// Wait for error
counter.waitForErrorCalls(1, 1000, TimeUnit.MILLISECONDS);

// We should get a error
assertEquals("An error is expected", 1, counter.errorCalls.get());

// @after check there is no leak
}

/**
* Verify there is no leak if we failed before to sent block request for a notification.
*/
@Test
public void testObserveFailureBeforeToSendDuringBlockNotification() throws Exception {
System.out.println("Observe fails before we send the next block2 request for a notification");
respPayload = generateRandomPayload(10);
String path = "test";

// Add error injector to client endpoint to be able to simulate error
// before we really send the message.
ErrorInjector errorInjector = new ErrorInjector();
client.addInterceptor(errorInjector);

// establish observe relation
Request request = createRequest(GET, path, server);
request.setObserve();
SynchronousNotificationListener notificationListener = new SynchronousNotificationListener(request);
client.addNotificationListener(notificationListener);
respPayload = generateRandomPayload(16);
client.sendRequest(request);
server.expectRequest(CON, GET, path).storeBoth("OBS").go();
server.sendResponse(ACK, CONTENT).loadBoth("OBS").observe(1).payload(respPayload).go();
Response response = request.waitForResponse(2000);
assertResponseContainsExpectedPayload(response, respPayload);

// New notification
String notifyPayload = generateRandomPayload(32);
server.sendResponse(CON, CONTENT).loadToken("OBS").observe(3).mid(++mid).block2(0, true, 16)
.payload(notifyPayload.substring(0, 16)).go();
// Expect ACK and Next Block request for the notification
server.startMultiExpectation();
server.expectEmpty(ACK, mid).go();
server.expectRequest(CON, GET, path).storeBoth("BLOCK").block2(1, false, 16).go();
server.goMultiExpectation();

// Simulate error before we send the next block2 request
errorInjector.setErrorOnReadyToSend();
server.sendResponse(ACK, CONTENT).loadBoth("BLOCK").block2(1, true, 16).payload(notifyPayload.substring(16, 32)).go();

// TODO We would like to check if the block2 request failed but we have no API for a block2 request which failed to be sent

// @after check there is no leak
}
}

0 comments on commit f9f8b91

Please sign in to comment.