/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.worker.netty; import alluxio.Constants; import alluxio.network.protocol.RPCProtoMessage; import alluxio.proto.status.Status.PStatus; import alluxio.util.CommonUtils; import alluxio.util.WaitForOptions; import alluxio.util.io.BufferUtils; import alluxio.util.proto.ProtoMessage; import com.google.common.base.Function; import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.IOException; import java.io.RandomAccessFile; /** * Unit tests for {@link DataServerWriteHandler}. */ public abstract class DataServerWriteHandlerTest { protected static final int PACKET_SIZE = 1024; protected static final int EOF = 0; protected static final int CANCEL = -1; protected long mChecksum; protected EmbeddedChannel mChannel; protected EmbeddedChannel mChannelNoException; /** The file used to hold the data written by the test. */ protected String mFile; @Rule public TemporaryFolder mTestFolder = new TemporaryFolder(); /** * Writes an empty file. */ @Test public void writeEmptyFile() throws Exception { mChannel.writeInbound(buildWriteRequest(0, 0)); Object writeResponse = waitForResponse(mChannel); checkWriteResponse(writeResponse, PStatus.OK); } /** * Writes an non-empty file. */ @Test public void writeNonEmptyFile() throws Exception { long len = 0; for (int i = 0; i < 128; i++) { mChannel.writeInbound(buildWriteRequest(len, PACKET_SIZE)); len += PACKET_SIZE; } // EOF. mChannel.writeInbound(buildWriteRequest(len, 0)); Object writeResponse = waitForResponse(mChannel); checkWriteResponse(writeResponse, PStatus.OK); checkFileContent(len); } /** * Writes an non-empty file. */ @Test public void cancel() throws Exception { long len = 0; for (int i = 0; i < 128; i++) { mChannel.writeInbound(buildWriteRequest(len, PACKET_SIZE)); len += PACKET_SIZE; } // EOF. mChannel.writeInbound(buildWriteRequest(len, -1)); Object writeResponse = waitForResponse(mChannel); checkWriteResponse(writeResponse, PStatus.CANCELED); // Our current implementation does not really abort the file when the write is cancelled. // The client issues another request to block worker to abort it. checkFileContent(len); } /** * Fails if the write request contains an invalid offset. */ @Test public void writeInvalidOffset() throws Exception { mChannelNoException.writeInbound(buildWriteRequest(0, PACKET_SIZE)); mChannelNoException.writeInbound(buildWriteRequest(PACKET_SIZE + 1, PACKET_SIZE)); Object writeResponse = waitForResponse(mChannelNoException); Assert.assertTrue(writeResponse instanceof RPCProtoMessage); checkWriteResponse(writeResponse, PStatus.INVALID_ARGUMENT); } /** * Checks the given write response is expected and matches the given error code. * * @param writeResponse the write response * @param statusExpected the expected status code */ protected void checkWriteResponse(Object writeResponse, PStatus statusExpected) { Assert.assertTrue(writeResponse instanceof RPCProtoMessage); ProtoMessage response = ((RPCProtoMessage) writeResponse).getMessage(); Assert.assertTrue(response.isResponse()); Assert.assertEquals(statusExpected, response.asResponse().getStatus()); } /** * Checks the file content matches expectation (file length and file checksum). * * @param size the file size in bytes */ protected void checkFileContent(long size) throws IOException { RandomAccessFile file = new RandomAccessFile(mFile, "r"); long checksumActual = 0; long sizeActual = 0; byte[] buffer = new byte[(int) Math.min(Constants.KB, size)]; int bytesRead; do { bytesRead = file.read(buffer); for (int i = 0; i < bytesRead; i++) { checksumActual += BufferUtils.byteToInt(buffer[i]); sizeActual++; } } while (bytesRead >= 0); Assert.assertEquals(mChecksum, checksumActual); Assert.assertEquals(size, sizeActual); } /** * Waits for a response. * * @return the response */ protected Object waitForResponse(final EmbeddedChannel channel) { return CommonUtils .waitForResult("response from the channel.", new Function<Void, Object>() { @Override public Object apply(Void v) { return channel.readOutbound(); } }, WaitForOptions.defaults().setTimeout(Constants.MINUTE_MS)); } /** * Builds the write request. * * @param offset the offset * @param len the length of the block * @return the write request */ protected abstract RPCProtoMessage buildWriteRequest(long offset, int len); }