# First, make sure to load the necessary packages library(testthat) # Load or source the code containing the NeuroHyperVec and NeuroSpace classes # Assume the classes are defined in 'neuroimaging_classes.R' # source('neuroimaging_classes.R') # Start writing the test suite context("NeuroHyperVec Class Tests") test_that("NeuroHyperVec constructor works correctly with valid inputs", { # Define dimensions spatial_dims <- c(10L, 10L, 10L) num_trials <- 5L num_features <- 3L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(2.0, 2.0, 2.0), origin = c(-10.0, -10.0, -10.0) ) # Create a mask with 20% of voxels active set.seed(123) mask_data <- array(runif(prod(spatial_dims)) < 0.2, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate random data for active voxels num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Check that the object is created correctly expect_s4_class(hvec, "NeuroHyperVec") expect_equal(dim(hvec@data), c(num_features, num_trials, num_voxels)) expect_equal(hvec@space, space) expect_equal(hvec@mask, mask) expect_length(hvec@lookup_map, prod(spatial_dims)) expect_equal(sum(hvec@lookup_map > 0), num_voxels) }) test_that("NeuroHyperVec constructor throws errors with invalid inputs", { # Define dimensions spatial_dims <- c(10L, 10L, 10L) num_trials <- 5L num_features <- 3L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(2.0, 2.0, 2.0), origin = c(-10.0, -10.0, -10.0) ) # Create a mask mask_data <- array(TRUE, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data with incorrect dimensions data_array <- array(rnorm(100), dim = c(2, 2, 2)) # Wrong dimensions # Attempt to create NeuroHyperVec object with incorrect data dimensions expect_error( NeuroHyperVec(data = data_array, space = space, mask = mask), "Data array dimensions \\[2 x 2 x 2\\] do not match expected \\[3 x 5 x 1000\\]" ) # Attempt to create NeuroHyperVec object with invalid mask invalid_mask <- array(TRUE, dim = c(5, 5)) # Wrong dimensions expect_error( NeuroHyperVec(data = data_array, space = space, mask = invalid_mask), "'mask' must be a 3D logical array" ) }) test_that("series method retrieves correct data", { # Define dimensions spatial_dims <- c(5L, 5L, 5L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask with all voxels active mask_data <- array(TRUE, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate sequential data for testing num_voxels <- sum(mask_data) data_array <- array(1:(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Test series method at a specific voxel i <- 2L; j <- 2L; k <- 2L result <- series(hvec, i, j, k) # Expected data voxel_index <- ((k - 1) * spatial_dims[1] * spatial_dims[2]) + ((j - 1) * spatial_dims[1]) + i lookup_index <- hvec@lookup_map[voxel_index] expected_data <- hvec@data[,,lookup_index] expect_equal(result, expected_data) }) test_that("series method returns zeros for voxels outside mask", { # Define dimensions spatial_dims <- c(5L, 5L, 5L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask with some voxels inactive mask_data <- array(FALSE, dim = spatial_dims) mask_data[2,2,2] <- TRUE mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Test series method at an inactive voxel i <- 3L; j <- 3L; k <- 3L result <- series(hvec, i, j, k) # Expected data is zeros expected_data <- array(0, dim = c(num_features, num_trials)) expect_equal(result, expected_data) }) test_that("linear_access method retrieves correct data", { # Define dimensions spatial_dims <- c(4L, 4L, 4L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask with all voxels active mask_data <- array(TRUE, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(1:(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Total number of elements total_elements <- prod(c(spatial_dims, num_trials, num_features)) # Test linear_access at random indices set.seed(456) indices <- sample(1:total_elements, 10) result <- linear_access(hvec, indices) # Expected data # Map indices to (feature_idx, trial_idx, spatial_idx) tmp <- indices - 1 spatial_nels <- prod(spatial_dims) spatial_idx <- (tmp %% spatial_nels) + 1 tmp <- tmp %/% spatial_nels trial_idx <- (tmp %% num_trials) + 1 feature_idx <- (tmp %/% num_trials) + 1 lookup_indices <- hvec@lookup_map[spatial_idx] expected_data <- numeric(length(indices)) expected_data <- hvec@data[cbind(feature_idx, trial_idx, lookup_indices)] expect_equal(result, expected_data) }) test_that("linear_access returns zeros for indices outside mask", { # Define dimensions spatial_dims <- c(4L, 4L, 4L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask with some voxels inactive mask_data <- array(FALSE, dim = spatial_dims) mask_data[1,1,1] <- TRUE mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Indices corresponding to inactive voxels total_elements <- prod(c(spatial_dims, num_trials, num_features)) indices <- c(1, total_elements) # First and last indices # Test linear_access result <- linear_access(hvec, indices) # Expected data is zero for the last index (inactive voxel) expect_true(result[1] != 0) expect_equal(result[2], 0) }) test_that("Extracting subsets with [ works correctly", { # Define dimensions spatial_dims <- c(5L, 5L, 5L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask with all voxels active mask_data <- array(TRUE, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(1:(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Extract a subset subset_data <- hvec[1:2, 1:2, 1:2, 1, 1, drop = TRUE] # Expected data dimensions expect_equal(dim(subset_data), c(2, 2, 2)) }) test_that("show method works without errors", { # Define dimensions spatial_dims <- c(10L, 10L, 10L) num_trials <- 3L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(2.0, 2.0, 2.0), origin = c(-10.0, -10.0, -10.0) ) # Create a mask with 50% of voxels active set.seed(789) mask_data <- array(runif(prod(spatial_dims)) < 0.5, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Capture output of show method expect_output(show(hvec)) }) test_that("NeuroSpace is used correctly within NeuroHyperVec", { # Define dimensions spatial_dims <- c(8L, 8L, 8L) num_trials <- 4L num_features <- 2L # Create a NeuroSpace object with extra dimensions space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.5, 1.5, 1.5), origin = c(-12.0, -12.0, -12.0) ) # Check that spacing and origin have length 3 expect_equal(length(space@spacing), 3) expect_equal(length(space@origin), 3) # Create a mask mask_data <- array(runif(prod(spatial_dims)) < 0.3, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Check that NeuroSpace properties are consistent expect_equal(dim(hvec@space), c(spatial_dims, num_trials, num_features)) expect_equal(hvec@space@spacing, c(1.5, 1.5, 1.5)) expect_equal(hvec@space@origin, c(-12.0, -12.0, -12.0)) }) test_that("Error handling for invalid indices in series method", { # Define dimensions spatial_dims <- c(5L, 5L, 5L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask mask_data <- array(TRUE, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Attempt to access invalid indices expect_error( series(hvec, 6, 1, 1), "Indices out of bounds" ) expect_error( series(hvec, 1, 6, 1), "Indices out of bounds" ) expect_error( series(hvec, 1, 1, 6), "Indices out of bounds" ) }) test_that("Error handling for invalid indices in linear_access method", { # Define dimensions spatial_dims <- c(4L, 4L, 4L) num_trials <- 2L num_features <- 2L # Create a NeuroSpace object space <- NeuroSpace( dim = c(spatial_dims, num_trials, num_features), spacing = c(1.0, 1.0, 1.0), origin = c(0.0, 0.0, 0.0) ) # Create a mask mask_data <- array(TRUE, dim = spatial_dims) mask <- LogicalNeuroVol(mask_data, NeuroSpace(spatial_dims)) # Generate data num_voxels <- sum(mask_data) data_array <- array(rnorm(num_features * num_trials * num_voxels), dim = c(num_features, num_trials, num_voxels)) # Create NeuroHyperVec object hvec <- NeuroHyperVec(data = data_array, space = space, mask = mask) # Total number of elements total_elements <- prod(c(spatial_dims, num_trials, num_features)) # Attempt to access invalid indices expect_error( linear_access(hvec, c(0, total_elements + 1)), "indices must be within range of data dimensions" ) })