/*
* The MIT License
*
* Copyright (c) 2009 The Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package htsjdk.samtools.util;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Random;
public class SortingCollectionTest {
// Create a separate directory for files so it is possible to confirm that the directory is emptied
protected final File tmpDir = new File(System.getProperty("java.io.tmpdir") + "/" + System.getProperty("user.name"),
"SortingCollectionTest");
@BeforeTest void setup() {
// Clear out any existing files if the directory exists
if (tmpDir.exists()) {
for (final File f : tmpDir.listFiles()) {
f.delete();
}
}
tmpDir.mkdirs();
}
@AfterTest void tearDown() {
System.err.println("In SortingCollectionTest.tearDown. tmpDir: " + tmpDir);
if (tmpDir.exists()) {
for (final File f : tmpDir.listFiles()) {
f.delete();
}
tmpDir.delete();
}
}
protected boolean tmpDirIsEmpty() {
System.err.println("In SortingCollectionTest.tmpDirIsEmpty. tmpDir: " + tmpDir);
return tmpDir.listFiles().length == 0;
}
@DataProvider(name = "test1")
public Object[][] createTestData() {
return new Object[][] {
{"empty", 0, 100},
{"singleton", 1, 100},
{"less than threshold", 100, 200},
{"threshold minus 1", 99, 100},
{"greater than threshold", 550, 100},
{"threshold multiple", 600, 100},
{"threshold multiple plus one", 101, 100},
{"exactly threshold", 100, 100},
};
}
/**
* Generate some strings, put into SortingCollection, confirm that the right number of
* Strings come out, and in the right order.
* @param numStringsToGenerate
* @param maxRecordsInRam
*/
@Test(dataProvider = "test1")
public void testPositive(final String testName, final int numStringsToGenerate, final int maxRecordsInRam) {
final String[] strings = new String[numStringsToGenerate];
int numStringsGenerated = 0;
final SortingCollection<String> sortingCollection = makeSortingCollection(maxRecordsInRam);
for (final String s : new RandomStringGenerator(numStringsToGenerate)) {
sortingCollection.add(s);
strings[numStringsGenerated++] = s;
}
Arrays.sort(strings, new StringComparator());
Assert.assertEquals(tmpDirIsEmpty(), numStringsToGenerate <= maxRecordsInRam);
sortingCollection.setDestructiveIteration(false);
assertIteratorEqualsList(strings, sortingCollection.iterator());
assertIteratorEqualsList(strings, sortingCollection.iterator());
sortingCollection.cleanup();
Assert.assertEquals(tmpDir.list().length, 0);
}
private void assertIteratorEqualsList(final String[] strings, final Iterator<String> sortingCollection) {
int i = 0;
while (sortingCollection.hasNext()) {
final String s = sortingCollection.next();
Assert.assertEquals(s, strings[i++]);
}
Assert.assertEquals(i, strings.length);
}
private SortingCollection<String> makeSortingCollection(final int maxRecordsInRam) {
return SortingCollection.newInstance(String.class, new StringCodec(), new StringComparator(),
maxRecordsInRam, tmpDir);
}
/**
* Generate pseudo-random Strings for testing
*/
static class RandomStringGenerator implements Iterable<String>, Iterator<String> {
Random random = new Random(0);
int numElementsToGenerate;
int numElementsGenerated = 0;
/**
* @param numElementsToGenerate Iteration ends after this many have been generated.
*/
RandomStringGenerator(final int numElementsToGenerate) {
this.numElementsToGenerate = numElementsToGenerate;
}
public Iterator<String> iterator() {
return this;
}
public boolean hasNext() {
return numElementsGenerated < numElementsToGenerate;
}
public String next() {
++numElementsGenerated;
return Integer.toString(random.nextInt());
}
public void remove() {
throw new UnsupportedOperationException();
}
}
static class StringComparator implements Comparator<String> {
public int compare(final String s, final String s1) {
return s.compareTo(s1);
}
}
static class StringCodec implements SortingCollection.Codec<String> {
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
OutputStream os;
InputStream is;
public SortingCollection.Codec<String> clone() {
return new StringCodec();
}
/**
* Where to write encoded output
*
* @param os
*/
public void setOutputStream(final OutputStream os) {
this.os = os;
}
/**
* Where to read encoded input from
*
* @param is
*/
public void setInputStream(final InputStream is) {
this.is = is;
}
/**
* Write object to file
*
* @param val what to write
*/
public void encode(final String val) {
try {
byteBuffer.clear();
byteBuffer.putInt(val.length());
os.write(byteBuffer.array());
os.write(val.getBytes());
} catch (IOException e) {
throw new RuntimeIOException(e);
}
}
/**
* Read the next record from the input stream and convert into a java object.
*
* @return null if no more records. Should throw exception if EOF is encountered in the middle of
* a record.
*/
public String decode() {
try {
byteBuffer.clear();
int bytesRead = is.read(byteBuffer.array());
if (bytesRead == -1) {
return null;
}
if (bytesRead != 4) {
throw new RuntimeException("Unexpected EOF in middle of record");
}
byteBuffer.limit(4);
final int length = byteBuffer.getInt();
final byte[] buf = new byte[length];
bytesRead = is.read(buf);
if (bytesRead != length) {
throw new RuntimeException("Unexpected EOF in middle of record");
}
return new String(buf);
} catch (IOException e) {
throw new RuntimeIOException(e);
}
}
}
}