[HUDI-568] Improve unit test coverage
Classes improved: * HoodieTableMetaClient * RocksDBDAO * HoodieRealtimeFileSplit
This commit is contained in:
committed by
n3nash
parent
996f761232
commit
f5f34bb1c1
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.hudi.hadoop.realtime;
|
||||
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.mapred.FileSplit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.AdditionalMatchers.aryEq;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyByte;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class TestHoodieRealtimeFileSplit {
|
||||
|
||||
private HoodieRealtimeFileSplit split;
|
||||
private String basePath;
|
||||
private List<String> deltaLogPaths;
|
||||
private String fileSplitName;
|
||||
private FileSplit baseFileSplit;
|
||||
private String maxCommitTime;
|
||||
private TemporaryFolder tmp;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tmp = new TemporaryFolder();
|
||||
tmp.create();
|
||||
|
||||
basePath = tmp.getRoot().toString();
|
||||
deltaLogPaths = Collections.singletonList(basePath + "/1.log");
|
||||
fileSplitName = basePath + "/test.file";
|
||||
baseFileSplit = new FileSplit(new Path(fileSplitName), 0, 100, new String[]{});
|
||||
maxCommitTime = "10001";
|
||||
|
||||
split = new HoodieRealtimeFileSplit(baseFileSplit, basePath, deltaLogPaths, maxCommitTime);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
tmp.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws IOException {
|
||||
// create a mock for DataOutput that will be used in the write method
|
||||
// this way we can capture and verify if correct arguments were passed
|
||||
DataOutput out = mock(DataOutput.class);
|
||||
|
||||
// register expected method calls for void functions
|
||||
// so that we can verify what was called after the method call finishes
|
||||
doNothing().when(out).writeByte(anyByte());
|
||||
doNothing().when(out).writeInt(anyInt());
|
||||
doNothing().when(out).write(any(byte[].class), anyInt(), anyInt());
|
||||
doNothing().when(out).write(any(byte[].class));
|
||||
|
||||
// call the method we want to test with the mocked input
|
||||
split.write(out);
|
||||
|
||||
// verify the method calls on the mocked object in the order of the calls
|
||||
InOrder inorder = inOrder(out);
|
||||
inorder.verify(out, times(1)).writeByte(eq(fileSplitName.length()));
|
||||
inorder.verify(out, times(1)).write(aryEq(Text.encode(fileSplitName).array()), eq(0), eq(fileSplitName.length()));
|
||||
inorder.verify(out, times(1)).writeInt(eq(basePath.length()));
|
||||
inorder.verify(out, times(1)).write(aryEq(basePath.getBytes(StandardCharsets.UTF_8)));
|
||||
inorder.verify(out, times(1)).writeInt(eq(maxCommitTime.length()));
|
||||
inorder.verify(out, times(1)).write(aryEq(maxCommitTime.getBytes(StandardCharsets.UTF_8)));
|
||||
inorder.verify(out, times(1)).writeInt(eq(deltaLogPaths.size()));
|
||||
inorder.verify(out, times(1)).writeInt(eq(deltaLogPaths.get(0).length()));
|
||||
inorder.verify(out, times(1)).write(aryEq(deltaLogPaths.get(0).getBytes(StandardCharsets.UTF_8)));
|
||||
// verify there are no more interactions happened on the mocked object
|
||||
inorder.verifyNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFields() throws IOException {
|
||||
// create a mock for DataOutput that will be used in the readFields method
|
||||
// this way we can capture and verify if correct arguments were passed
|
||||
DataInput in = mock(DataInput.class);
|
||||
|
||||
// register the mock responses to be returned when particular method call happens
|
||||
// on the mocked object
|
||||
when(in.readByte()).thenReturn((byte) fileSplitName.length());
|
||||
// Answer implementation is used to guarantee the response in sequence of the mock method calls
|
||||
// since the same method is called many times, we need to return the responses in proper sequence
|
||||
when(in.readInt()).thenAnswer(new Answer<Integer>() {
|
||||
private int count = 0;
|
||||
private int[] answers = new int[]{basePath.length(), maxCommitTime.length(), deltaLogPaths.size(), deltaLogPaths.get(0).length()};
|
||||
|
||||
@Override
|
||||
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return answers[count++];
|
||||
}
|
||||
});
|
||||
Answer<Void> readFullyAnswer = new Answer<Void>() {
|
||||
private int count = 0;
|
||||
private byte[][] answers = new byte[][]{
|
||||
fileSplitName.getBytes(StandardCharsets.UTF_8),
|
||||
basePath.getBytes(StandardCharsets.UTF_8),
|
||||
maxCommitTime.getBytes(StandardCharsets.UTF_8),
|
||||
deltaLogPaths.get(0).getBytes(StandardCharsets.UTF_8),
|
||||
};
|
||||
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
byte[] bytes = invocation.getArgumentAt(0, byte[].class);
|
||||
byte[] answer = answers[count++];
|
||||
System.arraycopy(answer, 0, bytes, 0, answer.length);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
doAnswer(readFullyAnswer).when(in).readFully(any());
|
||||
doAnswer(readFullyAnswer).when(in).readFully(any(), anyInt(), anyInt());
|
||||
|
||||
// call readFields with mocked object
|
||||
HoodieRealtimeFileSplit read = new HoodieRealtimeFileSplit();
|
||||
read.readFields(in);
|
||||
|
||||
// assert proper returns after reading from the mocked object
|
||||
assertEquals(basePath, read.getBasePath());
|
||||
assertEquals(maxCommitTime, read.getMaxCommitTime());
|
||||
assertEquals(deltaLogPaths, read.getDeltaLogPaths());
|
||||
assertEquals(split.toString(), read.toString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user