- * TODO: These unit tests are nice and fast, but we need integration tests too. For that, we'll need an AWS instance
- * under our control that we can host test data on.
- *
- * @author cwardgar
- * @since 2015/08/26
- */
-class ThreddsS3ClientImplSpec extends Specification {
- private static final Logger logger = LoggerFactory.getLogger(ThreddsS3ClientImplSpec)
-
- ObjectListing emptyMockObjectListing
- ObjectListing nonEmptyMockObjectListing
- AmazonServiceException amazonServiceException
-
- def setup() {
- emptyMockObjectListing = Mock(ObjectListing) {
- getObjectSummaries() >> []
- getCommonPrefixes() >> []
- }
-
- nonEmptyMockObjectListing = Mock(ObjectListing) {
- getObjectSummaries() >> [Mock(S3ObjectSummary)]
- getCommonPrefixes() >> ['fake']
- }
-
- // Create exception that stubbed methods will throw.
- amazonServiceException = new AmazonServiceException("error")
- amazonServiceException.setStatusCode(404)
- }
-
- def "null key"() {
- setup: "create mock to avoid actually connecting to S3"
- AmazonS3Client amazonS3Client = Mock(AmazonS3Client) {
- // This is the behavior of the actual AmazonS3Client for these 3 methods when key is null.
- getObjectMetadata(*_) >> { throw new IllegalArgumentException("null key") }
- listObjects(*_) >> nonEmptyMockObjectListing
- getObject(*_) >> { throw new IllegalArgumentException("null key") }
- }
-
- and: "create ThreddsS3Client that uses the mock AmazonS3Client"
- ThreddsS3Client threddsS3Client = new ThreddsS3ClientImpl(amazonS3Client)
-
- and: "create URI with null key (not that it matters with the mocking we've done)"
- S3URI s3uri = new S3URI("s3://imos-data")
-
- expect: "null key"
- threddsS3Client.getObjectMetadata(s3uri) == null
- threddsS3Client.listObjects(s3uri).commonPrefixes == ['fake'] // doesn't need a key.
- threddsS3Client.saveObjectToFile(s3uri, new File("some file")) == null
- }
-
- def "non-existent bucket"() {
- setup: "create mock to avoid actually connecting to S3"
- AmazonS3Client amazonS3Client = Mock(AmazonS3Client) {
- // This is the behavior of the actual AmazonS3Client for these 3 methods when bucket is non-existent.
- getObjectMetadata(*_) >> { throw amazonServiceException }
- listObjects(*_) >> { throw amazonServiceException }
- getObject(*_) >> { throw amazonServiceException }
- }
-
- and: "create ThreddsS3Client that uses the mock AmazonS3Client"
- ThreddsS3Client threddsS3Client = new ThreddsS3ClientImpl(amazonS3Client)
-
- and: "create URI with non-existent bucket (not that it matters with the mocking we've done)"
- S3URI s3uri = new S3URI("s3://non-existent-bucket/blah")
-
- expect: "non-existent bucket"
- threddsS3Client.getObjectMetadata(s3uri) == null
- threddsS3Client.listObjects(s3uri) == null
- threddsS3Client.saveObjectToFile(s3uri, new File("some file")) == null
- }
-
- def "non-existent key"() {
- setup: "create mock to avoid actually connecting to S3"
- AmazonS3Client amazonS3Client = Mock(AmazonS3Client) {
- // This is the behavior of the actual AmazonS3Client for these 3 methods when key is non-existent.
- getObjectMetadata(*_) >> { throw amazonServiceException }
- listObjects(*_) >> emptyMockObjectListing
- getObject(*_) >> { throw amazonServiceException }
- }
-
- and: "create ThreddsS3Client that uses the mock AmazonS3Client"
- ThreddsS3Client threddsS3Client = new ThreddsS3ClientImpl(amazonS3Client)
-
- and: "create URI with non-existent key (not that it matters with the mocking we've done)"
- S3URI s3uri = new S3URI("s3://imos-data/non-existent-key")
-
- expect: "non-existent key"
- threddsS3Client.getObjectMetadata(s3uri) == null
- threddsS3Client.listObjects(s3uri) == null
- threddsS3Client.saveObjectToFile(s3uri, new File("some file")) == null
- }
-
- def "existent bucket and key"() {
- setup: "create mock ObjectMetadata"
- ObjectMetadata mockObjectMetadata = Mock(ObjectMetadata) {
- getContentType() >> 'fake'
- }
-
- and: "create mock to avoid actually connecting to S3"
- AmazonS3Client amazonS3Client = Mock(AmazonS3Client) {
- // This is the behavior of the actual AmazonS3Client for these 3 methods when bucket and key exist.
- getObjectMetadata(*_) >> mockObjectMetadata // Non-null ObjectMetadata
- listObjects(*_) >> emptyMockObjectListing // Empty ObjectListing
- getObject(*_) >> mockObjectMetadata // Non-null ObjectMetadata
- }
-
- and: "create ThreddsS3Client that uses the mock AmazonS3Client"
- ThreddsS3Client threddsS3Client = new ThreddsS3ClientImpl(amazonS3Client)
-
- and: "create URI with existent bucket and key (not that it matters with the mocking we've done)"
- S3URI s3uri = new S3URI("s3://bucket/dataset.nc")
-
- expect: "existent bucket and key"
- threddsS3Client.getObjectMetadata(s3uri).contentType == 'fake'
- threddsS3Client.listObjects(s3uri) == null
- threddsS3Client.saveObjectToFile(s3uri, s3uri.getTempFile()).name.endsWith('dataset.nc')
- }
-}
diff --git a/netcdf-java-testing-platform/build.gradle b/netcdf-java-testing-platform/build.gradle
index 3163b2a19c..5527e0abee 100644
--- a/netcdf-java-testing-platform/build.gradle
+++ b/netcdf-java-testing-platform/build.gradle
@@ -18,10 +18,6 @@ dependencies {
// Fluent assertions for Java
api 'com.google.truth:truth:1.0'
- // These two are for Spock.
- api 'org.spockframework:spock-core:1.3-groovy-2.5'
- api 'org.codehaus.groovy:groovy-all:2.5.12'
-
//mockito
api 'org.mockito:mockito-core:2.28.2'
@@ -33,14 +29,6 @@ dependencies {
// opendap, dap4, and httpservices
api 'org.testcontainers:testcontainers:1.19.7'
-
- // In Spock, allows mocking of classes (in addition to interfaces).
- // todo: remove with legacy in 6
- runtime 'cglib:cglib-nodep:3.2.4'
-
- // In Spock, allows mocking of classes without default constructor (together with CGLIB).
- // todo: remove with legacy in 6
- runtime 'org.objenesis:objenesis:2.4'
}
}
diff --git a/netcdf4/build.gradle b/netcdf4/build.gradle
index 7f1cf95400..9038c9cf91 100644
--- a/netcdf4/build.gradle
+++ b/netcdf4/build.gradle
@@ -4,7 +4,6 @@ ext.url = 'https://www.unidata.ucar.edu/software/netcdf/'
apply from: "$rootDir/gradle/any/dependencies.gradle"
apply from: "$rootDir/gradle/any/java-library.gradle"
-apply plugin: 'groovy' // For Spock tests.
apply plugin: 'jacoco'
dependencies {
@@ -18,8 +17,6 @@ dependencies {
testImplementation project(':cdm-test-utils')
testImplementation 'com.google.truth:truth'
- testImplementation 'org.codehaus.groovy:groovy-all' // for spock
- testImplementation 'org.spockframework:spock-core'
testRuntimeOnly 'ch.qos.logback:logback-classic'
}
@@ -31,7 +28,6 @@ dependencies {
sourceSets {
unloadedTest {
- groovy.srcDir file('src/unloadedTest/groovy')
resources.srcDir file('src/unloadedTest/resources')
compileClasspath += sourceSets.main.output + configurations.testCompileClasspath
runtimeClasspath += output + sourceSets.main.output + configurations.testRuntimeClasspath
diff --git a/netcdf4/src/main/java/ucar/nc2/jni/netcdf/Nc4Iosp.java b/netcdf4/src/main/java/ucar/nc2/jni/netcdf/Nc4Iosp.java
index 2bcb679edd..91d45f12e6 100755
--- a/netcdf4/src/main/java/ucar/nc2/jni/netcdf/Nc4Iosp.java
+++ b/netcdf4/src/main/java/ucar/nc2/jni/netcdf/Nc4Iosp.java
@@ -1131,7 +1131,7 @@ private String nc_inq_var_name(int grpid, int varno) throws IOException {
}
//////////////////////////////////////////////////////////////////////////
- private static class Vinfo {
+ static class Vinfo {
final Group4 g4;
int varid, typeid;
UserType utype; // may be null
@@ -1143,7 +1143,7 @@ private static class Vinfo {
}
}
- private static class Group4 {
+ static class Group4 {
final int grpid;
final Group g;
final Group4 parent;
diff --git a/netcdf4/src/test/groovy/ucar/nc2/jni/netcdf/Nc4IospMiscSpec.groovy b/netcdf4/src/test/groovy/ucar/nc2/jni/netcdf/Nc4IospMiscSpec.groovy
deleted file mode 100644
index 2cd3592be7..0000000000
--- a/netcdf4/src/test/groovy/ucar/nc2/jni/netcdf/Nc4IospMiscSpec.groovy
+++ /dev/null
@@ -1,172 +0,0 @@
-package ucar.nc2.jni.netcdf
-
-import org.junit.Rule
-import org.junit.rules.TemporaryFolder
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import spock.lang.Specification
-import spock.lang.Unroll
-import ucar.ma2.Array
-import ucar.ma2.ArrayChar
-import ucar.ma2.DataType
-import ucar.nc2.Attribute
-import ucar.nc2.Dimension
-import ucar.nc2.NetcdfFile
-import ucar.nc2.NetcdfFileWriter
-import ucar.nc2.Variable
-
-/**
- * Tests miscellaneous aspects of Nc4Iosp.
- *
- * @author cwardgar
- * @since 2017-03-27
- */
-class Nc4IospMiscSpec extends Specification {
- private static final Logger logger = LoggerFactory.getLogger(Nc4IospMiscSpec)
-
- @Rule TemporaryFolder tempFolder = new TemporaryFolder()
-
- /*
- * Demonstrates bug from https://www.unidata.ucar.edu/mailing_lists/archives/netcdf-java/2017/msg00012.html
- * Prior to fix, this test would fail for 'u_short', 'u_int', and 'u_long' variables with
- * "Unknown userType == 8", "Unknown userType == 9", and "Unknown userType == 11" errors respectively.
- */
- @Unroll // Report iterations of method independently.
- def "Nc4Iosp.readDataSection() can read '#varName' variables"() {
- setup: "locate test file"
- File file = new File(this.class.getResource("unsigned.nc4").toURI())
- assert file.exists()
-
- and: "open it as a NetcdfFile using Nc4Iosp"
- NetcdfFile ncFile = NetcdfFile.open(file.absolutePath, Nc4Iosp.class.canonicalName, -1, null, null)
-
- and: "grab the Nc4Iosp instance within so that we can test Nc4Iosp.readDataSection()"
- Nc4Iosp nc4Iosp = ncFile.iosp as Nc4Iosp
-
- when: "read all of var's data using readDataSection()"
- Variable var = ncFile.findVariable(varName)
- Nc4Iosp.Vinfo vinfo = var.SPobject as Nc4Iosp.Vinfo
- Array array = nc4Iosp.readDataSection(vinfo.g4.grpid, vinfo.varid, vinfo.typeid, var.shapeAsSection);
-
- then: "actual data equals expected data"
- array.storage == expectedData
-
- cleanup: "close NetcdfFile"
- ncFile?.close()
-
- where: "data are too big for their type. Overflow expected because Java doesn't support unsigned types"
- varName << [ "u_byte", "u_short", "u_int", "u_long" ]
- expectedData << [
- [(1 << 7), (1 << 7) + 1, (1 << 7) + 2] as byte[], // Will overflow to [-128, -127, -126]
- [(1 << 15), (1 << 15) + 1, (1 << 15) + 2] as short[],
- [(1 << 31), (1 << 31) + 1, (1 << 31) + 2] as int[],
- [(1L << 63), (1L << 63) + 1, (1L << 63) + 2] as long[]
- ];
- }
-
- /*
- * Demonstrates bug from
- * https://andy.unidata.ucar.edu/esupport/staff/index.php?_m=tickets&_a=viewticket&ticketid=28098
- * Prior to fix, primary2Dim and primary3Dim were not being identified as unlimited.
- */
- def "Nc4Iosp supports multiple groups, each containing an unlimited dimension"() {
- setup: "locate test file"
- File file = new File(this.class.getResource("DBP-690959.nc4").toURI())
- assert file.exists()
-
- and: "open it as a NetcdfFile using Nc4Iosp"
- NetcdfFile ncFile = NetcdfFile.open(file.absolutePath, Nc4Iosp.class.canonicalName, -1, null, null)
-
- and: "find unlimited dimensions"
- Dimension primary1Dim = ncFile.findDimension("/group1/primary")
- Dimension primary2Dim = ncFile.findDimension("/group2/primary")
- Dimension primary3Dim = ncFile.findDimension("/group3/primary")
-
- expect: "all dimensions are unlimited"
- primary1Dim.isUnlimited()
- primary2Dim.isUnlimited()
- primary3Dim.isUnlimited()
-
- cleanup: "close NetcdfFile"
- ncFile?.close()
- }
-
- def "create NetCDF-4 file with unlimited dimension"() {
- setup: "create temp file that will be deleted after test by TemporaryFolder @Rule"
- File tempFile = new File(tempFolder.root, "Nc4IospMiscSpec.nc4")
-
- and: "open a NetcdfFileWriter that will write NetCDF-4 to tempFile"
- NetcdfFileWriter ncWriter = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf4, tempFile.absolutePath)
-
- and: "add an unlimited dimension and create the file on disk"
- Dimension dimBefore = ncWriter.addDimension(null, "dim", 3, true, false)
- ncWriter.create()
-
- and: "close the file for writing and reopen it for reading"
- ncWriter.close()
- NetcdfFile ncFile = NetcdfFile.open(tempFile.absolutePath)
-
- expect: "the dimension is the same after the write/read round-trip"
- Dimension dimAfter = ncFile.findDimension(dimBefore.fullName)
- // Failed prior to fix, because dimAfter was not unlimited.
- dimBefore.equals dimAfter
-
- cleanup: "close writer and reader"
- ncWriter?.close() // Under normal circumstances, this will already be closed. Luckily method is idempotent.
- ncFile?.close()
- }
-
- def "create NetCDF-4 file null valued attributes"() {
- setup: "create temp file that will be deleted after test by TemporaryFolder @Rule"
- File tempFile = new File(tempFolder.root, "Nc4IospMiscSpec.nc4")
-
- and: "open a NetcdfFileWriter that will write NetCDF-4 to tempFile"
- NetcdfFileWriter ncWriter = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf4, tempFile.absolutePath)
-
- and: "add a numerical valued attribute with a null value"
- Attribute attrNum = new Attribute("nullvalnum", DataType.INT)
- Attribute attrNumBefore = ncWriter.addGlobalAttribute(attrNum)
-
- and: "add a string valued attribute with a null value"
- Attribute attrStr = new Attribute("nullvalstr", DataType.STRING)
- Attribute attrStrBefore = ncWriter.addGlobalAttribute(attrStr)
-
- and: "add a character valued attribute with a null value"
- Attribute attrChar = new Attribute("nullvalchar", DataType.CHAR)
- Attribute attrCharBefore = ncWriter.addGlobalAttribute(attrChar)
-
- and: "add a character valued attribute with a specific null char value"
- Attribute attrNullChar = new Attribute("nullcharvalchar", DataType.CHAR)
- Array attrNullCharValue = ArrayChar.makeFromString("\0", 1);
- attrNullChar.setValues(attrNullCharValue)
- Attribute attrNullCharBefore = ncWriter.addGlobalAttribute(attrNullChar)
- ncWriter.create()
-
- and: "close the file for writing and reopen it for reading"
- ncWriter.close()
- NetcdfFile ncFile = NetcdfFile.open(tempFile.absolutePath)
-
- expect: "the value of the attributes are null"
- Attribute attrNumAfter = ncFile.findGlobalAttribute(attrNumBefore.fullName)
- attrNumBefore.getValues().equals attrNumAfter.getValues()
- attrNumBefore.getValues() == null
-
- Attribute attrStrAfter = ncFile.findGlobalAttribute(attrStrBefore.fullName)
- attrStrBefore.getValues().equals attrStrAfter.getValues()
- attrStrBefore.getValues() == null
-
- Attribute attrCharAfter = ncFile.findGlobalAttribute(attrCharBefore.fullName)
- attrCharBefore.getValues().equals attrCharAfter.getValues()
- attrCharBefore.getValues() == null
-
- Attribute attrNullCharAfter = ncFile.findGlobalAttribute(attrNullCharBefore.fullName)
- attrNullCharBefore.getValues().getSize() == attrNullCharAfter.getValues().getSize()
- attrNullCharBefore.getValues().getSize() == 1
- attrNullCharBefore.getValue(0).equals(attrNullCharAfter.getValue(0))
- attrNullCharBefore.equals(attrNullCharAfter)
-
- cleanup: "close writer and reader"
- ncWriter?.close() // Under normal circumstances, this will already be closed. Luckily method is idempotent.
- ncFile?.close()
- }
-}
diff --git a/netcdf4/src/test/java/ucar/nc2/jni/netcdf/TestNc4IospMisc.java b/netcdf4/src/test/java/ucar/nc2/jni/netcdf/TestNc4IospMisc.java
new file mode 100644
index 0000000000..757173b63d
--- /dev/null
+++ b/netcdf4/src/test/java/ucar/nc2/jni/netcdf/TestNc4IospMisc.java
@@ -0,0 +1,144 @@
+package ucar.nc2.jni.netcdf;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import ucar.ma2.Array;
+import ucar.ma2.ArrayChar;
+import ucar.ma2.DataType;
+import ucar.ma2.MAMath;
+import ucar.nc2.Attribute;
+import ucar.nc2.Dimension;
+import ucar.nc2.NetcdfFile;
+import ucar.nc2.NetcdfFileWriter;
+import ucar.nc2.Variable;
+
+public class TestNc4IospMisc {
+
+ @Rule
+ public final TemporaryFolder tempFolder = new TemporaryFolder();
+
+ /*
+ * Demonstrates bug from https://www.unidata.ucar.edu/mailing_lists/archives/netcdf-java/2017/msg00012.html
+ * Prior to fix, this test would fail for 'u_short', 'u_int', and 'u_long' variables with
+ * "Unknown userType == 8", "Unknown userType == 9", and "Unknown userType == 11" errors respectively.
+ */
+ @Test
+ public void shouldReadDataSectionOfVariables() throws Exception {
+ String[] varNames = new String[] {"u_byte", "u_short", "u_int", "u_long"};
+ List