tests/testthat/test_seurat.R

#' Returns a random counts matrix.
get_random_counts <- function(ncells, nfeatures) {
  # Instantiate an empty nfeatures by ncells matrix.
  counts <- matrix(NA, ncol = ncells, nrow = nfeatures)
  
  # Populate the matrix one row at a time by generating a negative 
  # binomial distribution using a random `mu` between 0.1 and 10.
  for (i in 1:nfeatures) {
    mu <- runif(1, min = 0.1, max = 10)
    counts[i, ] <- values <- rnbinom(ncells, mu = mu, size = 1)
  }

  # Assign column and row names to the matrix to label cells and genes.
  colnames(counts) <- paste0("cell", seq(ncol(counts)))
  row.names(counts) <- paste0("gene", seq(nrow(counts)))

  # Convert `counts` to a `dgCMatrix`.
  counts_sparse <- as.sparse(counts)

  return(counts_sparse)
}

context("merge")

test_that("`merge` works with multi-assay inputs", {
  # Run checks against `Assay` and `Assay5` instances.
  for (assay_version in c("v5")) {
    create_assay <- switch(assay_version,
      v3 = CreateAssayObject,
      v5 = CreateAssay5Object,
      stop("`assay_version` should be one of 'v3', 'v5'")
    )
    
    # Populate a `Seurat` instance with two assays, "LEFT" and "RIGHT".
    counts_LEFT_A <- get_random_counts(ncells = 10, nfeatures = 10)
    input_A <- CreateSeuratObject(
      create_assay(counts_LEFT_A), 
      assay = "LEFT", 
      project = "A"
    )
    counts_RIGHT_A <- get_random_counts(ncells = 10, nfeatures = 10)
    input_A[["RIGHT"]] <- create_assay(counts_RIGHT_A)
    
    # Populate a second `Seurat` instance with a single assay, "LEFT".
    counts_LEFT_B <- get_random_counts(ncells = 5, nfeatures = 5)
    input_B <- CreateSeuratObject(
      create_assay(counts_LEFT_B), 
      assay = "LEFT", 
      project = "B"
    )
    
    # Populate a third `Seurat` instance with two assays, "LEFT" and "RIGHT".
    counts_LEFT_C <- get_random_counts(ncells = 5, nfeatures = 5)
    input_C <- CreateSeuratObject(
      create_assay(counts_LEFT_C), 
      assay = "LEFT", 
      project = "C"
    )
    counts_RIGHT_C <- get_random_counts(ncells = 5, nfeatures = 5)
    input_C[["RIGHT"]] <- create_assay(counts_RIGHT_C)

    # Merge the three `Seurat` instances.
    result <- expect_warning(
      merge(input_A, c(input_B, input_C)),
      paste(
        "Some cell names are duplicated across objects provided.",
        "Renaming to enforce unique cell names."
      )
    )

    # If we have an `Assay` input, split it into multiple layers
    if (assay_version == "v3") {
      result <- expect_warning(split(result, f = Idents(result)))
    }
    
    # Check that the result layers are named according to their project values.
    expected_layers <- c("counts.A", "counts.B", "counts.C")
    expect_identical(Layers(result[["LEFT"]]), expected_layers)
    expected_layers <- c("counts.A", "counts.C")
    expect_identical(Layers(result[["RIGHT"]]), expected_layers)
    # Check that the "LEFT" assay contains the expected cell names.
    expected_cells <- c(
      paste0("cell", seq(10), "_1"),
      paste0("cell", seq(5), "_2"),
      paste0("cell", seq(5), "_3")
    )
    expect_identical(Cells(result[["LEFT"]]), expected_cells)
    # Check that the "RIGHT" assay contains the expected cell names.
    expected_cells <- c(
      paste0("cell", seq(10), "_1"),
      paste0("cell", seq(5), "_3")
    )
    expect_identical(Cells(result[["RIGHT"]]), expected_cells)
    # Check that the values for "counts.A" in the "LEFT" assay are preserved.
    expected_counts <- counts_LEFT_A
    colnames(expected_counts) <- paste0("cell", seq(10), "_1")
    expect_identical(
      LayerData(result, assay = "LEFT", layer = "counts.A"),
      expected_counts
    )
    # Check that the values for "counts.B" in the "LEFT" assay are preserved.
    expected_counts <- counts_LEFT_B
    colnames(expected_counts) <- paste0("cell", seq(5), "_2")
    observed_counts <- LayerData(result, assay = "LEFT", layer = "counts.B")
    expect_identical(
      observed_counts,
      expected_counts,
      info = dim(expected_counts)
    )
    # Check that the values for "counts.C" in the "LEFT" assay are preserved.
    expected_counts <- counts_LEFT_C
    colnames(expected_counts) <- paste0("cell", seq(5), "_3")
    expect_identical(
      LayerData(result, assay = "LEFT", layer = "counts.C"),
      expected_counts,
      info = assay_version
    )
    # Check that the values for "counts.A" in the "RIGHT" assay are preserved.
    expected_counts <- counts_RIGHT_A
    colnames(expected_counts) <- paste0("cell", seq(10), "_1")
    expect_identical(
      LayerData(result, assay = "RIGHT", layer = "counts.A"),
      expected_counts
    )
    # Check that the values for "counts.C" in the "RIGHT" assay are preserved.
    expected_counts <- counts_RIGHT_C
    colnames(expected_counts) <- paste0("cell", seq(5), "_3")
    expect_identical(
      LayerData(result, assay = "RIGHT", layer = "counts.C"),
      expected_counts,
      info = assay_version
    )
  }
})

context("RenameCells")

test_that("`RenameCells` works with multi-assay inputs", {
  counts <- get_random_counts(ncells = 10, nfeatures = 10)

  # Run checks against `Assay` and `Assay5` instances.
  for (assay_version in c("v3", "v5")) {
    create_assay <- switch(assay_version,
      v3 = CreateAssayObject,
      v5 = CreateAssay5Object,
      stop("`assay_version` should be one of 'v3', 'v5'")
    )

    counts_left <- counts[, 1:5]
    assay_left <- create_assay(counts_left)
    left <- CreateSeuratObject(assay_left)

    counts_right <- counts[, 6:10]
    assay_right <- create_assay(counts_right)
    right <- CreateSeuratObject(assay_right, assay = "right")

    test_case <- merge(left, right)
    old_names <- colnames(test_case)

    # Rename all the cells in the input using `add.cell.id`.
    new_names <- paste0("new_cell", 1:10)
    result <- RenameCells(test_case, add.cell.id = "new")
    expect_identical(colnames(result), new_names)

    # Rename all the cells in the input using `new.names`.
    new_names <- paste0("new-cell", 1:10)
    result <- RenameCells(test_case, new.names = new_names)
    expect_identical(colnames(result), new_names)

    # Rename just the cells in the input's default assay.
    new_names <- paste0("new-cell", 1:5)
    expected_names <- c(new_names, tail(old_names, n = 5))
    result <- RenameCells(test_case, new.names = new_names)
    expect_identical(colnames(result), expected_names)

    # Rename a subset of cells across both assays.
    new_names <- paste0("new-cell", 2:9)
    expected_names <- c(head(old_names, n = 1), new_names, tail(old_names, n = 1))
    # Requires a named vector to be passed to `new.names`.
    expect_error(RenameCells(test_case, new.names = new_names))
    names(new_names) <- old_names[2:9]
    result <- RenameCells(test_case, new.names = new_names)
    expect_identical(colnames(result), expected_names)

    # All cells provided in a named vector mapping must exist.
    new_names <- paste0("new-cell", 1:10)
    names(new_names) <- c("not-a-cell", tail(old_names, n = 1))
    expect_error(RenameCells(test_case, new.names = new_names))
  }
})
mojaveazure/seurat-object documentation built on April 5, 2025, 3:13 a.m.