Files
hdf5/java/jtest/TestH5Iffm.java
Allen Byrne b754dcb8f2 Move Java wrappers to FFM using jextract and java 25 (#5957)
FFM build requires Java 25, Jextract 25.
Generates FFM bindings during configure.
JNI is default when the requirements are not met or can be forced.
Presets added for maven and FFM - JNI is default selection.
Enhanced Maven options will work with either JNI or FFM
New Workflows for testing and maven uploads.
Extensive documentation changes for java.
2025-11-04 14:03:06 -06:00

487 lines
18 KiB
Java

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the LICENSE file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package jtest;
import static org.junit.Assert.*;
import static jtest.FfmTestSupport.*;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import org.hdfgroup.javahdf5.H5I_free_t;
import org.hdfgroup.javahdf5.H5I_iterate_func_t;
import org.hdfgroup.javahdf5.hdf5_h;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
/**
* FFM-only tests for HDF5 Identifier (H5I) operations.
*/
public class TestH5Iffm {
@Rule
public TestName testname = new TestName();
private static final String H5_FILE = "test_H5Iffm.h5";
long H5fid = hdf5_h.H5I_INVALID_HID();
long H5gid = hdf5_h.H5I_INVALID_HID();
@Before
public void createH5file()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
MemorySegment filename = stringToSegment(arena, H5_FILE);
H5fid = hdf5_h.H5Fcreate(filename, hdf5_h.H5F_ACC_TRUNC(), hdf5_h.H5P_DEFAULT(),
hdf5_h.H5P_DEFAULT());
assertTrue("H5Fcreate failed", isValidId(H5fid));
MemorySegment groupname = stringToSegment(arena, "Group1");
H5gid = hdf5_h.H5Gcreate2(H5fid, groupname, hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT(),
hdf5_h.H5P_DEFAULT());
assertTrue("H5Gcreate2 failed", isValidId(H5gid));
}
}
@After
public void deleteH5file()
{
if (isValidId(H5gid)) {
closeQuietly(H5gid, hdf5_h::H5Gclose);
H5gid = hdf5_h.H5I_INVALID_HID();
}
if (isValidId(H5fid)) {
closeQuietly(H5fid, hdf5_h::H5Fclose);
H5fid = hdf5_h.H5I_INVALID_HID();
}
System.out.println();
}
@Test
public void testH5Iget_type()
{
int file_type = hdf5_h.H5Iget_type(H5fid);
assertEquals("File type should be H5I_FILE", hdf5_h.H5I_FILE(), file_type);
int group_type = hdf5_h.H5Iget_type(H5gid);
assertEquals("Group type should be H5I_GROUP", hdf5_h.H5I_GROUP(), group_type);
}
@Test
public void testH5Iis_valid()
{
int result = hdf5_h.H5Iis_valid(H5fid);
assertTrue("File ID should be valid", result > 0);
result = hdf5_h.H5Iis_valid(H5gid);
assertTrue("Group ID should be valid", result > 0);
result = hdf5_h.H5Iis_valid(hdf5_h.H5I_INVALID_HID());
assertEquals("Invalid ID should not be valid", 0, result);
}
@Test
public void testH5Iget_name()
{
try (Arena arena = Arena.ofConfined()) {
// Get group name size
long name_size = hdf5_h.H5Iget_name(H5gid, MemorySegment.NULL, 0);
assertTrue("H5Iget_name size query failed", name_size > 0);
// Get group name
MemorySegment nameBuffer = arena.allocate(name_size + 1);
long actual_size = hdf5_h.H5Iget_name(H5gid, nameBuffer, name_size + 1);
assertTrue("H5Iget_name failed", actual_size > 0);
String name = nameBuffer.getString(0);
assertEquals("Group name should be /Group1", "/Group1", name);
}
}
@Test
public void testH5Iget_file_id()
{
long file_id = hdf5_h.H5Iget_file_id(H5gid);
assertTrue("H5Iget_file_id failed", isValidId(file_id));
int type = hdf5_h.H5Iget_type(file_id);
assertEquals("Should be file type", hdf5_h.H5I_FILE(), type);
hdf5_h.H5Fclose(file_id);
}
@Test
public void testH5Iinc_dec_ref()
{
// Get initial ref count
int ref_count = hdf5_h.H5Iget_ref(H5gid);
assertTrue("Initial ref count should be positive", ref_count > 0);
// Increment ref count
int new_count = hdf5_h.H5Iinc_ref(H5gid);
assertEquals("Ref count should increase by 1", ref_count + 1, new_count);
// Decrement ref count
new_count = hdf5_h.H5Idec_ref(H5gid);
assertEquals("Ref count should decrease by 1", ref_count, new_count);
}
@Test
public void testH5I_complete_workflow()
{
try (Arena arena = Arena.ofConfined()) {
// 1. Verify ID is valid
int result = hdf5_h.H5Iis_valid(H5gid);
assertTrue("ID should be valid", result > 0);
// 2. Get type
int type = hdf5_h.H5Iget_type(H5gid);
assertEquals("Type should be GROUP", hdf5_h.H5I_GROUP(), type);
// 3. Get name
long name_size = hdf5_h.H5Iget_name(H5gid, MemorySegment.NULL, 0);
assertTrue("Name size should be positive", name_size > 0);
// 4. Get file ID
long file_id = hdf5_h.H5Iget_file_id(H5gid);
assertTrue("File ID should be valid", isValidId(file_id));
// 5. Get ref count
int ref_count = hdf5_h.H5Iget_ref(H5gid);
assertTrue("Ref count should be positive", ref_count > 0);
hdf5_h.H5Fclose(file_id);
}
}
// ============================================================================
// User-Defined ID Type Tests (H5Iregister_type2)
// ============================================================================
@Test
public void testH5Iregister_type2()
{
try (Arena arena = Arena.ofConfined()) {
// Define free function callback (no-op for this test)
H5I_free_t.Function freeFunc = (MemorySegment obj, MemorySegment request) ->
{
// Simple free function that does nothing
// In real usage, this would free memory associated with the object
return 0; // Success
};
// Allocate callback function pointer
MemorySegment freeFuncPtr = H5I_free_t.allocate(freeFunc, arena);
// Register a new user-defined type
int myType = hdf5_h.H5Iregister_type2(0, freeFuncPtr);
assertTrue("H5Iregister_type2 should succeed", myType >= hdf5_h.H5I_NTYPES());
// Verify type exists
int exists = hdf5_h.H5Itype_exists(myType);
assertTrue("User type should exist", exists > 0);
// Get initial member count (should be 0)
MemorySegment numMembers = allocateLongArray(arena, 1);
int result = hdf5_h.H5Inmembers(myType, numMembers);
assertTrue("H5Inmembers should succeed", isSuccess(result));
assertEquals("Should have 0 members initially", 0L, getLong(numMembers));
// Destroy the type
result = hdf5_h.H5Idestroy_type(myType);
assertTrue("H5Idestroy_type should succeed", isSuccess(result));
// Verify type no longer exists
exists = hdf5_h.H5Itype_exists(myType);
assertEquals("User type should not exist after destroy", 0, exists);
}
}
@Test
public void testH5Iregister_and_operations()
{
try (Arena arena = Arena.ofConfined()) {
// Track whether free function was called
MemorySegment freeCalled = allocateIntArray(arena, 1);
freeCalled.set(ValueLayout.JAVA_INT, 0, 0);
// Define free function callback that tracks calls
H5I_free_t.Function freeFunc = (MemorySegment obj, MemorySegment request) ->
{
freeCalled.set(ValueLayout.JAVA_INT, 0, 1);
return 0; // Success
};
MemorySegment freeFuncPtr = H5I_free_t.allocate(freeFunc, arena);
// Register user-defined type
int myType = hdf5_h.H5Iregister_type2(0, freeFuncPtr);
assertTrue("H5Iregister_type2 should succeed", myType >= hdf5_h.H5I_NTYPES());
// Create a test object (just a simple integer in memory)
MemorySegment testObj = allocateIntArray(arena, 1);
testObj.set(ValueLayout.JAVA_INT, 0, 42);
// Register the object with the user type
long objId = hdf5_h.H5Iregister(myType, testObj);
assertTrue("H5Iregister should succeed", isValidId(objId));
// Verify ID is valid
int valid = hdf5_h.H5Iis_valid(objId);
assertTrue("Object ID should be valid", valid > 0);
// Verify ID type matches
int idType = hdf5_h.H5Iget_type(objId);
assertEquals("ID type should match registered type", myType, idType);
// Check member count (should be 1 now)
MemorySegment numMembers = allocateLongArray(arena, 1);
int result = hdf5_h.H5Inmembers(myType, numMembers);
assertTrue("H5Inmembers should succeed", isSuccess(result));
assertEquals("Should have 1 member", 1L, getLong(numMembers));
// Increment reference count
int refCount = hdf5_h.H5Iinc_ref(objId);
assertEquals("Ref count should be 2", 2, refCount);
// Decrement reference count
refCount = hdf5_h.H5Idec_ref(objId);
assertEquals("Ref count should be 1", 1, refCount);
// Clear type (should call free function)
result = hdf5_h.H5Iclear_type(myType, false);
assertTrue("H5Iclear_type should succeed", isSuccess(result));
// Verify free function was called
assertEquals("Free function should have been called", 1, getInt(freeCalled));
// Verify member count is now 0
result = hdf5_h.H5Inmembers(myType, numMembers);
assertTrue("H5Inmembers should succeed", isSuccess(result));
assertEquals("Should have 0 members after clear", 0L, getLong(numMembers));
// Destroy type
result = hdf5_h.H5Idestroy_type(myType);
assertTrue("H5Idestroy_type should succeed", isSuccess(result));
}
}
@Test
public void testH5Iiterate_user_type()
{
try (Arena arena = Arena.ofConfined()) {
// Free function
H5I_free_t.Function freeFunc = (MemorySegment obj, MemorySegment request) -> 0;
MemorySegment freeFuncPtr = H5I_free_t.allocate(freeFunc, arena);
// Register user type
int myType = hdf5_h.H5Iregister_type2(0, freeFuncPtr);
assertTrue("H5Iregister_type2 should succeed", myType >= hdf5_h.H5I_NTYPES());
// Register 3 objects
MemorySegment obj1 = allocateIntArray(arena, 1);
MemorySegment obj2 = allocateIntArray(arena, 1);
MemorySegment obj3 = allocateIntArray(arena, 1);
obj1.set(ValueLayout.JAVA_INT, 0, 10);
obj2.set(ValueLayout.JAVA_INT, 0, 20);
obj3.set(ValueLayout.JAVA_INT, 0, 30);
long id1 = hdf5_h.H5Iregister(myType, obj1);
long id2 = hdf5_h.H5Iregister(myType, obj2);
long id3 = hdf5_h.H5Iregister(myType, obj3);
assertTrue("All IDs should be valid", isValidId(id1) && isValidId(id2) && isValidId(id3));
// Iterate and count IDs
MemorySegment counter = allocateIntArray(arena, 1);
counter.set(ValueLayout.JAVA_INT, 0, 0);
H5I_iterate_func_t.Function callback = (long id, MemorySegment udata) ->
{
int current = udata.get(ValueLayout.JAVA_INT, 0);
udata.set(ValueLayout.JAVA_INT, 0, current + 1);
return 0; // Continue
};
MemorySegment callbackPtr = H5I_iterate_func_t.allocate(callback, arena);
int result = hdf5_h.H5Iiterate(myType, callbackPtr, counter);
assertTrue("H5Iiterate should succeed", isSuccess(result));
// Should have iterated over all 3 objects
assertEquals("Should iterate over 3 IDs", 3, getInt(counter));
// Cleanup - clear type first to free all IDs
result = hdf5_h.H5Iclear_type(myType, false);
assertTrue("H5Iclear_type should succeed", isSuccess(result));
result = hdf5_h.H5Idestroy_type(myType);
assertTrue("H5Idestroy_type should succeed", isSuccess(result));
}
}
@Test
public void testH5Itype_ref_counting()
{
try (Arena arena = Arena.ofConfined()) {
// Free function
H5I_free_t.Function freeFunc = (MemorySegment obj, MemorySegment request) -> 0;
MemorySegment freeFuncPtr = H5I_free_t.allocate(freeFunc, arena);
// Register user type
int myType = hdf5_h.H5Iregister_type2(0, freeFuncPtr);
assertTrue("H5Iregister_type2 should succeed", myType >= hdf5_h.H5I_NTYPES());
// Get initial type ref count
int initialRef = hdf5_h.H5Iget_type_ref(myType);
assertTrue("Initial ref count should be positive", initialRef > 0);
// Increment type ref count
int newRef = hdf5_h.H5Iinc_type_ref(myType);
assertEquals("Ref count should increase by 1", initialRef + 1, newRef);
// Verify with get
int currentRef = hdf5_h.H5Iget_type_ref(myType);
assertEquals("Ref count should match", newRef, currentRef);
// Decrement type ref count
newRef = hdf5_h.H5Idec_type_ref(myType);
assertEquals("Ref count should decrease by 1", initialRef, newRef);
// Verify type still exists
int exists = hdf5_h.H5Itype_exists(myType);
assertTrue("Type should still exist", exists > 0);
// Final cleanup
int result = hdf5_h.H5Idestroy_type(myType);
assertTrue("H5Idestroy_type should succeed", isSuccess(result));
}
}
@Test
public void testH5Iget_type_ref()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create a user type (library types like H5I_FILE cannot be used with this API)
int user_type = hdf5_h.H5Iregister_type2(0, MemorySegment.NULL);
assertTrue("Register type failed", isValidId(user_type));
// Get reference count for the user type
int ref_count = hdf5_h.H5Iget_type_ref(user_type);
assertTrue("Type ref count should be >= 0", ref_count >= 0);
// Cleanup
hdf5_h.H5Idestroy_type(user_type);
}
}
@Test
public void testH5Iinc_type_ref()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create a user type (library types like H5I_DATATYPE cannot be used with this API)
int user_type = hdf5_h.H5Iregister_type2(0, MemorySegment.NULL);
assertTrue("Register type failed", isValidId(user_type));
// Get initial ref count
int initial_ref = hdf5_h.H5Iget_type_ref(user_type);
assertTrue("Initial ref should be >= 0", initial_ref >= 0);
// Increment type ref count
int new_ref = hdf5_h.H5Iinc_type_ref(user_type);
assertEquals("Ref should increment", initial_ref + 1, new_ref);
// Decrement back
int dec_ref = hdf5_h.H5Idec_type_ref(user_type);
assertEquals("Ref should decrement", initial_ref, dec_ref);
// Cleanup
hdf5_h.H5Idestroy_type(user_type);
}
}
@Test
public void testH5Inmembers()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create a user type (library types like H5I_FILE cannot be used with this API)
int user_type = hdf5_h.H5Iregister_type2(0, MemorySegment.NULL);
assertTrue("Register type failed", isValidId(user_type));
// Get number of members of the user type
MemorySegment num_members = allocateLongArray(arena, 1);
int result = hdf5_h.H5Inmembers(user_type, num_members);
assertTrue("H5Inmembers should succeed", isSuccess(result));
long count = getLong(num_members);
assertTrue("Member count should be >= 0", count >= 0);
// Cleanup
hdf5_h.H5Idestroy_type(user_type);
}
}
@Test
public void testH5Iclear_type()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Register a user-defined type
int user_type = hdf5_h.H5Iregister_type2(0, MemorySegment.NULL);
assertTrue("Register type failed", isValidId(user_type));
// Clear type (remove all objects of this type)
int result = hdf5_h.H5Iclear_type(user_type, false);
assertTrue("H5Iclear_type should succeed", isSuccess(result));
// Destroy type
result = hdf5_h.H5Idestroy_type(user_type);
assertTrue("Destroy type should succeed", isSuccess(result));
}
}
@Test
public void testH5Itype_exists()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create a user type to test existence
int user_type = hdf5_h.H5Iregister_type2(0, MemorySegment.NULL);
assertTrue("Register type failed", isValidId(user_type));
// Check if the user type exists
int exists = hdf5_h.H5Itype_exists(user_type);
assertTrue("User type should exist", exists > 0);
// Destroy the type
int result = hdf5_h.H5Idestroy_type(user_type);
assertTrue("Destroy type should succeed", isSuccess(result));
// After destruction, type should not exist
exists = hdf5_h.H5Itype_exists(user_type);
assertTrue("Destroyed type should not exist", exists == 0);
}
}
}