/*
Copyright (c) 2015 LinkedIn Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.linkedin.multipart;
import com.linkedin.data.ByteString;
import com.linkedin.multipart.exceptions.MultiPartReaderFinishedException;
import com.linkedin.multipart.exceptions.SinglePartBindException;
import com.linkedin.multipart.exceptions.SinglePartFinishedException;
import com.linkedin.multipart.exceptions.SinglePartNotInitializedException;
import com.linkedin.multipart.exceptions.StreamBusyException;
import com.linkedin.r2.message.stream.StreamRequest;
import com.linkedin.r2.message.stream.entitystream.EntityStream;
import java.util.Collections;
import org.testng.Assert;
import org.testng.annotations.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* These tests will verify that the correct exceptions are thrown in the face of unorthodox clients.
*
* @author Karim Vidhani
*/
public class TestMIMEReaderStateTransitions
{
private static final EmptyMultiPartMIMEReaderCallback EMPTY_MULTI_PART_MIME_READER_CALLBACK = new EmptyMultiPartMIMEReaderCallback();
private static final EmptySinglePartMIMEReaderCallback EMPTY_SINGLE_PART_MIME_READER_CALLBACK = new EmptySinglePartMIMEReaderCallback();
//MultiPartMIMEReader exceptions:
@Test
public void testRegisterCallbackMultiPartMIMEReader()
{
final EntityStream entityStream = mock(EntityStream.class);
final StreamRequest streamRequest = mock(StreamRequest.class);
when(streamRequest.getEntityStream()).thenReturn(entityStream);
when(streamRequest.getHeader(MultiPartMIMEUtils.CONTENT_TYPE_HEADER)).thenReturn("multipart/mixed; boundary=\"--123\"");
MultiPartMIMEReader reader = MultiPartMIMEReader.createAndAcquireStream(streamRequest);
//Test each possible exception:
reader.setState(MultiPartMIMEReader.MultiPartReaderState.FINISHED);
try
{
reader.registerReaderCallback(EMPTY_MULTI_PART_MIME_READER_CALLBACK);
Assert.fail();
}
catch (MultiPartReaderFinishedException multiPartReaderFinishedException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.READING_EPILOGUE);
try
{
reader.registerReaderCallback(EMPTY_MULTI_PART_MIME_READER_CALLBACK);
Assert.fail();
}
catch (MultiPartReaderFinishedException multiPartReaderFinishedException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.CALLBACK_BOUND_AND_READING_PREAMBLE);
try
{
reader.registerReaderCallback(EMPTY_MULTI_PART_MIME_READER_CALLBACK);
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.DRAINING);
try
{
reader.registerReaderCallback(EMPTY_MULTI_PART_MIME_READER_CALLBACK);
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.READING_PARTS); //This is a desired top level reader state
final MultiPartMIMEReader.SinglePartMIMEReader singlePartMIMEReader = reader.new SinglePartMIMEReader(Collections.<String, String>emptyMap());
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.REQUESTED_DATA); //This is a undesired single part state
reader.setCurrentSinglePartMIMEReader(singlePartMIMEReader);
try
{
reader.registerReaderCallback(EMPTY_MULTI_PART_MIME_READER_CALLBACK);
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
}
@Test
public void testDrainAllPartsMultiPartMIMEReader()
{
final EntityStream entityStream = mock(EntityStream.class);
final StreamRequest streamRequest = mock(StreamRequest.class);
when(streamRequest.getEntityStream()).thenReturn(entityStream);
when(streamRequest.getHeader(MultiPartMIMEUtils.CONTENT_TYPE_HEADER)).thenReturn("multipart/mixed; boundary=\"--123\"");
MultiPartMIMEReader reader = MultiPartMIMEReader.createAndAcquireStream(streamRequest);
//Test each possible exception:
reader.setState(MultiPartMIMEReader.MultiPartReaderState.FINISHED);
try
{
reader.drainAllParts();
Assert.fail();
}
catch (MultiPartReaderFinishedException multiPartReaderFinishedException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.READING_EPILOGUE);
try
{
reader.drainAllParts();
Assert.fail();
}
catch (MultiPartReaderFinishedException multiPartReaderFinishedException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.CALLBACK_BOUND_AND_READING_PREAMBLE);
try
{
reader.drainAllParts();
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.DRAINING);
try
{
reader.drainAllParts();
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
reader.setState(MultiPartMIMEReader.MultiPartReaderState.READING_PARTS); //This is the desired top level reader state
final MultiPartMIMEReader.SinglePartMIMEReader singlePartMIMEReader = reader.new SinglePartMIMEReader(Collections.<String, String>emptyMap());
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.REQUESTED_DATA); //This is a undesired single part state
reader.setCurrentSinglePartMIMEReader(singlePartMIMEReader);
try
{
reader.drainAllParts();
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
}
///////////////////////////////////////////////////////////////////////////////////////
//SinglePartMIMEReader exceptions:
@Test
public void testRegisterSinglePartMIMEReaderCallbackTwice()
{
final EntityStream entityStream = mock(EntityStream.class);
final StreamRequest streamRequest = mock(StreamRequest.class);
when(streamRequest.getEntityStream()).thenReturn(entityStream);
when(streamRequest.getHeader(MultiPartMIMEUtils.CONTENT_TYPE_HEADER)).thenReturn("multipart/mixed; boundary=\"--123\"");
MultiPartMIMEReader reader = MultiPartMIMEReader.createAndAcquireStream(streamRequest);
final MultiPartMIMEReader.SinglePartMIMEReader singlePartMIMEReader =
reader.new SinglePartMIMEReader(Collections.<String, String>emptyMap());
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.REQUESTED_DATA); //This is a undesired single part state
try
{
singlePartMIMEReader.registerReaderCallback(EMPTY_SINGLE_PART_MIME_READER_CALLBACK);
Assert.fail();
}
catch (SinglePartBindException singlePartBindException)
{
}
}
@Test
public void testSinglePartMIMEReaderVerifyState()
{
//This will cover drainPart() and most of requestPartData().
//The caveat is that requestPartData() requires a callback to be registered. This
//will be covered in the next test.
final EntityStream entityStream = mock(EntityStream.class);
final StreamRequest streamRequest = mock(StreamRequest.class);
when(streamRequest.getEntityStream()).thenReturn(entityStream);
when(streamRequest.getHeader(MultiPartMIMEUtils.CONTENT_TYPE_HEADER)).thenReturn("multipart/mixed; boundary=\"--123\"");
MultiPartMIMEReader reader = MultiPartMIMEReader.createAndAcquireStream(streamRequest);
final MultiPartMIMEReader.SinglePartMIMEReader singlePartMIMEReader =
reader.new SinglePartMIMEReader(Collections.<String, String>emptyMap());
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.FINISHED);
try
{
singlePartMIMEReader.verifyUsableState();
Assert.fail();
}
catch (SinglePartFinishedException singlePartFinishedException)
{
}
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.REQUESTED_DATA);
try
{
singlePartMIMEReader.verifyUsableState();
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.REQUESTED_DRAIN);
try
{
singlePartMIMEReader.verifyUsableState();
Assert.fail();
}
catch (StreamBusyException streamBusyException)
{
}
}
@Test
public void testSinglePartMIMEReaderRequestData()
{
final EntityStream entityStream = mock(EntityStream.class);
final StreamRequest streamRequest = mock(StreamRequest.class);
when(streamRequest.getEntityStream()).thenReturn(entityStream);
when(streamRequest.getHeader(MultiPartMIMEUtils.CONTENT_TYPE_HEADER)).thenReturn("multipart/mixed; boundary=\"--123\"");
MultiPartMIMEReader reader = MultiPartMIMEReader.createAndAcquireStream(streamRequest);
final MultiPartMIMEReader.SinglePartMIMEReader singlePartMIMEReader = reader.new SinglePartMIMEReader(Collections.<String, String>emptyMap());
singlePartMIMEReader.setState(MultiPartMIMEReader.SingleReaderState.CREATED);
try
{
singlePartMIMEReader.requestPartData();
Assert.fail();
}
catch (SinglePartNotInitializedException singlePartNotInitializedException)
{
}
}
private static final class EmptyMultiPartMIMEReaderCallback implements MultiPartMIMEReaderCallback
{
@Override
public void onNewPart(MultiPartMIMEReader.SinglePartMIMEReader singlePartMIMEReader)
{
}
@Override
public void onFinished()
{
}
@Override
public void onDrainComplete()
{
}
@Override
public void onStreamError(Throwable throwable)
{
}
}
private static final class EmptySinglePartMIMEReaderCallback implements SinglePartMIMEReaderCallback
{
@Override
public void onPartDataAvailable(ByteString partData)
{
}
@Override
public void onFinished()
{
}
@Override
public void onDrainComplete()
{
}
@Override
public void onStreamError(Throwable throwable)
{
}
}
}