/*
* The MIT License
*
* Copyright (c) 2010 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;
import htsjdk.samtools.util.IOUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* Test BAM file index creation
*/
public class BAMIndexWriterTest {
// Two input files for basic test
private final String BAM_FILE_LOCATION = "testdata/htsjdk/samtools/BAMFileIndexTest/index_test.bam";
private final String BAI_FILE_LOCATION = "testdata/htsjdk/samtools/BAMFileIndexTest/index_test.bam.bai";
private final File BAM_FILE = new File(BAM_FILE_LOCATION);
private final File BAI_FILE = new File(BAI_FILE_LOCATION);
private final boolean mVerbose = true;
@Test(enabled=true)
public void testWriteText() throws Exception {
// Compare the text form of the c-generated bai file and a java-generated one
final File cBaiTxtFile = File.createTempFile("cBai.", ".bai.txt");
BAMIndexer.createAndWriteIndex(BAI_FILE, cBaiTxtFile, true);
verbose ("Wrote textual C BAM Index file " + cBaiTxtFile);
final File javaBaiFile = File.createTempFile("javaBai.", "java.bai");
final File javaBaiTxtFile = new File(javaBaiFile.getAbsolutePath() + ".txt");
final SAMFileReader bam = new SAMFileReader(BAM_FILE);
BAMIndexer.createIndex(bam, javaBaiFile);
verbose ("Wrote binary Java BAM Index file " + javaBaiFile);
// now, turn the bai file into text
BAMIndexer.createAndWriteIndex(javaBaiFile, javaBaiTxtFile, true);
// and compare them
verbose ("diff " + javaBaiTxtFile + " " + cBaiTxtFile);
IOUtil.assertFilesEqual(javaBaiTxtFile, cBaiTxtFile);
cBaiTxtFile.deleteOnExit();
javaBaiFile.deleteOnExit();
javaBaiTxtFile.deleteOnExit();
}
@Test(enabled=true)
public void testWriteBinary() throws Exception {
// Compare java-generated bai file with c-generated and sorted bai file
final File javaBaiFile = File.createTempFile("javaBai.", ".bai");
final SAMFileReader bam = new SAMFileReader(BAM_FILE);
BAMIndexer.createIndex(bam, javaBaiFile);
verbose ("Wrote binary java BAM Index file " + javaBaiFile);
final File cRegeneratedBaiFile = File.createTempFile("cBai.", ".bai");
BAMIndexer.createAndWriteIndex(BAI_FILE, cRegeneratedBaiFile, false);
verbose ("Wrote sorted C binary BAM Index file " + cRegeneratedBaiFile);
// Binary compare of javaBaiFile and cRegeneratedBaiFile should be the same
verbose ("diff " + javaBaiFile + " " + cRegeneratedBaiFile);
IOUtil.assertFilesEqual(javaBaiFile, cRegeneratedBaiFile);
javaBaiFile.deleteOnExit();
cRegeneratedBaiFile.deleteOnExit();
}
@Test(enabled = false, dataProvider = "linearIndexTestData")
/** Test linear index at specific references and windows */
public void testLinearIndex(String testName, String filepath, int problemReference, int problemWindowStart, int problemWindowEnd, int expectedCount) {
final SAMFileReader sfr = new SAMFileReader(new File(filepath));
for (int problemWindow = problemWindowStart; problemWindow <= problemWindowEnd; problemWindow++) {
int count = countAlignmentsInWindow(problemReference, problemWindow, sfr, expectedCount);
if (expectedCount != -1)
assertEquals(expectedCount, count);
}
}
@DataProvider(name = "linearIndexTestData")
public Object[][] getLinearIndexTestData() {
// Add data here for test cases, reference, and windows where linear index needs testing
return new Object[][]{
new Object[]{"index_test", BAM_FILE_LOCATION, 1, 29, 66, -1}, // 29-66
new Object[]{"index_test", BAM_FILE_LOCATION, 1, 68, 118, -1}, // 29-66
};
}
private int countAlignmentsInWindow(int reference, int window, SAMFileReader reader, int expectedCount) {
final int SIXTEEN_K = 1 << 14; // 1 << LinearIndex.BAM_LIDX_SHIFT
final int start = window >> 14; // window * SIXTEEN_K;
final int stop = ((window + 1) >> 14) - 1; // (window + 1 * SIXTEEN_K) - 1;
final String chr = reader.getFileHeader().getSequence(reference).getSequenceName();
// get records for the entire linear index window
SAMRecordIterator iter = reader.queryOverlapping(chr, start, stop);
SAMRecord rec;
int count = 0;
while (iter.hasNext()) {
rec = iter.next();
count++;
if (expectedCount == -1)
System.err.println(rec.getReadName());
}
iter.close();
return count;
}
@Test(enabled=false, dataProvider = "indexComparisonData")
/** Test linear index at all references and windows, comparing with existing index */
public void compareLinearIndex(String testName, String bamFile, String bamIndexFile) throws IOException{
// compare index generated from bamFile with existing bamIndex file
// by testing all the references' windows and comparing the counts
// 1. generate bai file
// 2. count its references
// 3. count bamIndex references comparing counts
// 1. generate bai file
File bam = new File(bamFile);
assertTrue(bam.exists(), testName + " input bam file doesn't exist: " + bamFile);
File indexFile1 = createIndexFile(bam);
assertTrue(indexFile1.exists(), testName + " generated bam file's index doesn't exist: " + indexFile1);
// 2. count its references
File indexFile2 = new File (bamIndexFile);
assertTrue(indexFile2.exists(), testName + " input index file doesn't exist: " + indexFile2);
final CachingBAMFileIndex existingIndex1 = new CachingBAMFileIndex(indexFile1, null); // todo null sequence dictionary?
final CachingBAMFileIndex existingIndex2 = new CachingBAMFileIndex(indexFile2, null);
final int n_ref = existingIndex1.getNumberOfReferences();
assertEquals(n_ref, existingIndex2.getNumberOfReferences());
final SAMFileReader reader1 = new SAMFileReader(bam, indexFile1, false);
final SAMFileReader reader2 = new SAMFileReader(bam, indexFile2, false );
System.out.println("Comparing " + n_ref + " references in " + indexFile1 + " and " + indexFile2);
for (int i = 0; i < n_ref; i++) {
final BAMIndexContent content1 = existingIndex1.getQueryResults(i);
final BAMIndexContent content2 = existingIndex2.getQueryResults(i);
if (content1 == null){
assertTrue(content2 == null, "No content for 1st bam index, but content for second at reference" + i);
continue;
}
int[] counts1 = new int[LinearIndex.MAX_LINEAR_INDEX_SIZE];
int[] counts2 = new int[LinearIndex.MAX_LINEAR_INDEX_SIZE];
LinearIndex li1 = content1.getLinearIndex();
LinearIndex li2 = content2.getLinearIndex();
// todo not li1 and li2 sizes may differ. Implies 0's in the smaller index windows
// 3. count bamIndex references comparing counts
int baiSize = Math.max(li1.size(), li2.size());
for (int win = 0; win < baiSize; win++) {
counts1[win] = countAlignmentsInWindow(i, win, reader1, 0);
counts2[win] = countAlignmentsInWindow(i, win, reader2, counts1[win]);
assertEquals(counts2[win], counts1[win], "Counts don't match for reference " + i +
" window " + win );
}
}
indexFile1.deleteOnExit();
}
@DataProvider(name="indexComparisonData")
public Object[][] getIndexComparisonData() {
// enter bam file and alternate index file to be tested against generated bam index
return new Object[][]{
new Object[]{"index_test", BAM_FILE_LOCATION, BAI_FILE_LOCATION} ,
};
}
/** generates the index file using the latest java index generating code */
private File createIndexFile(File bamFile) throws IOException {
final File bamIndexFile = File.createTempFile("Bai.", ".bai");
final SAMFileReader bam = new SAMFileReader(bamFile);
BAMIndexer.createIndex(bam, bamIndexFile);
verbose ("Wrote BAM Index file " + bamIndexFile);
return bamIndexFile;
}
private void verbose(final String text) {
if (mVerbose) {
System.out.println("#BAMIndexWriterTest " + text);
}
}
}