mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
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.
487 lines
18 KiB
Java
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);
|
|
}
|
|
}
|
|
}
|