Skip to content

Commit 8f5fe60

Browse files
authored
Fix up unit tests so that gradle correctly reports results (no more erroneous skips) (#1759)
* Remove testng.xml that caused Gradle to report all tests as skipped. The testng.xml in src/test/resources was auto-discovered by TestNG on the classpath, causing it to run tests through its own parallel infrastructure (parallel="methods" thread-count="8") instead of through Gradle's TestListener. As a result, Gradle never received test completion events and reported all ~19,000 tests as "skipped" with 0 successes, even though they actually ran. The build.gradle already configures TestNG properly via useTestNG {} blocks with the correct group inclusions/exclusions, making this file redundant. * Replace @BeforeTest/@AfterTest with @BeforeMethod/@AfterMethod in most tests. Without testng.xml, TestNG puts all test classes into a single <test> scope. A @BeforeTest failure in any class (e.g. HtsgetBAMFileReaderTest trying to connect to a local htsget server) can cascade to skip every test in the entire suite — resulting in 19,000+ tests reported as skipped with 0 successes. * Auto-detect samtools on PATH when HTSJDK_SAMTOOLS_BIN is not set. SamtoolsTestUtils.getSamtoolsBin() now checks: (1) the HTSJDK_SAMTOOLS_BIN environment variable, (2) "which samtools" on the system PATH, (3) the default /usr/local/bin/samtools. This allows tests that depend on samtools (CRAM31Tests, HtsCRAMCodec31Test, SamtoolsTestUtilsTest) to pass without requiring the environment variable when samtools is already installed. * Add "htsget" test group and exclude it from the default test run. Tests that require a local htsget server (HtsgetBAMFileReaderTest, HtsgetBAMCodecTest) are now tagged with groups="htsget" and excluded by default, alongside ftp/http/sra/ena. HTSGET permutations are also removed from SamReaderFactoryTest's data provider since the dedicated htsget test classes provide full coverage. TestNGUtils.getDataProviders() now accepts excluded groups so that TestDataProviders doesn't try to invoke data providers from classes that require unavailable external infrastructure. * Fix thread-safety issues in tests for parallel class execution. JimFS: Replace hardcoded Jimfs.newFileSystem("test", ...) with Jimfs.newFileSystem(Configuration.unix()) in 7 test files so concurrent test classes don't collide on the filesystem name "jimfs://test". BlockCompressedInputStreamTest: Pass CountingInflaterFactory directly to each stream constructor instead of setting it as a global default via BlockGunzipper.setDefaultInflaterFactory(). The global approach caused flaky failures when other classes concurrently used the same global InflaterFactory. The test now verifies the same behavior (custom inflater is used and call count is correct) without touching shared state. * Switch to TestNG parallel="classes" with 2x CPU threads. Use TestNG's in-process parallel="classes" instead of Gradle's maxParallelForks. Unlike parallel="methods" (which breaks Gradle's test listener), parallel="classes" preserves correct event ordering since onBeforeClass completes before any onTestStart for that class. * Fix testExternalApis CI job and remove duplicate testFTP job. testExternalApis was running ./gradlew testFTP (copy-paste error) instead of ./gradlew testExternalApis (which runs the sra, http, and ena groups). The testFTP job was an exact duplicate of the broken testExternalApis job, both running the same testFTP gradle task. Removed it since the target FTP site no longer exists.
1 parent c6729bb commit 8f5fe60

36 files changed

Lines changed: 140 additions & 147 deletions

.github/workflows/tests.yml

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -60,41 +60,13 @@ jobs:
6060
- name: Compile with Gradle
6161
run: ./gradlew compileJava
6262
- name: Run tests
63-
run: ./gradlew testFTP jacocoTestReport
64-
# - name: Upload to codecov
65-
# run: bash <(curl -s https://raw.githubusercontent.com/broadinstitute/codecov-bash-uploader/main/codecov-verified.bash)
63+
run: ./gradlew testExternalApis jacocoTestReport
6664
- name: Upload test results
6765
if: always()
6866
uses: actions/upload-artifact@v4
6967
with:
7068
name: test-results-external-apis
7169
path: build/reports/tests
72-
testFTP:
73-
continue-on-error: true
74-
runs-on: ubuntu-latest
75-
name: FTP tests
76-
steps:
77-
- uses: actions/checkout@v3
78-
- name: Set up java 17
79-
uses: actions/setup-java@v3
80-
with:
81-
java-version: '17'
82-
distribution: 'adopt'
83-
cache: gradle
84-
- name: Grant execute permission for gradlew
85-
run: chmod +x gradlew
86-
- name: Compile with Gradle
87-
run: ./gradlew compileJava
88-
- name: Run tests
89-
run: ./gradlew testFTP jacocoTestReport
90-
# - name: Upload to codecov
91-
# run: bash <(curl -s https://raw.githubusercontent.com/broadinstitute/codecov-bash-uploader/main/codecov-verified.bash)
92-
- name: Upload test results
93-
if: always()
94-
uses: actions/upload-artifact@v4
95-
with:
96-
name: test-results-ftp
97-
path: build/reports/tests
9870
spotBugs:
9971
runs-on: ubuntu-latest
10072
name: SpotBugs

build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,11 @@ import org.gradle.internal.os.OperatingSystem;
8484
tasks.withType(Test).configureEach { task ->
8585
task.outputs.upToDateWhen { false } // tests will always rerun
8686

87-
// Always run serially because there are some very badly behaved tests in HTSJDK that
88-
// will cause errors and even deadlocks if run multi-threaded
8987
task.maxParallelForks = 1
9088

9189
// set heap size for the test JVM(s)
9290
task.minHeapSize = "1G"
93-
task.maxHeapSize = "2G"
91+
task.maxHeapSize = "6G"
9492

9593
task.jvmArgs '-Djava.awt.headless=true' //this prevents awt from displaying a java icon while the tests are running
9694

@@ -141,10 +139,12 @@ test {
141139

142140
useTestNG {
143141
if (OperatingSystem.current().isUnix()) {
144-
excludeGroups "slow", "broken", "defaultReference", "optimistic_vcf_4_4", "ftp", "http", "sra", "ena"
142+
excludeGroups "slow", "broken", "defaultReference", "optimistic_vcf_4_4", "ftp", "http", "sra", "ena", "htsget"
145143
} else {
146-
excludeGroups "slow", "broken", "defaultReference", "optimistic_vcf_4_4", "ftp", "http", "sra", "ena", "unix"
144+
excludeGroups "slow", "broken", "defaultReference", "optimistic_vcf_4_4", "ftp", "http", "sra", "ena", "htsget", "unix"
147145
}
146+
parallel = "classes"
147+
threadCount = 2 * Runtime.runtime.availableProcessors()
148148
}
149149
} dependsOn testWithDefaultReference, testWithOptimisticVCF4_4
150150

src/test/java/htsjdk/TestDataProviders.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ public void independentTestOfDataProviderTest() throws Exception {
3535
"getDataProviders didn't find testAllDataProvidersData, which is in this class. Something is wrong.");
3636
}
3737

38+
/** Groups that require external infrastructure and are excluded from the default test run. */
39+
private static final Set<String> EXCLUDED_GROUPS = Set.of("htsget", "ftp", "http", "sra", "ena");
40+
3841
@DataProvider(name = "DataprovidersThatDontTestThemselves")
3942
private Iterator<Object[]> testAllDataProvidersData() throws Exception {
4043

41-
return TestNGUtils.getDataProviders("htsjdk");
44+
return TestNGUtils.getDataProviders("htsjdk", EXCLUDED_GROUPS);
4245
}
4346

4447
// runs all the @DataProviders it gets from DataprovidersThatDontTestThemselves.

src/test/java/htsjdk/beta/codecs/reads/htsget/HtsgetBAM/HtsgetBAMCodecTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import java.util.Iterator;
2828

2929
// NOTE: running these tests require a local htsget server
30-
30+
@Test(groups = "htsget")
3131
public class HtsgetBAMCodecTest extends HtsjdkTest {
3232
private final static IOPath htsgetBAM = new HtsPath(
3333
HtsgetBAMFileReaderTest.HTSGET_ENDPOINT +

src/test/java/htsjdk/beta/codecs/variants/vcf/HtsVCFCodecTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ private Object[][] vcfReadWriteTests() {
6969

7070
@Test
7171
public void testRoundTripNio() throws IOException {
72-
try(FileSystem fs = Jimfs.newFileSystem("test", Configuration.unix())){
72+
try(FileSystem fs = Jimfs.newFileSystem(Configuration.unix())){
7373
final IOPath outputPath = IOUtils.createTempPath("roundTripVCFThroughPath", ".vcf");
7474
Path tribbleFileInJimfs = TestUtils.getTribbleFileInJimfs(TEST_VCF_WITH_INDEX.getRawInputString(), TEST_VCF_INDEX.getRawInputString(), fs);
7575
HtsPath vcf = new HtsPath(tribbleFileInJimfs.toUri().toString());

src/test/java/htsjdk/samtools/BAMFileReaderTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import htsjdk.samtools.util.CloseableIterator;
55
import htsjdk.samtools.util.CoordMath;
66
import org.testng.Assert;
7-
import org.testng.annotations.BeforeTest;
7+
import org.testng.annotations.BeforeMethod;
88
import org.testng.annotations.Test;
99

1010
import java.io.File;
@@ -28,7 +28,7 @@ public class BAMFileReaderTest extends HtsjdkTest {
2828
private static BAMFileReader bamFileReaderWrong;
2929
private static BAMFileReader bamFileReaderNull;
3030

31-
@BeforeTest
31+
@BeforeMethod
3232
public void init() throws IOException {
3333
bamFileReaderBAI = new BAMFileReader(bamFile, baiFileIndex, true, false, ValidationStringency.DEFAULT_STRINGENCY, DefaultSAMRecordFactory.getInstance());
3434
bamFileReaderCSI = new BAMFileReader(bamFile, csiFileIndex, true, false, ValidationStringency.DEFAULT_STRINGENCY, DefaultSAMRecordFactory.getInstance());

src/test/java/htsjdk/samtools/CRAMFileCRAIIndexTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import htsjdk.samtools.util.CoordMath;
1616
import htsjdk.samtools.util.IOUtil;
1717
import org.testng.Assert;
18-
import org.testng.annotations.BeforeTest;
18+
import org.testng.annotations.BeforeMethod;
1919
import org.testng.annotations.Test;
2020

2121
import java.io.*;
@@ -322,7 +322,7 @@ public void testQueryIntervalWithFilePointers() throws IOException {
322322
}
323323
}
324324

325-
@BeforeTest
325+
@BeforeMethod
326326
public void prepare() throws IOException {
327327
tmpCramFile = File.createTempFile(BAM_FILE.getName(), ".cram") ;
328328
tmpCramFile.deleteOnExit();

src/test/java/htsjdk/samtools/CRAMFileWriterWithIndexTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import htsjdk.samtools.util.CloseableIterator;
1010
import htsjdk.samtools.util.TestUtil;
1111
import org.testng.Assert;
12-
import org.testng.annotations.BeforeTest;
12+
import org.testng.annotations.BeforeMethod;
1313
import org.testng.annotations.Test;
1414

1515
import java.io.ByteArrayOutputStream;
@@ -150,7 +150,7 @@ public void testUnnecessaryIO() throws IOException {
150150
Assert.assertTrue(iterator.hasNext());
151151
}
152152

153-
@BeforeTest
153+
@BeforeMethod
154154
public void beforeTest() {
155155
header = new SAMFileHeader();
156156
header.setSortOrder(SAMFileHeader.SortOrder.coordinate);

src/test/java/htsjdk/samtools/CSIIndexTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import htsjdk.HtsjdkTest;
44
import org.testng.Assert;
5-
import org.testng.annotations.BeforeTest;
5+
import org.testng.annotations.BeforeClass;
66
import org.testng.annotations.DataProvider;
77
import org.testng.annotations.Test;
88

@@ -39,7 +39,7 @@ public class CSIIndexTest extends HtsjdkTest {
3939
public static final int BAI_EQUIVALENT_MIN_SHIFT = 14;
4040
public static final int BAI_EQUIVALENT_BIN_DEPTH = 6;
4141

42-
@BeforeTest
42+
@BeforeClass
4343
public void init() {
4444
bai = new DiskBasedBAMFileIndex(new File(TEST_DATA_DIR, "index_test.bam.bai"), null);
4545
csi = new CSIIndex(new File(TEST_DATA_DIR, "index_test.bam.csi"), false, null);

src/test/java/htsjdk/samtools/HtsgetBAMFileReaderTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
import htsjdk.HtsjdkTest;
44
import htsjdk.samtools.util.CloseableIterator;
55
import org.testng.Assert;
6-
import org.testng.annotations.AfterTest;
7-
import org.testng.annotations.BeforeTest;
6+
import org.testng.annotations.AfterMethod;
7+
import org.testng.annotations.BeforeMethod;
88
import org.testng.annotations.DataProvider;
99
import org.testng.annotations.Test;
1010

1111
import java.io.File;
1212
import java.io.IOException;
1313
import java.net.URI;
1414

15+
@Test(groups = "htsget")
1516
public class HtsgetBAMFileReaderTest extends HtsjdkTest {
1617
public static final String HTSGET_ENDPOINT = "http://127.0.0.1:3000/reads/";
1718
public static final String LOCAL_PREFIX = "htsjdk_test.";
@@ -30,7 +31,7 @@ public class HtsgetBAMFileReaderTest extends HtsjdkTest {
3031
private static HtsgetBAMFileReader bamFileReaderHtsgetPOST;
3132
private static HtsgetBAMFileReader bamFileReaderHtsgetAsync;
3233

33-
@BeforeTest
34+
@BeforeMethod
3435
public void init() throws IOException {
3536
bamFileReaderHtsgetGET = new HtsgetBAMFileReader(htsgetBAM, true, ValidationStringency.DEFAULT_STRINGENCY, DefaultSAMRecordFactory.getInstance(), false);
3637
bamFileReaderHtsgetGET.setUsingPOST(false);
@@ -45,7 +46,7 @@ public void init() throws IOException {
4546
Assert.assertFalse(bamFileReaderHtsgetAsync.isUsingPOST());
4647
}
4748

48-
@AfterTest
49+
@AfterMethod
4950
public void tearDown() {
5051
bamFileReaderHtsgetGET.close();
5152
bamFileReaderHtsgetPOST.close();

0 commit comments

Comments
 (0)