diff --git a/.github/workflows/rhub.yaml b/.github/workflows/rhub.yaml deleted file mode 100644 index 74ec7b0..0000000 --- a/.github/workflows/rhub.yaml +++ /dev/null @@ -1,95 +0,0 @@ -# R-hub's generic GitHub Actions workflow file. It's canonical location is at -# https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml -# You can update this file to a newer version using the rhub2 package: -# -# rhub::rhub_setup() -# -# It is unlikely that you need to modify this file manually. - -name: R-hub -run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" - -on: - workflow_dispatch: - inputs: - config: - description: 'A comma separated list of R-hub platforms to use.' - type: string - default: 'linux,windows,macos' - name: - description: 'Run name. You can leave this empty now.' - type: string - id: - description: 'Unique ID. You can leave this empty now.' - type: string - -jobs: - - setup: - runs-on: ubuntu-latest - outputs: - containers: ${{ steps.rhub-setup.outputs.containers }} - platforms: ${{ steps.rhub-setup.outputs.platforms }} - - steps: - # NO NEED TO CHECKOUT HERE - - uses: r-hub/actions/setup@v1 - with: - config: ${{ github.event.inputs.config }} - id: rhub-setup - - linux-containers: - needs: setup - if: ${{ needs.setup.outputs.containers != '[]' }} - runs-on: ubuntu-latest - name: ${{ matrix.config.label }} - strategy: - fail-fast: false - matrix: - config: ${{ fromJson(needs.setup.outputs.containers) }} - container: - image: ${{ matrix.config.container }} - - steps: - - uses: r-hub/actions/checkout@v1 - - uses: r-hub/actions/platform-info@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - uses: r-hub/actions/setup-deps@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - uses: r-hub/actions/run-check@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - other-platforms: - needs: setup - if: ${{ needs.setup.outputs.platforms != '[]' }} - runs-on: ${{ matrix.config.os }} - name: ${{ matrix.config.label }} - strategy: - fail-fast: false - matrix: - config: ${{ fromJson(needs.setup.outputs.platforms) }} - - steps: - - uses: r-hub/actions/checkout@v1 - - uses: r-hub/actions/setup-r@v1 - with: - job-config: ${{ matrix.config.job-config }} - token: ${{ secrets.RHUB_TOKEN }} - - uses: r-hub/actions/platform-info@v1 - with: - token: ${{ secrets.RHUB_TOKEN }} - job-config: ${{ matrix.config.job-config }} - - uses: r-hub/actions/setup-deps@v1 - with: - job-config: ${{ matrix.config.job-config }} - token: ${{ secrets.RHUB_TOKEN }} - - uses: r-hub/actions/run-check@v1 - with: - job-config: ${{ matrix.config.job-config }} - token: ${{ secrets.RHUB_TOKEN }} diff --git a/.github/workflows/windows-check.yml b/.github/workflows/windows-check.yml deleted file mode 100644 index e7643d8..0000000 --- a/.github/workflows/windows-check.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Windows Check - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - workflow_dispatch: - -jobs: - windows-check: - runs-on: windows-latest - strategy: - matrix: - r-version: ['4.3', '4.4'] - - steps: - - uses: actions/checkout@v3 - - - name: Set up R ${{ matrix.r-version }} - uses: r-lib/actions/setup-r@v2 - with: - r-version: ${{ matrix.r-version }} - - - name: Install Rtools - uses: r-lib/actions/setup-rtools@v2 - if: runner.os == 'Windows' - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - shell: Rscript {0} - - - name: Cache R packages - uses: actions/cache@v3 - with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-r-${{ matrix.r-version }}-${{ hashFiles('.github/depends.Rds') }} - restore-keys: ${{ runner.os }}-r-${{ matrix.r-version }}- - - - name: Install dependencies - run: | - remotes::install_deps(dependencies = TRUE) - shell: Rscript {0} - - - name: Check package - run: | - rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") - shell: Rscript {0} - - - name: Test installation - run: | - install.packages(".", repos = NULL, type = "source") - library(MicrobiomeStat) - shell: Rscript {0} - - - name: Upload check results - if: failure() - uses: actions/upload-artifact@v3 - with: - name: ${{ runner.os }}-r${{ matrix.r-version }}-results - path: check diff --git a/R/generate_taxa_cladogram_single.R b/R/generate_taxa_cladogram_single.R index 24da34c..f4ef0cc 100644 --- a/R/generate_taxa_cladogram_single.R +++ b/R/generate_taxa_cladogram_single.R @@ -207,8 +207,23 @@ generate_taxa_cladogram_single <- function( fix_link_frame <- link_frame fix_link_frame[[min_label]] <- stringr::str_replace_all(fix_link_frame[[min_label]], pattern = " |\\(|\\)", replacement = "_") fix_link_frame[[min_label]] <- stringr::str_replace_all(fix_link_frame[[min_label]], pattern = "\\.", replacement = "") - # Build phylogenetic tree - treex <- build_tree(fix_link_frame, level_seq) + + # Check and use existing tree if available, otherwise build from taxonomy + if (!is.null(data.obj$tree)) { + tree <- data.obj$tree + # Ensure tree tips match our feature names + common_tips <- intersect(tree$tip.label, fix_link_frame[[min_label]]) + if (length(common_tips) > 0) { + message("Using phylogenetic tree from data object.") + treex <- ape::keep.tip(tree, common_tips) + } else { + message("No matching tips found between phylogenetic tree and features. Using taxonomy-based tree instead.") + treex <- build_tree(fix_link_frame, level_seq) + } + } else { + message("Building taxonomy-based tree.") + treex <- build_tree(fix_link_frame, level_seq) + } # Join and process test results inputframe_linked <- join_frames(test.list, level_seq, link_frame) diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf new file mode 100644 index 0000000..7fb70be Binary files /dev/null and b/tests/testthat/Rplots.pdf differ diff --git a/tests/testthat/test-generate_taxa_cladogram_single.R b/tests/testthat/test-generate_taxa_cladogram_single.R new file mode 100644 index 0000000..7e087b5 --- /dev/null +++ b/tests/testthat/test-generate_taxa_cladogram_single.R @@ -0,0 +1,123 @@ +test_that("generate_taxa_cladogram_single works with and without phylogenetic tree", { + # Create mock data objects with more realistic taxonomic structure + features <- paste0("ASV", 1:5) # 使用简单的ASV标识符 + + # Create feature annotation data frame - 避免使用可能冲突的列名 + feature.ann <- data.frame( + tax_Kingdom = rep("Bacteria", 5), + tax_Phylum = c("Firmicutes", "Firmicutes", "Bacteroidetes", "Bacteroidetes", "Proteobacteria"), + tax_Class = c("Bacilli", "Clostridia", "Bacteroidia", "Flavobacteriia", "Gammaproteobacteria"), + row.names = features, + stringsAsFactors = FALSE + ) + + # Create abundance matrix + feature.tab <- matrix( + rpois(50, lambda = 10), + nrow = 5, + ncol = 10, + dimnames = list(features, paste0("Sample", 1:10)) + ) + + # Create metadata + meta.dat <- data.frame( + Group = rep(c("A", "B"), each = 5), + row.names = paste0("Sample", 1:10) + ) + + # Create base data object + data.obj.no.tree <- list( + feature.tab = feature.tab, + feature.ann = feature.ann, + meta.dat = meta.dat + ) + + # Create mock test results + test.list <- list( + tax_Phylum = list( + "A_vs_B" = data.frame( + Feature = unique(feature.ann$tax_Phylum), + Coefficient = rnorm(3), + P.Value = runif(3), + Adjusted.P.Value = runif(3) + ) + ), + tax_Class = list( + "A_vs_B" = data.frame( + Feature = unique(feature.ann$tax_Class), + Coefficient = rnorm(5), + P.Value = runif(5), + Adjusted.P.Value = runif(5) + ) + ) + ) + + # Create data object with tree - 确保树的标签与特征完全匹配 + library(ape) + tree <- ape::read.tree(text = paste0("(", paste(features, collapse = ","), ");")) + tree$edge.length <- rep(1, nrow(tree$edge)) + # 添加必要的属性 + tree$node.label <- paste0("Node", 1:(tree$Nnode)) + + data.obj.with.tree <- data.obj.no.tree + data.obj.with.tree$tree <- tree + + # Test cases + suppressWarnings({ + # Test case 1: Without tree + result_no_tree <- capture_messages({ + plot.list.no.tree <- generate_taxa_cladogram_single( + data.obj = data.obj.no.tree, + test.list = test.list, + group.var = "Group", + feature.level = c("tax_Phylum", "tax_Class"), + color.group.level = "tax_Phylum" + ) + }) + expect_true(any(grepl("Building taxonomy-based tree", result_no_tree))) + + # Test case 2: With tree + result_with_tree <- capture_messages({ + plot.list.with.tree <- generate_taxa_cladogram_single( + data.obj = data.obj.with.tree, + test.list = test.list, + group.var = "Group", + feature.level = c("tax_Phylum", "tax_Class"), + color.group.level = "tax_Phylum" + ) + }) + cat("\nActual messages with tree:\n") + print(result_with_tree) + + # Test case 3: With mismatched tree + mismatched_tree <- ape::read.tree(text = "(Wrong1,Wrong2,Wrong3,Wrong4,Wrong5);") + mismatched_tree$edge.length <- rep(1, nrow(mismatched_tree$edge)) + data.obj.mismatched <- data.obj.no.tree + data.obj.mismatched$tree <- mismatched_tree + + result_mismatched <- capture_messages({ + plot.list.mismatched <- generate_taxa_cladogram_single( + data.obj = data.obj.mismatched, + test.list = test.list, + group.var = "Group", + feature.level = c("tax_Phylum", "tax_Class"), + color.group.level = "tax_Phylum" + ) + }) + }) + + # Basic checks + expect_type(plot.list.no.tree, "list") + expect_type(plot.list.with.tree, "list") + expect_type(plot.list.mismatched, "list") + + # Check list contents + expect_true(all(sapply(plot.list.no.tree, function(x) inherits(x, "ggplot")))) + expect_true(all(sapply(plot.list.with.tree, function(x) inherits(x, "ggplot")))) + expect_true(all(sapply(plot.list.mismatched, function(x) inherits(x, "ggplot")))) + + # Check list length + expect_length(plot.list.no.tree, 1) + expect_length(plot.list.with.tree, 1) + expect_length(plot.list.mismatched, 1) +})