tests/testthat/test_c_errors.R

# ==============================================================================
# scclust for R -- R wrapper for the scclust library
# https://github.com/fsavje/scclust-R
#
# Copyright (C) 2016-2017  Fredrik Savje -- http://fredriksavje.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/
# ==============================================================================

library(scclust)
context("Input checking in C code")


# ==============================================================================
# hierarchical.c
# ==============================================================================

c_hierarchical_clustering <- function(distances = distances::distances(matrix(as.numeric(1:16), ncol = 2)),
                                      size_constraint = 2L,
                                      batch_assign = FALSE,
                                      existing_clustering = NULL) {
  .Call(Rscc_hierarchical_clustering,
        distances,
        size_constraint,
        batch_assign,
        existing_clustering)
}

temp_existing_clustering1 <- 1:6
attr(temp_existing_clustering1, "cluster_count") <- 2L
temp_existing_clustering2 <- 1:8
attr(temp_existing_clustering2, "cluster_count") <- 0L

test_that("`Rscc_hierarchical_clustering` checks input.", {
  expect_silent(c_hierarchical_clustering())
  expect_error(c_hierarchical_clustering(distances = as.numeric(1:16)),
               regexp = "`R_distances` is not a valid distance object.")
  expect_error(c_hierarchical_clustering(distances = matrix(1:16, ncol = 8)),
               regexp = "`R_distances` is not a valid distance object.")
  expect_error(c_hierarchical_clustering(size_constraint = 2.5),
               regexp = "`R_size_constraint` must be integer.")
  expect_error(c_hierarchical_clustering(batch_assign = 1),
               regexp = "`R_batch_assign` must be logical.")
  expect_error(c_hierarchical_clustering(existing_clustering = letters[1:8]),
               regexp = "`R_existing_clustering` is not a valid clustering object.")
  expect_error(c_hierarchical_clustering(existing_clustering = 1:8),
               regexp = "`R_existing_clustering` is not a valid clustering object.")
  expect_error(c_hierarchical_clustering(existing_clustering = temp_existing_clustering1),
               regexp = "`R_existing_clustering` does not match `R_distances`.")
  expect_error(c_hierarchical_clustering(existing_clustering = temp_existing_clustering2),
               regexp = "`R_existing_clustering` is empty.")
})


# ==============================================================================
# sc_clustering.c
# ==============================================================================

c_sc_clustering <- function(distances = distances::distances(matrix(as.numeric(1:16), ncol = 2)),
                            size_constraint = 2L,
                            type_labels = c(1L, 1L, 2L, 1L, 1L, 2L, 2L, 2L),
                            type_constraints = c("0" = 0L, "1" = 1L, "2" = 1L),
                            seed_method = "exclusion_updating",
                            primary_data_points = NULL,
                            primary_unassigned_method = "closest_seed",
                            secondary_unassigned_method = "ignore",
                            seed_radius = NULL,
                            primary_radius = NULL,
                            secondary_radius = NULL,
                            batch_size = NULL) {
  .Call(Rscc_sc_clustering,
        distances,
        size_constraint,
        type_labels,
        type_constraints,
        seed_method,
        primary_data_points,
        primary_unassigned_method,
        secondary_unassigned_method,
        seed_radius,
        primary_radius,
        secondary_radius,
        batch_size)
}

test_that("`Rscc_sc_clustering` checks input.", {
  expect_silent(c_sc_clustering())
  expect_error(c_sc_clustering(distances = as.numeric(1:16)),
               regexp = "`R_distances` is not a valid distance object.")
  expect_error(c_sc_clustering(distances = matrix(1:16, ncol = 8)),
               regexp = "`R_distances` is not a valid distance object.")
  expect_error(c_sc_clustering(size_constraint = 2.5),
               regexp = "`R_size_constraint` must be integer.")
  expect_error(c_sc_clustering(type_labels = letters[1:8]),
               regexp = "`R_type_labels` must be factor, integer or NULL.")
  expect_error(c_sc_clustering(type_labels = NULL),
               regexp = "`R_type_constraints` must be NULL when no types are supplied.")
  expect_error(c_sc_clustering(type_labels = c(1L, 1L, 2L, 1L, 1L, 2L)),
               regexp = "`R_type_labels` does not match `R_distances`.")
  expect_error(c_sc_clustering(type_constraints = c("0", "1", "2")),
               regexp = "`R_type_constraints` must be integer.")
  expect_error(c_sc_clustering(type_constraints = c("0" = 0L, "1" = -1L, "2" = 1L)),
               regexp = "Negative type size constraint.")
  expect_error(c_sc_clustering(seed_method = 1L),
               regexp = "`R_seed_method` must be string.")
  expect_error(c_sc_clustering(seed_method = "invalid"),
               regexp = "Not a valid seed method.")
  expect_error(c_sc_clustering(primary_data_points = letters[1:8]),
               regexp = "`R_primary_data_points` must be NULL or integer.")
  expect_error(c_sc_clustering(primary_unassigned_method = 1L),
               regexp = "`R_primary_unassigned_method` must be string.")
  expect_error(c_sc_clustering(primary_unassigned_method = "invalid"),
               regexp = "Not a valid unassigned method.")
  expect_error(c_sc_clustering(secondary_unassigned_method = 1L),
               regexp = "`R_secondary_unassigned_method` must be string.")
  expect_error(c_sc_clustering(secondary_unassigned_method = "invalid"),
               regexp = "Not a valid unassigned method.")
  expect_error(c_sc_clustering(seed_radius = "a"),
               regexp = "`R_seed_radius` must be NULL or double.")
  expect_error(c_sc_clustering(primary_radius = FALSE),
               regexp = "`R_primary_radius` must be NULL, string or double.")
  expect_error(c_sc_clustering(primary_radius = "invalid"),
               regexp = "Not a valid radius method.")
  expect_error(c_sc_clustering(secondary_radius = FALSE),
               regexp = "`R_secondary_radius` must be NULL, string or double.")
  expect_error(c_sc_clustering(secondary_radius = "invalid"),
               regexp = "Not a valid radius method.")
  expect_error(c_sc_clustering(batch_size = "invalid"),
               regexp = "`R_batch_size` must be NULL or integer.")
})


# ==============================================================================
# utilities.c
# ==============================================================================

temp_clustering1 <- c(1L, 1L, 0L, 1L, 1L, 0L, 0L, 0L)
attr(temp_clustering1, "cluster_count") <- 2L
temp_clustering2 <- c(1L, 1L, 0L, 1L, 1L, 0L, 0L, 0L)
attr(temp_clustering2, "cluster_count") <- 0L

c_check_clustering <- function(clustering = temp_clustering1,
                               size_constraint = 2L,
                               type_labels = c(1L, 2L, 1L, 1L, 2L, 1L, 2L, 2L),
                               type_constraints = c("0" = 0L, "1" = 1L, "2" = 1L),
                               primary_data_points = NULL) {
  .Call(Rscc_check_clustering,
        clustering,
        size_constraint,
        unclass(type_labels),
        type_constraints,
        primary_data_points)
}

test_that("`Rscc_check_clustering` checks input.", {
  expect_silent(c_check_clustering())
  expect_error(c_check_clustering(clustering = letters[1:8]),
               regexp = "`R_clustering` is not a valid clustering object.")
  expect_error(c_check_clustering(clustering = 1:8),
               regexp = "`R_clustering` is not a valid clustering object.")
  expect_error(c_check_clustering(clustering = temp_clustering2),
               regexp = "`R_clustering` is empty.")
  expect_error(c_check_clustering(size_constraint = 2.5),
               regexp = "`R_size_constraint` must be integer.")
  expect_error(c_check_clustering(type_labels = letters[1:8]),
               regexp = "`R_type_labels` must be factor, integer or NULL.")
  expect_error(c_check_clustering(type_labels = NULL),
               regexp = "`R_type_constraints` must be NULL when no types are supplied.")
  expect_error(c_check_clustering(type_labels = c(1L, 1L, 2L, 1L, 1L, 2L)),
               regexp = "`R_type_labels` does not match `R_clustering`.")
  expect_error(c_check_clustering(type_constraints = c("0", "1", "2")),
               regexp = "`R_type_constraints` must be integer.")
  expect_error(c_check_clustering(type_constraints = c("0" = 0L, "1" = -1L, "2" = 1L)),
               regexp = "Negative type size constraint.")
  expect_error(c_check_clustering(primary_data_points = "a"),
               regexp = "`R_primary_data_points` must be NULL or integer.")
})


c_get_clustering_stats <- function(distances = distances::distances(matrix(as.numeric(1:16), ncol = 2)),
                                   clustering = temp_clustering1) {
  .Call(Rscc_get_clustering_stats,
        distances,
        clustering)
}

test_that("`Rscc_get_clustering_stats` checks input.", {
  expect_silent(c_get_clustering_stats())
  expect_error(c_get_clustering_stats(clustering = letters[1:8]),
               regexp = "`R_clustering` is not a valid clustering object.")
  expect_error(c_get_clustering_stats(clustering = 1:8),
               regexp = "`R_clustering` is not a valid clustering object.")
  expect_error(c_get_clustering_stats(clustering = temp_clustering2),
               regexp = "`R_clustering` is empty.")
  expect_error(c_get_clustering_stats(distances = as.numeric(1:16)),
               regexp = "`R_distances` is not a valid distance object.")
  expect_error(c_get_clustering_stats(distances = matrix(1:16, ncol = 8)),
               regexp = "`R_distances` is not a valid distance object.")
  expect_error(c_get_clustering_stats(distances = distances::distances(matrix(as.numeric(1:14), ncol = 7))),
               regexp = "`R_distances` does not match `R_clustering`.")
})


# ==============================================================================
# Check scclust error
# ==============================================================================

test_that("scclust returns errors correctly.", {
  expect_silent(c_hierarchical_clustering())
  expect_error(c_hierarchical_clustering(size_constraint = 1L),
               regexp = "[(]scclust:src/hierarchical_clustering.c")
  expect_error(sc_clustering(distances::distances(matrix(c(0.1, 0.2, 0.3), ncol = 1)), size_constraint = 2L, seed_radius = 0.001),
               "Infeasible radius constraint.")
})
fsavje/Rscclust documentation built on Jan. 5, 2024, 2:31 a.m.