├── NAMESPACE ├── tests ├── testthat.R └── testthat │ └── test-repo-metrics.R ├── images ├── Rplot_mem.jpeg ├── Rplot_time.jpeg ├── Rplot_branchmem.jpeg └── Rplot_time_updated.png ├── exec ├── rss.sh └── get_mem.R ├── .travis.yml ├── .gitignore ├── Rperform.Rproj ├── .Rbuildignore ├── man ├── get_sha.Rd ├── get_msg.Rd ├── get_branch.Rd ├── get_datetime.Rd ├── Rperform-package.Rd ├── list_commits.Rd ├── time_branch.Rd ├── time_commit.Rd ├── compare_brancht.Rd ├── mem_compare.Rd ├── plot_PR.Rd ├── time_compare.Rd ├── get_mem.Rd ├── mem_commit.Rd ├── compare_branchm.Rd ├── plot_PR_webpage.Rd ├── plot_webpage.Rd ├── compare_PR.Rd ├── plot_directory.Rd ├── compare_dir.Rd ├── plot_metrics.Rd └── plot_branchmetrics.Rd ├── SampleFiles_TravisPR ├── sample_travis.yml ├── sample_tempRperform.R └── sample_push_gh_pages.sh ├── DESCRIPTION ├── .github └── workflows │ └── Rperform-CI-test.yaml ├── R ├── mem_operations.R ├── git_help.R ├── branch_metrics.R ├── travisPR_metrics.R ├── repo_metrics.R └── plot_metrics.R ├── README.md ├── README.Rmd └── LICENSE.md /NAMESPACE: -------------------------------------------------------------------------------- 1 | exportPattern("^[[:alpha:]]+") 2 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(Rperform) 3 | 4 | test_check("Rperform") 5 | -------------------------------------------------------------------------------- /images/Rplot_mem.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticalmonk/Rperform/HEAD/images/Rplot_mem.jpeg -------------------------------------------------------------------------------- /images/Rplot_time.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticalmonk/Rperform/HEAD/images/Rplot_time.jpeg -------------------------------------------------------------------------------- /images/Rplot_branchmem.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticalmonk/Rperform/HEAD/images/Rplot_branchmem.jpeg -------------------------------------------------------------------------------- /images/Rplot_time_updated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analyticalmonk/Rperform/HEAD/images/Rplot_time_updated.png -------------------------------------------------------------------------------- /exec/rss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | file=$1 3 | pid=$2 4 | rm -f $file $file.DONE 5 | while [ ! -f "$file.DONE" ] ; do 6 | ps h -p $pid -o rss >> $file 7 | done 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | cache: packages 3 | sudo: false 4 | warnings_are_errors: false 5 | r_github_packages: 6 | - hadley/plyr 7 | - tdhock/animint 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | *.o 6 | *.so 7 | *.dll 8 | inst/doc 9 | .DS_Store 10 | .github/actions/.DS_Store 11 | .github/.DS_Store 12 | -------------------------------------------------------------------------------- /exec/get_mem.R: -------------------------------------------------------------------------------- 1 | argv <- commandArgs(trailingOnly = TRUE) 2 | test_path <- argv[1] 3 | commit_num <- as.numeric(argv[2]) 4 | suppressMessages(mem_result <- Rperform::get_mem(test_path = test_path, 5 | commit_num = commit_num)) 6 | save(mem_result, file="mem_result.RData") -------------------------------------------------------------------------------- /Rperform.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ # Automatically added by RStudio, 2 | ^\.Rproj\.user$ # used for temporary files. 3 | ^README\.Rmd$ # An Rmarkdown file used to generate README.md 4 | ^cran-comments\.md$ # Comments for CRAN submission 5 | ^NEWS\.md$ # A news file written in Markdown 6 | ^/\.gitattributes$ 7 | ^README-.*\.png$ 8 | ^images$ 9 | SampleFiles_TravisPR 10 | ^\.github/ 11 | ^\.travis\.yml$ 12 | -------------------------------------------------------------------------------- /man/get_sha.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/git_help.R 3 | \name{get_sha} 4 | \alias{get_sha} 5 | \title{SHA1 value of a git commit object.} 6 | \usage{ 7 | get_sha(commit_val) 8 | } 9 | \arguments{ 10 | \item{commit_val}{git commit object, as returned by git2r::commits()} 11 | } 12 | \description{ 13 | \code{get_sha(commit_val = )} returns the SHA1 value for the git commit object provided 14 | as the parameter. 15 | } 16 | \seealso{ 17 | \code{\link[git2r]{commits}} 18 | } 19 | 20 | -------------------------------------------------------------------------------- /man/get_msg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/git_help.R 3 | \name{get_msg} 4 | \alias{get_msg} 5 | \title{Message summary of a git commit object.} 6 | \usage{ 7 | get_msg(commit_val) 8 | } 9 | \arguments{ 10 | \item{commit_val}{git commit object, as returned by git2r::commits()} 11 | } 12 | \description{ 13 | \code{get_sha(commit_val = )} returns the summary of the message for the git 14 | commit object provided as the parameter. 15 | } 16 | \seealso{ 17 | \code{\link[git2r]{commits}} 18 | } 19 | 20 | -------------------------------------------------------------------------------- /man/get_branch.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/git_help.R 3 | \name{get_branch} 4 | \alias{get_branch} 5 | \title{Current branch name of a git repository.} 6 | \usage{ 7 | get_branch(dir_path = "./") 8 | } 9 | \arguments{ 10 | \item{dir_path}{Path of the git repository.} 11 | } 12 | \description{ 13 | \code{get_branch} returns the current branch name for the git repository passed in as 14 | parameter (default being the current repository). 15 | } 16 | \seealso{ 17 | \code{\link[git2r]{repository}} 18 | } 19 | 20 | -------------------------------------------------------------------------------- /man/get_datetime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/git_help.R 3 | \name{get_datetime} 4 | \alias{get_datetime} 5 | \title{DateTime value of a git commit object.} 6 | \usage{ 7 | get_datetime(commit_val) 8 | } 9 | \arguments{ 10 | \item{commit_val}{git commit object, as returned by git2r::commits()} 11 | } 12 | \description{ 13 | \code{get_sha(commit_val = )} returns the date-time value for the git commit 14 | object provided as the parameter. 15 | } 16 | \seealso{ 17 | \code{\link[git2r]{commits}} 18 | } 19 | 20 | -------------------------------------------------------------------------------- /SampleFiles_TravisPR/sample_travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | 3 | r_packages: 4 | - microbenchmark 5 | - roxygen2 6 | - rmarkdown 7 | 8 | r_github_packages: 9 | - analyticalmonk/Rperform 10 | 11 | env: 12 | global: 13 | - secure: "ENTER_YOUR_ENCRYPTED_GITHUB_ACCESS_TOKEN_HERE" 14 | - USER_EMAIL="INSERT_EMAIL_ID_HERE" 15 | - USER_NAME="INSERT_USERNAME_HERE" 16 | - PR_COMMAND="Rperform::plot_PR_webpage('./INSERT_PATH_TO_TEST_DIR/INSERT_TEST_NAME_HERE', metric = 'time')" 17 | - RPERFORM_COMMAND="Rperform::plot_webpage(test_directory = './INSERT_PATH_TO_TEST_DIR/', metric = 'time')" 18 | 19 | before_script: 20 | - travis_wait 30 source `Rscript -e "cat(find.package(\"Rperform\"))"`/push_gh_pages.sh 21 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: Rperform 2 | Type: Package 3 | Title: Rperform - Performance testing for R packages 4 | Version: 0.0.0.9000 5 | License: GPL-3 6 | Date: 2015-05-29 7 | Author: Akash Tandon (analyticalmonk on GitHub), Toby Dylan Hocking 8 | Maintainer: Akash Tandon 9 | Description: Rperform helps R package developers keep track of the quantitative 10 | metrics of their packages over various development versions. 11 | BugReports: https://github.com/analyticalmonk/Rperform/issues 12 | Imports: 13 | git2r (>= 0.30.1), 14 | ggplot2 (>= 1.0.1), 15 | testthat (>= 0.10.0), 16 | devtools (>= 1.8.0), 17 | microbenchmark (>= 1.4-2) 18 | Suggests: 19 | animint2, 20 | rmarkdown, 21 | knitr 22 | VignetteBuilder: knitr 23 | RoxygenNote: 5.0.1 24 | -------------------------------------------------------------------------------- /.github/workflows/Rperform-CI-test.yaml: -------------------------------------------------------------------------------- 1 | name: Check-RPerform 2 | 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | jobs: 10 | build_rperform: 11 | name: Build Rperform 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: r-lib/actions/setup-r@v2 17 | - uses: r-lib/actions/setup-r-dependencies@v2 18 | with: 19 | extra-packages: any::rcmdcheck 20 | needs: check 21 | 22 | - uses: r-lib/actions/check-r-package@v2 23 | 24 | announce_completion: 25 | name: Show Success Message 26 | needs: 27 | - build_rperform 28 | runs-on: ubuntu-latest 29 | 30 | steps: 31 | - name: "Test Completion" 32 | run: echo "All GitHub CI tests have passed" 33 | -------------------------------------------------------------------------------- /tests/testthat/test-repo-metrics.R: -------------------------------------------------------------------------------- 1 | library(Rperform) 2 | library(testthat) 3 | 4 | context("Check if repo metric functionalities work properly") 5 | 6 | if(!dir.exists(paths = "./stringr")){ 7 | git2r::clone(url = "https://github.com/EngineerDanny/stringr", local_path = "./stringr") 8 | } 9 | setwd("./stringr") 10 | 11 | test_that("Wrong parameter type results in error", { 12 | expect_error(time_compare(test_path = "inst/tests/test-dup.r", num_commits = "5")) 13 | expect_error(mem_compare(test_path = "inst/tests/test-dup.r", num_commits = "5")) 14 | }) 15 | 16 | # test_that("Data frames returned by metric functions are of the correct dimensions",{ 17 | # expect_equal((ncol(time_compare(test_path = "tests/testthat/test-count.r", 2))), 6) 18 | # expect_equal(ncol(mem_compare(test_path = "tests/testthat/test-count.r", 2)), 6) 19 | # 20 | # }) 21 | setwd("./../") 22 | unlink(x = "./stringr", recursive = T, force = T) 23 | -------------------------------------------------------------------------------- /man/Rperform-package.Rd: -------------------------------------------------------------------------------- 1 | \name{Rperform-package} 2 | \alias{Rperform-package} 3 | \alias{Rperform} 4 | \docType{package} 5 | \title{ 6 | \packageTitle{Rperform} 7 | } 8 | \description{ 9 | \packageDescription{Rperform} 10 | } 11 | \details{ 12 | 13 | The DESCRIPTION file: 14 | \packageDESCRIPTION{Rperform} 15 | \packageIndices{Rperform} 16 | ~~ An overview of how to use the package, including the most important functions ~~ 17 | } 18 | \author{ 19 | \packageAuthor{Rperform} 20 | 21 | Maintainer: \packageMaintainer{Rperform} 22 | } 23 | \references{ 24 | ~~ Literature or other references for background information ~~ 25 | } 26 | \keyword{ package } 27 | \seealso{ 28 | ~~ Optional links to other man pages, e.g. ~~ 29 | ~~ \code{\link[https://github.com/ropensci/git2r]{git2r}} ~~ 30 | } 31 | \examples{ 32 | 33 | \dontrun{ 34 | # Set the current directory to the git repository concerned. 35 | setwd("./Path/to/repository") 36 | 37 | # Set the file-path 38 | t_path <- "Path/to/file" 39 | 40 | # Load the library 41 | library(Rperform) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /man/list_commits.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repo_metrics.R 3 | \name{list_commits} 4 | \alias{list_commits} 5 | \title{Commits' details.} 6 | \usage{ 7 | list_commits(path = "./", num_commits = 20) 8 | } 9 | \arguments{ 10 | \item{path}{File-path to the git repository whose commits are to be summarized.} 11 | 12 | \item{num_commits}{Number of commits to be summarized. The default is 20.} 13 | } 14 | \description{ 15 | Given a repository path and number of commits (n), returns a data frame containing 16 | the date, SHA1 values and commit messages of the last n commits in the repo. 17 | } 18 | \examples{ 19 | 20 | \dontrun{ 21 | ## Example-1 22 | 23 | # Set the current directory to the git repository concerned. 24 | setwd("./Path/to/repository") 25 | 26 | # Obtained details of the last 10 commits in the repository 27 | list_commits(num_commits = 10) 28 | 29 | ## Example-2 30 | 31 | # Obtained the details of the last 20 (default value) commits in the repository 32 | # specified by path. 33 | list_commits(path) 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /man/time_branch.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/branch_metrics.R 3 | \name{time_branch} 4 | \alias{time_branch} 5 | \title{Run-times of a file on the given branch.} 6 | \usage{ 7 | time_branch(test_path, branch = "master", num_commits = 5) 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file to be tested.} 11 | 12 | \item{branch}{Branch against whose commits the test file is to be 13 | tested (with master being the default).} 14 | 15 | \item{num_commits}{Number of commits on the branch against which the test 16 | file is to be tested.} 17 | } 18 | \description{ 19 | Given a test-file and branch, returns the run-time details of the file over 20 | the given number of commits on the branch. 21 | } 22 | \section{Value}{ 23 | 24 | time_branch returns an object of class "data.frame". 25 | The data-frame consists of the following columns: 26 | \code{test_name} 27 | \code{metric_name} 28 | \code{status} 29 | \code{metric_val} 30 | \code{message} 31 | \code{date_time} 32 | \code{branch} 33 | } 34 | 35 | \section{Warning}{ 36 | 37 | Function assumes the current directory to be the root directory of the 38 | package being tested. 39 | } 40 | \examples{ 41 | 42 | \dontrun{ 43 | # Set the current directory to the git repository concerned. 44 | setwd("./Path/to/repository") 45 | 46 | # Set the file-path 47 | t_path <- "Path/to/file" 48 | 49 | # Load the library and pass the parameters to the function 50 | library(Rperform) 51 | time_branch(test_path = t_path, branch_name = "helper", num_commits = 10) 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /man/time_commit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repo_metrics.R 3 | \name{time_commit} 4 | \alias{time_commit} 5 | \title{Test file's run-time.} 6 | \usage{ 7 | time_commit(test_path, test_commit) 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file whose run-time is to be checked.} 11 | 12 | \item{test_commit}{git2r commit \code{object} corresponding to which the 13 | run-time is to be checked.} 14 | } 15 | \description{ 16 | Given a test-file's path, measures its run-time against the commit specified by the 17 | commit \code{object} passed as a parameter. 18 | } 19 | \section{Value}{ 20 | 21 | time_commit returns an object of class "data.frame". 22 | The data-frame consists of the following columns: 23 | \code{test_name} 24 | \code{metric_name} 25 | \code{status} 26 | \code{metric_val} 27 | \code{message} 28 | \code{sha} 29 | \code{date_time} 30 | } 31 | 32 | \section{Warning}{ 33 | 34 | Function assumes the current directory to be the root directory of the 35 | package being tested. 36 | } 37 | \examples{ 38 | \dontrun{ 39 | ## Example-1 40 | 41 | # Set the current directory to the git repository concerned. 42 | setwd("./Path/to/repository") 43 | 44 | # Obtain the commit object 45 | commit_list <- git2r::commits() 46 | t_commit <- commit_list[[1]] 47 | 48 | # Specify the test-file path 49 | t_path <- "Path/to/file" 50 | 51 | # Pass the parameters and obtain the run-time details 52 | library(Rperform) 53 | time_commit(t_path, t_commit) 54 | } 55 | 56 | } 57 | \seealso{ 58 | \code{\link[git2r]{commits}} 59 | } 60 | 61 | -------------------------------------------------------------------------------- /man/compare_brancht.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/branch_metrics.R 3 | \name{compare_brancht} 4 | \alias{compare_brancht} 5 | \title{Run-time details across branches.} 6 | \usage{ 7 | compare_brancht(test_path, branch1, branch2 = "master") 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file to be tested.} 11 | 12 | \item{branch1}{Branch against whose commits the test file is to be 13 | tested.} 14 | 15 | \item{branch2}{Branch into which branch1 is supposedly to be merged.} 16 | } 17 | \description{ 18 | Given a test-file and two branches, returns the run-time details of the file 19 | against the first commit till the latest common commit in branch1, and 20 | against the latest commit in branch2. 21 | } 22 | \section{Value}{ 23 | 24 | compare_brancht returns an object of class "data.frame". 25 | The data-frame consists of the following columns: 26 | \code{test_name} 27 | \code{metric_name} 28 | \code{status} 29 | \code{metric_val} 30 | \code{message} 31 | \code{date_time} 32 | \code{branch} 33 | } 34 | 35 | \section{Warning}{ 36 | 37 | Function assumes the current directory to be the root directory of the 38 | package being tested. 39 | } 40 | \examples{ 41 | 42 | \dontrun{ 43 | # Set the current directory to the git repository concerned. 44 | setwd("./Path/to/repository") 45 | 46 | # Set the file-path 47 | t_path <- "Path/to/file" 48 | 49 | # Load the library and pass the parameters to the function 50 | library(Rperform) 51 | compare_brancht(test_path = t_path, branch1 = "helper", branch2 = "master") 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /man/mem_compare.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repo_metrics.R 3 | \name{mem_compare} 4 | \alias{mem_compare} 5 | \title{Test-file's memory statistics for multiple commits.} 6 | \usage{ 7 | mem_compare(test_path, num_commits = 10) 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file which is to be checked.} 11 | 12 | \item{num_commits}{number of commits against all of which the memory stats 13 | are to be checked starting from the most recent one.} 14 | } 15 | \description{ 16 | Given a test-file's path, checks its memory metrics against the number of 17 | commits specified by the parameter num_commits with default being 10. Memory 18 | metrics returned are the memory leaked and maximum meory utilized during its 19 | execution. 20 | } 21 | \section{Value}{ 22 | 23 | time_commit returns an object of class "data.frame". 24 | The data-frame consists of the following columns: 25 | \code{test_name} 26 | \code{metric_name} 27 | \code{status} 28 | \code{metric_val} 29 | \code{message} 30 | \code{sha} 31 | \code{date_time} 32 | } 33 | 34 | \section{Warning}{ 35 | 36 | Function assumes the current directory to be the root directory of the 37 | package being tested. 38 | } 39 | \examples{ 40 | 41 | \dontrun{ 42 | ## Example-1 43 | 44 | # Set the current directory to the git repository concerned. 45 | setwd("./Path/to/repository") 46 | 47 | # Specify the test-file path 48 | t_path <- "Path/to/file" 49 | 50 | # Pass the parameters and obtain the run-time details 51 | library(Rperform) 52 | mem_compare(t_path, 10) 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /man/plot_PR.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/travisPR_metrics.R 3 | \name{plot_PR} 4 | \alias{plot_PR} 5 | \title{Visualize PR's impact on performance without having to merge.} 6 | \usage{ 7 | plot_PR(test_path, metric = "time") 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path, relative to the git repo, for the test file to 11 | be tested.} 12 | 13 | \item{metric}{The metric (runtime or memory) for which the file is to be 14 | tested.} 15 | } 16 | \description{ 17 | The function must be called from a directory containing only a single git 18 | repository checked out to the branch which is meant to be tested against the 19 | master branch of the repository's remote repo. This function is designed keeping 20 | in mind the PR testing methodlogy of Travis-CI. 21 | Given a test-file path and the required metric, it plots the metric details 22 | of the file against the first commit till the latest common commit for the 23 | git repo, and against the latest commit for master branch of the repo's 24 | remote. 25 | } 26 | \section{Value}{ 27 | None 28 | } 29 | 30 | \section{Warning}{ 31 | 32 | Function assumes the current directory to be the parent directory of the 33 | the repository being tested. 34 | } 35 | \examples{ 36 | 37 | \dontrun{ 38 | # Set the current directory to the parent directory of the concerned repository. 39 | setwd("./Path/to/parent/directory") 40 | 41 | # Set the file-path 42 | t_path <- "Path/to/file" 43 | 44 | # Load the library and pass the parameters to the function 45 | library(Rperform) 46 | plot_PR(t_path, metric = "time") 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /man/time_compare.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repo_metrics.R 3 | \name{time_compare} 4 | \alias{time_compare} 5 | \title{Run-time across versions.} 6 | \usage{ 7 | time_compare(test_path, num_commits = 10) 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path of the test-file which is to be used for run-time 11 | comparisons.} 12 | 13 | \item{num_commits}{Number of commits (versions) against which the file is to 14 | be tested, with default being 10.} 15 | } 16 | \description{ 17 | Given a test-file path, checks its run-time against the specified number of commits 18 | in the current git repository and returns a data-frame comprised of the test name, 19 | status of test run, time (if successful) and SHA1 value corresponding to the commit 20 | the value is for. 21 | } 22 | \section{Value}{ 23 | 24 | time_compare returns an object of class "data.frame". 25 | The data-frame consists of the following columns: 26 | \code{test_name} 27 | \code{metric_name} 28 | \code{status} 29 | \code{metric_val} 30 | \code{message} 31 | \code{sha} 32 | \code{date_time} 33 | } 34 | 35 | \section{Warning}{ 36 | 37 | Function assumes the current directory to be the root directory of the 38 | package being tested. 39 | } 40 | \examples{ 41 | 42 | \dontrun{ 43 | ## Example-1 44 | 45 | # Set the current directory to the git repository concerned. 46 | setwd("./Path/to/repository") 47 | 48 | # Specify the test-file path 49 | t_path <- "Path/to/file" 50 | 51 | # Pass the parameters and obtain the run-time details against 10 commits 52 | library(Rperform) 53 | time_compare(test_path = t_path, num_commits = 10) 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /man/get_mem.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repo_metrics.R 3 | \name{get_mem} 4 | \alias{get_mem} 5 | \title{Test-file's memory statistics.} 6 | \usage{ 7 | get_mem(test_path, commit_num = 1) 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file which is to be checked.} 11 | 12 | \item{commit_num}{commit number in the git log for the current git repository 13 | against which the memory stats are to be checked, with the commit number 14 | for the most recent commit being 1.} 15 | } 16 | \description{ 17 | Given a test-file's path, checks its memory metrics against the commit 18 | specified by the commit number passed as a parameter. Memory metrics returned 19 | are the memory leaked and maximum memory utilized during its execution. A 20 | commit number,n, would correspond to the nth commit in the commit log of the 21 | current git repository. 22 | } 23 | \section{Value}{ 24 | 25 | get_mem returns an object of class "data.frame". 26 | The data-frame consists of the following columns: 27 | \code{test_name} 28 | \code{metric_name} 29 | \code{status} 30 | \code{metric_val} 31 | \code{message} 32 | \code{sha} 33 | \code{date_time} 34 | } 35 | 36 | \section{Warning}{ 37 | 38 | Function assumes the current directory to be the root directory of the 39 | package being tested. 40 | } 41 | \examples{ 42 | 43 | \dontrun{ 44 | ## Example-1 45 | 46 | # Set the current directory to the git repository concerned. 47 | setwd("./Path/to/repository") 48 | 49 | # Specify the test-file path 50 | t_path <- "Path/to/file" 51 | 52 | # Pass the parameters and obtain the memory stats 53 | library(Rperform) 54 | get_mem(t_path, 3) 55 | } 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /man/mem_commit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repo_metrics.R 3 | \name{mem_commit} 4 | \alias{mem_commit} 5 | \title{Test-file's memory statistics.} 6 | \usage{ 7 | mem_commit(test_path, test_commit) 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file which is to be checked.} 11 | 12 | \item{test_commit}{git2r commit \code{object} corresponding to which the 13 | memory stats are to be checked.} 14 | } 15 | \description{ 16 | Given a test-file's path, checks its memory metrics against the commit 17 | specified by the commit \code{object} passed as a parameter. Memory 18 | metrics returned are the memory leaked and maximum memory utilized during 19 | its execution. 20 | } 21 | \section{Value}{ 22 | 23 | mem_commit returns an object of class "data.frame". 24 | The data-frame consists of the following columns: 25 | \code{test_name} 26 | \code{metric_name} 27 | \code{status} 28 | \code{metric_val} 29 | \code{message} 30 | \code{sha} 31 | \code{date_time} 32 | } 33 | 34 | \section{Warning}{ 35 | 36 | Function assumes the current directory to be the root directory of the 37 | package being tested. 38 | } 39 | \examples{ 40 | 41 | \dontrun{ 42 | ## Example-1 43 | 44 | # Set the current directory to the git repository concerned. 45 | setwd("./Path/to/repository") 46 | 47 | # Obtain the commit object 48 | commit_list <- git2r::commits() 49 | t_commit <- commit_list[[1]] 50 | 51 | # Specify the test-file path 52 | t_path <- "Path/to/file" 53 | 54 | # Pass the parameters and obtain the memory stats 55 | library(Rperform) 56 | mem_commit(t_path, t_commit) 57 | } 58 | 59 | } 60 | \seealso{ 61 | \code{\link[git2r]{commits}} 62 | } 63 | 64 | -------------------------------------------------------------------------------- /man/compare_branchm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/branch_metrics.R 3 | \name{compare_branchm} 4 | \alias{compare_branchm} 5 | \title{Memory metrics across branches.} 6 | \usage{ 7 | compare_branchm(test_path, branch1, branch2 = "master") 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path for the test file to be tested.} 11 | 12 | \item{branch1}{Branch against whose commits the test file is to be 13 | tested.} 14 | 15 | \item{branch2}{Branch into which branch1 is supposedly to be merged.} 16 | } 17 | \description{ 18 | Given a test-file and two branches, returns the memory metrics of the file 19 | against the first commit till the latest common commit in branch1, and 20 | against the latest commit in branch2. Memory metrics returned are the memory 21 | leaked and maximum memory utilized during its execution. 22 | } 23 | \section{Value}{ 24 | 25 | compare_branchm returns an object of class "data.frame". 26 | The data-frame consists of the following columns: 27 | \code{test_name} 28 | \code{metric_name} 29 | \code{status} 30 | \code{metric_val} 31 | \code{message} 32 | \code{date_time} 33 | \code{time_branch} 34 | } 35 | 36 | \section{Warning}{ 37 | 38 | Function assumes the current directory to be the root directory of the 39 | package being tested. 40 | } 41 | \examples{ 42 | 43 | \dontrun{ 44 | # Set the current directory to the git repository concerned. 45 | setwd("./Path/to/repository") 46 | 47 | # Set the file-path 48 | t_path <- "Path/to/file" 49 | 50 | # Load the library and pass the parameters to the function 51 | library(Rperform) 52 | compare_branchm(test_path = t_path, branch1 = "helper", branch2 = "master") 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /man/plot_PR_webpage.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/travisPR_metrics.R 3 | \name{plot_PR_webpage} 4 | \alias{plot_PR_webpage} 5 | \title{Generate a webpage containing a visualization detailing PR's impact on 6 | performance without haveing to merge.} 7 | \usage{ 8 | plot_PR_webpage(test_path, metric = "time") 9 | } 10 | \arguments{ 11 | \item{test_path}{File-path, relative to the git repo, for the test file to 12 | be tested.} 13 | 14 | \item{metric}{The metric (runtime or memory) for which the file is to be 15 | tested.} 16 | } 17 | \description{ 18 | The function must be called from a directory containing only a single git 19 | repository checked out to the branch which is meant to be tested against the 20 | master branch of the repository's remote repo. This function is designed keeping 21 | in mind the PR testing methodlogy of Travis-CI. 22 | Given a test-file path and the required metric, it creates a webpage 23 | visualizing the metric details of the file against the first commit till the 24 | latest common commit for the git repo, and against the latest commit for 25 | master branch of the repo's remote. 26 | } 27 | \section{Value}{ 28 | None 29 | } 30 | 31 | \section{Warning}{ 32 | 33 | Function assumes the current directory to be the parent directory of the 34 | the repository being tested. 35 | } 36 | \examples{ 37 | 38 | \dontrun{ 39 | # Set the current directory to the parent directory of the concerned repository. 40 | setwd("./Path/to/parent/directory") 41 | 42 | # Set the file-path 43 | t_path <- "Path/to/file" 44 | 45 | # Load the library and pass the parameters to the function 46 | library(Rperform) 47 | plot_PR_webpage(t_path, metric = "time") 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /man/plot_webpage.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_metrics.R 3 | \name{plot_webpage} 4 | \alias{plot_webpage} 5 | \title{Plot the specified metrics of all test files in a specified directory on a 6 | webpage.} 7 | \usage{ 8 | plot_webpage(test_directory = "tests/testthat", metric = "testMetrics", 9 | output_name = "RperformTest.html") 10 | } 11 | \arguments{ 12 | \item{test_directory}{Path of the directory containing the test files.} 13 | 14 | \item{metric}{Type of plot(s) desired. This can be set to \code{time}, 15 | \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 16 | for more details)} 17 | 18 | \item{output_name}{Name of the output .html file.} 19 | } 20 | \description{ 21 | It plots specified metrics for all the tests present in the specified 22 | directory of the current git repository on a webpage. 23 | } 24 | \section{WARNING}{ 25 | 26 | Function assumes the current directory to be the root directory of the 27 | repository being tested. 28 | } 29 | \examples{ 30 | 31 | \dontrun{ 32 | # Set to the git repository in consideration. 33 | setwd("path/to/repo") 34 | d_path <- "path/to/tests" 35 | 36 | # Load the library 37 | library(Rperform) 38 | 39 | ## Example-1 40 | 41 | # Pass the parameters and obtain the run-time followed by memory details against 10 commits 42 | # on two seperate webpages (html files). 43 | plot_webpage(test_directory = d_path, metric = "time", output_name = "timePage") 44 | plot_metrics(test_directory = d_path, metric = "memory", output_name = "memPage") 45 | 46 | ## Example-2 47 | 48 | # Obtain both memory and time metrics for each individual testthat block 49 | # inside a file and the file itself. 50 | plot_webpage(d_path, metric = "testMetrics", output_name = "testMetricsPage") 51 | } 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /man/compare_PR.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/travisPR_metrics.R 3 | \name{compare_PR} 4 | \alias{compare_PR} 5 | \title{Analyze PR's impact on performance without having to merge.} 6 | \usage{ 7 | compare_PR(test_path, metric = "time") 8 | } 9 | \arguments{ 10 | \item{test_path}{File-path, relative to the git repo, for the test file to 11 | be tested.} 12 | 13 | \item{metric}{The metric (runtime or memory) for which the file is to be 14 | tested.} 15 | } 16 | \description{ 17 | The function must be called from a directory containing only a single git 18 | repository checked out to the branch which is meant to be tested against the 19 | master branch of the repository's remote repo. This function is designed keeping 20 | in mind the PR testing methodlogy of Travis-CI. 21 | Given a test-file path and the required metric, it returns the metric details 22 | of the file against the first commit till the latest common commit for the 23 | git repo, and against the latest commit for master branch of the repo's 24 | remote. It also returns information regarding the latest common commit for 25 | the two directories. 26 | } 27 | \section{Value}{ 28 | compare_brancht returns an object of class "list" containing 29 | two members. 30 | 31 | The first member is a data-frame and consists of the following columns: 32 | \code{test_name} 33 | \code{metric_name} 34 | \code{status} 35 | \code{metric_val} 36 | \code{message} 37 | \code{sha} 38 | \code{date_time} 39 | \code{directory} 40 | 41 | The second member is a data-frame and consists of the following columns: 42 | \code{common_datetime} 43 | \code{common_message} 44 | \code{cnum_b1} 45 | \code{cnum_b2} 46 | } 47 | 48 | \section{Warning}{ 49 | 50 | Function assumes the current directory to be the parent directory of the 51 | the repository being tested. 52 | } 53 | \examples{ 54 | 55 | \dontrun{ 56 | # Set the current directory to the parent directory of the concerned repository. 57 | setwd("./Path/to/parent/directory") 58 | 59 | # Set the file-path 60 | t_path <- "Path/to/file" 61 | 62 | # Load the library and pass the parameters to the function 63 | library(Rperform) 64 | compare_PR(t_path, metric = "time") 65 | } 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /R/mem_operations.R: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | # ---------------------------------------------------------------------------- 3 | 4 | .rss.profile.start <- function(rss.file){ 5 | stopifnot(is.character(rss.file)) 6 | stopifnot(length(rss.file) == 1) 7 | sh.file <- system.file("exec", "rss.sh", package = "Rperform") 8 | cmd <- paste("bash", sh.file, rss.file, Sys.getpid()) 9 | gc(reset = T) 10 | system(cmd, wait = FALSE) 11 | ## while({ 12 | ## rss.size <- file.info(rss.file)$size 13 | ## is.na(rss.size) || rss.size == 0 14 | ## }){ 15 | ## ## wait for the system(cmd) to start writing to rss.file. 16 | ## cat("Waiting for rss.sh to start writing to ", rss.file, "\n") 17 | ## } 18 | Sys.sleep(0.5) 19 | } 20 | 21 | .rss.profile.stop <- function(rss.file){ 22 | stopifnot(is.character(rss.file)) 23 | stopifnot(length(rss.file) == 1) 24 | DONE.file <- paste0(rss.file, ".DONE") 25 | gc() 26 | Sys.sleep(0.5) 27 | cat("", file = DONE.file) 28 | kilobytes <- scan(rss.file, what = integer(), quiet = TRUE) 29 | list(max_mem = max(kilobytes) - kilobytes[1], 30 | leak = kilobytes[length(kilobytes)]-kilobytes[1]) 31 | } 32 | 33 | # ---------------------------------------------------------------------------- 34 | # ---------------------------------------------------------------------------- 35 | # Script for obtaining memory usage using "exec/get_mem.R" 36 | 37 | # script.R <- system.file("exec", "get_mem.R", package="Rperform") 38 | # Rscript <- file.path(R.home("bin"), "Rscript") 39 | # cmd <- paste(Rscript, script.R, "./tests/testthat/test-detect.r") 40 | # result.list <- list() 41 | # for(test.i in 1:3){ 42 | # system(cmd) 43 | # load("mem_result.RData") 44 | # result.list[[test.i]] <- mem_result 45 | # } 46 | 47 | # ---------------------------------------------------------------------------- 48 | 49 | ## Use the *nix ps program to get the memory usage of this R process. 50 | # 51 | # .memory.usage <- function(ps.parameter=paste("-p", Sys.getpid())){ 52 | # cmd <- sprintf("ps %s -o pid,cmd,rss", ps.parameter) 53 | # ps.lines <- system(cmd, intern=TRUE) 54 | # stopifnot(length(ps.lines) > 1) 55 | # ps.table <- read.table(text=ps.lines, header=TRUE) 56 | # ps.table$megabytes <- ps.table$RSS/1024 57 | # ps.table 58 | # } 59 | -------------------------------------------------------------------------------- /man/plot_directory.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_metrics.R 3 | \name{plot_directory} 4 | \alias{plot_directory} 5 | \title{Plot metrics across versions for all files in a given directory.} 6 | \usage{ 7 | plot_directory(test_directory, metric = "testMetrics", num_commits = 5, 8 | save_data = FALSE, save_plots = TRUE) 9 | } 10 | \arguments{ 11 | \item{test_directory}{Directory containing the test-files which are to be used.} 12 | 13 | \item{metric}{Type of plot(s) desired. This can be set to \code{time}, 14 | \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 15 | for more details)} 16 | 17 | \item{num_commits}{Number of commits (versions) against which the files are to 18 | be tested, with default being 5.} 19 | 20 | \item{save_data}{If set to TRUE, the metrics data is saved in a folder 'Rperform_Data' 21 | in the current directory.} 22 | 23 | \item{save_plots}{If set to TRUE, the plots generated are stored in the 24 | 'Rperform_plots' directory in the root of the repo rather than being 25 | printed.} 26 | } 27 | \description{ 28 | Given a directory path, plot the memory and time usage statistics of all files 29 | in the directory against the commit message summaries of the specified number 30 | of commits in the current git repository. 31 | } 32 | \section{WARNING}{ 33 | 34 | Library assumes the current directory to be the root directory of the 35 | package being tested. 36 | } 37 | \examples{ 38 | 39 | \dontrun{ 40 | # Set to the git repository in consideration. 41 | setwd("path/to/repo") 42 | d_path <- "path/to/tests" 43 | 44 | # Load the library 45 | library(Rperform) 46 | 47 | ## Example-1 48 | 49 | # Pass the parameters and obtain the run-time followed by memory details against 10 commits. 50 | plot_directory(test_directory = d_path, metric = "time", num_commits = 10, 51 | save_data = F, save_plots = T) 52 | plot_directory(test_directory = d_path, metric = "memory", num_commits = 10, 53 | save_data = F, save_plots = T) 54 | 55 | ## Example-2 56 | 57 | # Obtain both memory and time metrics for each individual testthat block 58 | # inside a file and the file itself ,and save the resulting plot as well as 59 | # data. 60 | plot_directory(d_path, metric = "testMetrics", num_commits = 5, save_data = F, 61 | save_plots = T) 62 | } 63 | 64 | } 65 | 66 | -------------------------------------------------------------------------------- /man/compare_dir.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/travisPR_metrics.R 3 | \name{compare_dir} 4 | \alias{compare_dir} 5 | \title{Compare performance across directories/repositories.} 6 | \usage{ 7 | compare_dir(dir1, dir2, test_path, metric = "time", PR = F) 8 | } 9 | \arguments{ 10 | \item{dir1}{Path to the first directory/repository.} 11 | 12 | \item{dir2}{Path to the second directory/repository.} 13 | 14 | \item{test_path}{File-path, relative to the directories, for the test file to 15 | be tested.} 16 | 17 | \item{metric}{The metric (runtime or memory) for which the file is to be 18 | tested.} 19 | 20 | \item{PR}{When set to True, it performs the analysis in accordance with the setup 21 | on Travis-CI environment for testing Pull Requests.} 22 | } 23 | \description{ 24 | Given a test-file, two directories and the required metric, returns the 25 | metric details of the file against the first commit till the latest common 26 | commit for dir1, and against the latest commit for dir2. It also returns 27 | information regarding the latest common commit for the two directories. 28 | } 29 | \section{Value}{ 30 | compare_brancht returns an object of class "list" containing 31 | two members. 32 | 33 | The first member is a data-frame and consists of the following columns: 34 | \code{test_name} 35 | \code{metric_name} 36 | \code{status} 37 | \code{metric_val} 38 | \code{message} 39 | \code{sha} 40 | \code{date_time} 41 | \code{directory} 42 | 43 | The second member is a data-frame and consists of the following columns: 44 | \code{common_datetime} 45 | \code{common_message} 46 | \code{cnum_b1} 47 | \code{cnum_b2} 48 | } 49 | 50 | \section{Warning}{ 51 | 52 | Function assumes the current directory to be the parent directory of both 53 | the repositories being tested. That means both the repositories should be 54 | inside the same directory. 55 | } 56 | \examples{ 57 | 58 | \dontrun{ 59 | # Set the current directory to the parent directory of the concerned repositories. 60 | setwd("./Path/to/parent/directory") 61 | 62 | # Set the directory paths 63 | d_path1 <- "Path/to/first/directory" 64 | d_path2 <- "Path/to/second/directory" 65 | 66 | # Set the file-path 67 | t_path <- "Path/to/file" 68 | 69 | # Load the library and pass the parameters to the function 70 | library(Rperform) 71 | compare_dir(d_path1, d_path2, t_path, metric = "time") 72 | } 73 | 74 | } 75 | 76 | -------------------------------------------------------------------------------- /R/git_help.R: -------------------------------------------------------------------------------- 1 | #' SHA1 value of a git commit object. 2 | #' 3 | #' \code{get_sha(commit_val = )} returns the SHA1 value for the git commit object provided 4 | #' as the parameter. 5 | #' 6 | #' @param commit_val git commit object, as returned by git2r::commits() 7 | #' 8 | #' @seealso \code{\link[git2r]{commits}} 9 | 10 | # The get_sha function, given a git commit object returns a character vector which is the 11 | # SHA1 value for the given commit. 12 | 13 | get_sha <- function(commit_val) { 14 | stopifnot(git2r::is_commit(commit_val)) 15 | 16 | commit_val$sha 17 | } 18 | 19 | ## ----------------------------------------------------------------------------------------- 20 | #' DateTime value of a git commit object. 21 | #' 22 | #' \code{get_sha(commit_val = )} returns the date-time value for the git commit 23 | #' object provided as the parameter. 24 | #' 25 | #' @param commit_val git commit object, as returned by git2r::commits() 26 | #' 27 | #' @seealso \code{\link[git2r]{commits}} 28 | 29 | get_datetime <- function(commit_val) { 30 | stopifnot(git2r::is_commit(commit_val)) 31 | 32 | as.POSIXct(git2r::when(commit_val$author$when)) 33 | } 34 | 35 | ## ----------------------------------------------------------------------------------------- 36 | 37 | #' Message summary of a git commit object. 38 | #' 39 | #' \code{get_sha(commit_val = )} returns the summary of the message for the git 40 | #' commit object provided as the parameter. 41 | #' 42 | #' @param commit_val git commit object, as returned by git2r::commits() 43 | #' 44 | #' @seealso \code{\link[git2r]{commits}} 45 | 46 | # The get_sha function, given a git commit object returns a character vector which is the 47 | # message's summary for the given commit. 48 | 49 | get_msg <- function(commit_val) { 50 | stopifnot(git2r::is_commit(commit_val)) 51 | 52 | base::substr(commit_val$summary, start = 1, stop = 15) 53 | } 54 | 55 | ## ----------------------------------------------------------------------------------------- 56 | 57 | #' Current branch name of a git repository. 58 | #' 59 | #' \code{get_branch} returns the current branch name for the git repository passed in as 60 | #' parameter (default being the current repository). 61 | #' 62 | #' @param dir_path Path of the git repository. 63 | #' 64 | #' @seealso \code{\link[git2r]{repository}} 65 | 66 | get_branch <- function(dir_path = "./") { 67 | repo <- git2r::repository(dir_path) 68 | head <- git2r::repository_head(repo) 69 | head$name 70 | } 71 | 72 | ## ----------------------------------------------------------------------------------------- 73 | -------------------------------------------------------------------------------- /man/plot_metrics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_metrics.R 3 | \name{plot_metrics} 4 | \alias{plot_metrics} 5 | \title{Plot test-file metrics across versions.} 6 | \usage{ 7 | plot_metrics(test_path, metric, num_commits = 5, save_data = FALSE, 8 | save_plots = FALSE, interactive = FALSE) 9 | } 10 | \arguments{ 11 | \item{test_path}{File-path of the test-file which is to be used for run-time 12 | comparisons.} 13 | 14 | \item{metric}{Type of plot(s) desired. This can be set to \code{time}, 15 | \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 16 | for more details)} 17 | 18 | \item{num_commits}{Number of commits (versions) against which the file is to 19 | be tested, with default being 5.} 20 | 21 | \item{save_data}{If set to TRUE, the data frame containing the metrics 22 | information is stored in the 'Rperform_Data' directory in the root of the 23 | repo. (default set to FALSE)} 24 | 25 | \item{save_plots}{If set to TRUE, the plots generated are stored in the 26 | 'Rperform_plots' directory in the root of the repo rather than being 27 | printed. (default set to TRUE)} 28 | 29 | \item{interactive}{If set to TRUE, the plots generated are interactive. The 30 | resulting plot is rendered in the default browser.} 31 | } 32 | \description{ 33 | Given a test-file path, plot the metrics of entire file and individual 34 | testthat blocks against the commit message summaries of the specified number 35 | of commits in the current git repository. If the parameter save_data is set 36 | to true, it also stores the corresponding data-frames in an RData file in a 37 | folder 'Rperform_Data' in the current directory.The metrics plotted are in 38 | accordance with those specified using the parameter metric. 39 | } 40 | \section{WARNING}{ 41 | 42 | Function assumes the current directory to be the root directory of the 43 | repository/package being tested. 44 | } 45 | \examples{ 46 | 47 | \dontrun{ 48 | # Set the current directory to the git repository concerned. 49 | setwd("./Path/to/repository") 50 | 51 | # Specify the test-file path 52 | t_path <- "Path/to/file" 53 | 54 | # Load the library 55 | library(Rperform) 56 | 57 | ## Example-1 58 | 59 | # Pass the parameters and obtain the run-time followed by memory details against 10 commits 60 | plot_metrics(test_path = t_path, metric = "time", n_commits = 10, save_data = F) 61 | plot_metrics(test_path = t_path, metric = "memory", n_commits = 10, save_data = F) 62 | 63 | ## Example-2 64 | 65 | # Obtain both memory and time metrics for each individual testthat block 66 | # inside a file and the file itself. The plots get stored in a directory 67 | # 'Rperform_testMetrics' in the repo's root directory. 68 | plot_metrics(test_path = t_path, metric = "testMetrics", n_commits = 5, save_data = F) 69 | } 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /SampleFiles_TravisPR/sample_tempRperform.R: -------------------------------------------------------------------------------- 1 | ##################################################################################### 2 | # Make sure that you have correctly set the test directory in the below R 3 | # command. The directory is set relative to the root of your git repository. 4 | # The parameter output_name specifies the name of the output html file which 5 | # will eventually be pushed to the gh-pages branch of your git repository. The 6 | # default results in a file named index.html 7 | 8 | Rperform::plot_webpage(test_directory = "./tests/testthat/", metric = "testMetrics", 9 | output_name = "index") 10 | 11 | ##################################################################################### 12 | # Alternatively, you can use other Rperform commands too. For example, 13 | # Rperform::plot_metrics(test_path = , metric = , num_commits = , save_plots = FALSE) 14 | 15 | ##################################################################################### 16 | # If you want to use the metrics data to make custom plots, follow the 17 | # instructions below and uncomment the required lines. Comment the above 18 | # plot_webpage() command in case you don't require it. 19 | 20 | # To obtain time and memory data for specific tests, 21 | # time_data <- Rperform::time_compare(test_path = , num_commits = ) 22 | # memory_data <- Rperform::mem_compare(test_path = , num_commits = ) 23 | 24 | # Given below is basic code to plot the time data. Make modifications as per 25 | # your purpose. 26 | # time_plot <- ggplot2::qplot(message, metric_val, data = time_data) + 27 | # ggplot2::facet_grid(facets = test_name ~ ., scales = "free") + 28 | # ggplot2::geom_point(color = "blue") + 29 | # ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90)) + 30 | # ggplot2::scale_x_discrete(limits = rev(levels(time_data$message))) + 31 | # # In the above 3 lines code, the first line creates the basic qplot. The 32 | # # second and third lines display the x-axis labels at 90 degrees to the 33 | # # horizontal and correct the order of message labels on the x -axis, 34 | # # respectively. 35 | # ggplot2::xlab("Commit message") + 36 | # ggplot2::ylab("Time (in seconds)") + 37 | # ggplot2::ggtitle(label = paste0("Variation in time metrics for ", curr_name)) 38 | # 39 | # 40 | # output_name <- "index" 41 | # out_file <- paste0(output_name, ".Rmd") 42 | # 43 | # if(!file.exists(out_file)){ 44 | # file.create(out_file) 45 | # } 46 | # 47 | # line_p1 <- "---\ntitle: \"plot\"\noutput: html_document\n---\n\n```{r}\nprint(\"" 48 | # line_p2 <- "time_plot\")\n```" 49 | # file_lines <- paste0(line_p1, line_p2) 50 | # writeLines(file_lines, con = out_file) 51 | # knitr::knit2html(input = out_file, output = paste0(output_name, ".html")) 52 | 53 | ##################################################################################### -------------------------------------------------------------------------------- /SampleFiles_TravisPR/sample_push_gh_pages.sh: -------------------------------------------------------------------------------- 1 | if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 2 | echo -e "Starting to update gh-pages\n" 3 | 4 | # Setup git 5 | git config --global user.email $USER_EMAIL 6 | git config --global user.name $USER_NAME 7 | 8 | # Store the original location (repo to be tested) and go up one level 9 | pushd ./ 10 | cd .. 11 | 12 | # Create a copy of the repo to be tested 13 | cp -Rf `ls` Rperform_copy 14 | cd Rperform_copy 15 | 16 | # Run the Rperform functions 17 | echo `pwd` 18 | touch temp_Rperform.R 19 | echo $RPERFORM_COMMAND >> temp_Rperform.R 20 | echo "Contents of Rperform_copy before running Rperform: " 21 | echo `ls` 22 | Rscript temp_Rperform.R 23 | echo "Contents of Rperform_copy after running Rperform: " 24 | echo `ls` 25 | rm temp_Rperform.R 26 | 27 | # We copy the generated html file to one level above the current directory (repo) in order 28 | # to easily move it to the gh-pages directory (which we will download later) 29 | cp -Rf RperformTest.html ../index.html 30 | # Go up one level 31 | cd .. 32 | 33 | # Using token clone gh-pages branch 34 | git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG} gh-pages > /dev/null 35 | 36 | # Copy the generated html file to the gh-pages branch and preserve the existing files 37 | cd ./gh-pages/Rperform 38 | if [! -f index.html] 39 | then 40 | mv index.html index_old.html 41 | fi 42 | cp -Rf ../../index.html index_buildnum${TRAVIS_BUILD_NUMBER}.html 43 | cp index_buildnum${TRAVIS_BUILD_NUMBER}.html index.html 44 | 45 | # Add, commit and push files to the gh-pages branch 46 | git add -f . 47 | git commit -m "Travis build $TRAVIS_BUILD_NUMBER pushed to gh-pages" 48 | git push -fq origin gh-pages > /dev/null 49 | 50 | popd 51 | echo -e "Done magic with Rperform\n" 52 | fi 53 | 54 | if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then 55 | echo -e "Starting to update gh-pages for the PR\n" 56 | 57 | # Setup git 58 | git config --global user.email $USER_EMAIL 59 | git config --global user.name $USER_NAME 60 | 61 | # Store the original location (repo to be tested) and go up one level 62 | pushd ./ 63 | cd .. 64 | 65 | # Create a copy of the repo to be tested 66 | cp -Rf `ls` Rperform_copy 67 | 68 | # Run the Rperform functions 69 | touch temp_Rperform.R 70 | # The plot_PR_webpage() function will generate a html file comparing performance of the current 71 | # branch (which has been pushed to Travis) and the master branch. 72 | echo $PR_COMMAND >> temp_Rperform.R 73 | Rscript temp_Rperform.R 74 | rm temp_Rperform.R 75 | rm PR.Rmd 76 | 77 | #using token clone gh-pages branch 78 | git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG} gh-pages > /dev/null 79 | 80 | # Copy the generated html file to the gh-pages branch and preserve the old files 81 | cd ./gh-pages 82 | if [! -f index.html] 83 | then 84 | mv index.html index_old.html 85 | fi 86 | cp -Rf ../RperformTest.html index_buildnum${TRAVIS_BUILD_NUMBER}.html 87 | cp index_buildnum${TRAVIS_BUILD_NUMBER}.html index.html 88 | 89 | # Add, commit and push files to gh-pages branch of the repo 90 | git add -f . 91 | git commit -m "Travis PR $TRAVIS_PULL_REQUEST build pushed to gh-pages" 92 | git push -fq origin gh-pages > /dev/null 93 | 94 | popd 95 | echo -e "Done magic with Rperform and PR\n" 96 | fi 97 | -------------------------------------------------------------------------------- /man/plot_branchmetrics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_metrics.R 3 | \name{plot_branchmetrics} 4 | \alias{plot_branchmetrics} 5 | \title{Plot and compare different versions of test files across two branches.} 6 | \usage{ 7 | plot_branchmetrics(test_path, metric, branch1, branch2 = "master", 8 | save_data = FALSE, save_plots = TRUE) 9 | } 10 | \arguments{ 11 | \item{test_path}{File-path of the test-file which is to be used for run-time 12 | comparisons.} 13 | 14 | \item{metric}{Type of plot(s) desired. This can be set to \code{time}, 15 | \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 16 | for more details)} 17 | 18 | \item{branch1}{Name of the first branch whose commits are to be analyzed.} 19 | 20 | \item{branch2}{Name of the second branch whose commits are to be analyzed. 21 | This is supposedly the branch into which branch1 is to be merged and its 22 | default value is set to 'master'.} 23 | 24 | \item{save_data}{If set to TRUE, the data frame containing the metrics 25 | information is stored in the 'Rperform_Data' directory in the root of the 26 | repo. (default set to FALSE)} 27 | 28 | \item{save_plots}{If set to TRUE, the plots generated are stored in the 29 | 'Rperform_plots' directory in the root of the repo rather than being 30 | printed. (default set to TRUE)} 31 | } 32 | \description{ 33 | Given a test-file path and two branches, plot and compare the metrics of the 34 | file across the two branches. The chosen metric values are plotted against the 35 | commit message summaries. For the first branch, metrics are plotted upto the 36 | latest common commit of both the branches. For the second branch, metrics for 37 | only the latest commit are plotted, which may or may not be the latest common 38 | commit. If the parameter save_data is set 39 | to true, it also stores the corresponding data-frames in an RData file in a 40 | folder 'Rperform_Data' in the current directory.The metrics plotted are in 41 | accordance with those specified using the parameter metric. 42 | } 43 | \section{NOTE}{ 44 | 45 | This function can be helpful when analyzing how would merging a branch into 46 | the master branch would affect performance. 'branch2' is assumed to be the 47 | branch into which 'branch1' is to be merged. 48 | This function is useful only when branch1 had branched off from branch2 at some 49 | point, that is they have at least one common commit. 50 | } 51 | 52 | \section{WARNING}{ 53 | 54 | Function assumes the current directory to be the root directory of the 55 | repository/package being tested. 56 | } 57 | \examples{ 58 | 59 | \dontrun{ 60 | # Set the current directory to the git repository concerned. 61 | setwd("./Path/to/repository") 62 | 63 | # Specify the test-file path 64 | t_path <- "Path/to/file" 65 | 66 | # Load the library 67 | library(Rperform) 68 | 69 | ## Example-1 70 | 71 | # Pass the parameters and obtain the run-time details for branches, 'experiment' and 72 | 'master'. 73 | # Since branch2 is not specified in this case, it's assumed to be 'master'. 74 | plot_branchmetrics(test_path = t_path, metric = "time", branch1 = 'experiment', save_data = F) 75 | 76 | # Pass the parameters and obtain the memory details. 77 | plot_branchmetrics(test_path = t_path, metric = 'memory', 78 | branch1 = 'experiment_1', branch2 = 'experiment_2') 79 | 80 | ## Example-2 81 | 82 | # Obtain both memory and time metrics for each individual testthat block 83 | # inside a file, and those for the file itself. Same as the other metric 84 | # cases, metrics both the commits are plotted. The plots get stored in a 85 | # directory 'Rperform_testMetrics' in the repo's root directory. 86 | plot_metrics(test_path = t_path, metric = "testMetrics", branch1 = "experiment") 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Rperform 5 | 6 | [![R-CMD-check](https://github.com/analyticalmonk/Rperform/workflows/Check-RPerform/badge.svg)](https://github.com/analyticalmonk/Rperform/actions) 7 | 8 | Rperform is a package that allows R developers to track quantitative performance metrics of their code. It focuses on providing changes in a package’s performance metrics, related to runtime and memory, over different git versions and across git branches. Rperform can be integrated with Travis-CI to do performance testing during Travis builds by making changes to the repo’s .travis.yml file. **It can prove to be particularly useful while measuring the possible changes which can be introduced by a pull request (PR).** 9 | 10 | ***For integrating Rperform with your travis build, check out the instructions provided on the [Rperform Wiki](https://github.com/analyticalmonk/Rperform/wiki/Integrating-Rperform-with-Travis-CI)***. 11 | 12 | ***For more information, tutorials and related blog posts, go through the [Rperform wiki](https://github.com/analyticalmonk/Rperform/wiki/Integrating-Rperform-with-Travis-CI).*** 13 | 14 | \*The project was initiated as a part of the [Google Summer of Code 2015](https://github.com/rstats-gsoc/gsoc2015/wiki/Test-timings-on-Travis) program and accepted into [Google Summer of Code 2016](https://github.com/rstats-gsoc/gsoc2016/wiki/Rperform:-Performance-analysis-of-R-package-code) program. 15 | 16 | \*The project has also been accepted into the [Google Summer of Code 2022](https://github.com/rstats-gsoc/gsoc2022/wiki/Rperform) program with [Toby Dylan Hocking](https://github.com/tdhock), [Akash Tandon](https://github.com/analyticalmonk) and [Randy Lai](mailto:randy.cs.lai@gmail.com) as the mentors. 17 | 18 | # Installation 19 | 20 | - You can install the package from github using `devtools`\*. 21 | 22 | 23 | 24 | ``` r 25 | library(devtools) 26 | install_github("analyticalmonk/Rperform") 27 | ``` 28 | 29 | or, 30 | 31 | ``` r 32 | devtools::install_github("analyticalmonk/Rperform") 33 | ``` 34 | 35 | # Basic examples 36 | 37 | *For detailed information regarding Rperform’s plotting functions, check out the [**Wiki**](https://github.com/analyticalmonk/Rperform/wiki/Plotting-package-metrics-with-Rperform).* 38 | 39 | **IMPORTANT**: The Rperform package requires you to set the current directory to the concerned git repository before using the functions. 40 | 41 | ``` r 42 | > setwd(dir = "Path/to/repo") 43 | ``` 44 | 45 | - The following example illustrates the use of the `Rperform::plot_metrics()` function on the git repository of the package [stringr](https://github.com/EngineerDanny/stringr). 46 | 47 | 48 | 49 | ``` r 50 | > setwd("./stringr") 51 | > library(Rperform) 52 | > plot_metrics(test_path = "inst/tests/test-join.r", metric = "time", num_commits = 10, save_data = FALSE, save_plots = FALSE) 53 | ``` 54 | 55 | ![time plot](images/Rplot_time_updated.png) 56 | 57 | - The following example illustrates the use of the `Rperform::plot_branchmetrics()` function on the git repository of the package [stringr](https://github.com/EngineerDanny/stringr). 58 | 59 | 60 | 61 | ``` r 62 | > setwd("./stringr") 63 | > library(Rperform) 64 | > plot_branchmetrics(test_path = "inst/tests/test-interp.r", metric = "memory", branch1 = "rperform_test", branch2 = "master", save_data = F, save_plots = F) 65 | ``` 66 | 67 | ![memory plot](images/Rplot_branchmem.jpeg) 68 | 69 | - The following example illustrates the use of the `Rperform::time_compare()` and `Rperform::mem_compare()` functions on the git repository of the package [stringr](https://github.com/EngineerDanny/stringr). 70 | 71 | 72 | 73 | ``` r 74 | > setwd("./stringr") 75 | > library(Rperform) 76 | > time_compare(test_path = "inst/tests/test-dup.r", num_commits = 2) 77 | 78 | test_name metric_name status metric_val message date_time 79 | 1 basic duplication works runtime (in seconds) pass 0.02904030 update 2022-06-28 09:38:26 80 | 2 basic duplication works runtime (in seconds) pass 0.02910049 update 2022-06-28 09:38:26 81 | 3 basic duplication works runtime (in seconds) pass 0.03021192 update 2022-06-28 09:38:26 82 | 4 0 duplicates equals empty string runtime (in seconds) pass 0.01538062 update 2022-06-28 09:38:26 83 | 5 0 duplicates equals empty string runtime (in seconds) pass 0.01498333 update 2022-06-28 09:38:26 84 | 6 0 duplicates equals empty string runtime (in seconds) pass 0.01516619 update 2022-06-28 09:38:26 85 | 7 test-dup.r runtime (in seconds) pass 0.01260504 update 2022-06-28 09:38:26 86 | 8 test-dup.r runtime (in seconds) pass 0.01253468 update 2022-06-28 09:38:26 87 | 9 test-dup.r runtime (in seconds) pass 0.01240689 update 2022-06-28 09:38:26 88 | 10 basic duplication works runtime (in seconds) pass 0.02921783 update git igno 2022-06-28 09:25:42 89 | 11 basic duplication works runtime (in seconds) pass 0.02894895 update git igno 2022-06-28 09:25:42 90 | 12 basic duplication works runtime (in seconds) pass 0.03001007 update git igno 2022-06-28 09:25:42 91 | 13 0 duplicates equals empty string runtime (in seconds) pass 0.01586163 update git igno 2022-06-28 09:25:42 92 | 14 0 duplicates equals empty string runtime (in seconds) pass 0.01525475 update git igno 2022-06-28 09:25:42 93 | 15 0 duplicates equals empty string runtime (in seconds) pass 0.01512830 update git igno 2022-06-28 09:25:42 94 | 16 test-dup.r runtime (in seconds) pass 0.01266998 update git igno 2022-06-28 09:25:42 95 | 17 test-dup.r runtime (in seconds) pass 0.01242419 update git igno 2022-06-28 09:25:42 96 | 18 test-dup.r runtime (in seconds) pass 0.01267261 update git igno 2022-06-28 09:25:42 97 | > 98 | ``` 99 | 100 | ``` r 101 | > Rperform::mem_compare(test_path = "inst/tests/test-join.r", num_commits = 1) 102 | 103 | test_name metric_name status metric_val msg_val date_time 104 | 11.1 basic case works max_mem_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 105 | 11.2 basic case works leak_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 106 | 11.3 NULLs are dropped max_mem_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 107 | 11.4 NULLs are dropped leak_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 108 | 11.5 test-join.r max_mem_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 109 | 11.6 test-join.r leak_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 110 | 12.1 basic case works max_mem_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 111 | 12.2 basic case works leak_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 112 | 12.3 NULLs are dropped max_mem_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 113 | 12.4 NULLs are dropped leak_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 114 | 12.5 test-join.r max_mem_mb pass 0.144 Can now use CRA 2015-01-08 14:09:43 115 | 12.6 test-join.r leak_mb pass 0.144 Can now use CRA 2015-01-08 14:09:43 116 | 13.1 basic case works max_mem_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 117 | 13.2 basic case works leak_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 118 | 13.3 NULLs are dropped max_mem_mb pass 0.048 Can now use CRA 2015-01-08 14:09:43 119 | 13.4 NULLs are dropped leak_mb pass 0.048 Can now use CRA 2015-01-08 14:09:43 120 | 13.5 test-join.r max_mem_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 121 | 13.6 test-join.r leak_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 122 | ``` -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | # Rperform 8 | 9 | [![R-CMD-check](https://github.com/analyticalmonk/Rperform/workflows/Check-RPerform/badge.svg)](https://github.com/analyticalmonk/Rperform/actions) 10 | 11 | Rperform is a package that allows R developers to track quantitative performance metrics of their code. It focuses on providing changes in a package’s performance metrics, related to runtime and memory, over different git versions and across git branches. Rperform can be integrated with Travis-CI to do performance testing during Travis builds by making changes to the repo’s .travis.yml file. **It can prove to be particularly useful while measuring the possible changes which can be introduced by a pull request (PR).** 12 | 13 | ***For integrating Rperform with your travis build, check out the instructions provided on the [Rperform Wiki](https://github.com/analyticalmonk/Rperform/wiki/Integrating-Rperform-with-Travis-CI)***. 14 | 15 | ***For more information, tutorials and related blog posts, go through the [Rperform wiki](https://github.com/analyticalmonk/Rperform/wiki/Integrating-Rperform-with-Travis-CI).*** 16 | 17 | \*The project was initiated as a part of the [Google Summer of Code 2015](https://github.com/rstats-gsoc/gsoc2015/wiki/Test-timings-on-Travis) program and accepted into [Google Summer of Code 2016](https://github.com/rstats-gsoc/gsoc2016/wiki/Rperform:-Performance-analysis-of-R-package-code) program. 18 | 19 | \*The project has also been accepted into the [Google Summer of Code 2022](https://github.com/rstats-gsoc/gsoc2022/wiki/Rperform) program with [Toby Dylan Hocking](https://github.com/tdhock), [Akash Tandon](https://github.com/analyticalmonk) and [Randy Lai](mailto:randy.cs.lai@gmail.com) as the mentors. 20 | 21 | # Installation 22 | 23 | - You can install the package from github using `devtools`\*. 24 | 25 | 26 | 27 | ``` r 28 | library(devtools) 29 | install_github("analyticalmonk/Rperform") 30 | ``` 31 | 32 | or, 33 | 34 | ``` r 35 | devtools::install_github("analyticalmonk/Rperform") 36 | ``` 37 | 38 | # Basic examples 39 | 40 | *For detailed information regarding Rperform’s plotting functions, check out the [**Wiki**](https://github.com/analyticalmonk/Rperform/wiki/Plotting-package-metrics-with-Rperform).* 41 | 42 | **IMPORTANT**: The Rperform package requires you to set the current directory to the concerned git repository before using the functions. 43 | 44 | ``` r 45 | > setwd(dir = "Path/to/repo") 46 | ``` 47 | 48 | - The following example illustrates the use of the `Rperform::plot_metrics()` function on the git repository of the package [stringr](https://github.com/EngineerDanny/stringr). 49 | 50 | 51 | 52 | ``` r 53 | > setwd("./stringr") 54 | > library(Rperform) 55 | > plot_metrics(test_path = "inst/tests/test-join.r", metric = "time", num_commits = 10, save_data = FALSE, save_plots = FALSE) 56 | ``` 57 | 58 | ![time plot](images/Rplot_time_updated.png) 59 | 60 | - The following example illustrates the use of the `Rperform::plot_branchmetrics()` function on the git repository of the package [stringr](https://github.com/EngineerDanny/stringr). 61 | 62 | 63 | 64 | ``` r 65 | > setwd("./stringr") 66 | > library(Rperform) 67 | > plot_branchmetrics(test_path = "inst/tests/test-interp.r", metric = "memory", branch1 = "rperform_test", branch2 = "master", save_data = F, save_plots = F) 68 | ``` 69 | 70 | ![memory plot](images/Rplot_branchmem.jpeg) 71 | 72 | - The following example illustrates the use of the `Rperform::time_compare()` and `Rperform::mem_compare()` functions on the git repository of the package [stringr](https://github.com/EngineerDanny/stringr). 73 | 74 | 75 | 76 | ``` r 77 | > setwd("./stringr") 78 | > library(Rperform) 79 | > time_compare(test_path = "inst/tests/test-dup.r", num_commits = 2) 80 | 81 | test_name metric_name status metric_val message date_time 82 | 1 basic duplication works runtime (in seconds) pass 0.02904030 update 2022-06-28 09:38:26 83 | 2 basic duplication works runtime (in seconds) pass 0.02910049 update 2022-06-28 09:38:26 84 | 3 basic duplication works runtime (in seconds) pass 0.03021192 update 2022-06-28 09:38:26 85 | 4 0 duplicates equals empty string runtime (in seconds) pass 0.01538062 update 2022-06-28 09:38:26 86 | 5 0 duplicates equals empty string runtime (in seconds) pass 0.01498333 update 2022-06-28 09:38:26 87 | 6 0 duplicates equals empty string runtime (in seconds) pass 0.01516619 update 2022-06-28 09:38:26 88 | 7 test-dup.r runtime (in seconds) pass 0.01260504 update 2022-06-28 09:38:26 89 | 8 test-dup.r runtime (in seconds) pass 0.01253468 update 2022-06-28 09:38:26 90 | 9 test-dup.r runtime (in seconds) pass 0.01240689 update 2022-06-28 09:38:26 91 | 10 basic duplication works runtime (in seconds) pass 0.02921783 update git igno 2022-06-28 09:25:42 92 | 11 basic duplication works runtime (in seconds) pass 0.02894895 update git igno 2022-06-28 09:25:42 93 | 12 basic duplication works runtime (in seconds) pass 0.03001007 update git igno 2022-06-28 09:25:42 94 | 13 0 duplicates equals empty string runtime (in seconds) pass 0.01586163 update git igno 2022-06-28 09:25:42 95 | 14 0 duplicates equals empty string runtime (in seconds) pass 0.01525475 update git igno 2022-06-28 09:25:42 96 | 15 0 duplicates equals empty string runtime (in seconds) pass 0.01512830 update git igno 2022-06-28 09:25:42 97 | 16 test-dup.r runtime (in seconds) pass 0.01266998 update git igno 2022-06-28 09:25:42 98 | 17 test-dup.r runtime (in seconds) pass 0.01242419 update git igno 2022-06-28 09:25:42 99 | 18 test-dup.r runtime (in seconds) pass 0.01267261 update git igno 2022-06-28 09:25:42 100 | > 101 | ``` 102 | 103 | ``` r 104 | > Rperform::mem_compare(test_path = "inst/tests/test-join.r", num_commits = 1) 105 | 106 | test_name metric_name status metric_val msg_val date_time 107 | 11.1 basic case works max_mem_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 108 | 11.2 basic case works leak_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 109 | 11.3 NULLs are dropped max_mem_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 110 | 11.4 NULLs are dropped leak_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 111 | 11.5 test-join.r max_mem_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 112 | 11.6 test-join.r leak_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 113 | 12.1 basic case works max_mem_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 114 | 12.2 basic case works leak_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 115 | 12.3 NULLs are dropped max_mem_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 116 | 12.4 NULLs are dropped leak_mb pass 0.044 Can now use CRA 2015-01-08 14:09:43 117 | 12.5 test-join.r max_mem_mb pass 0.144 Can now use CRA 2015-01-08 14:09:43 118 | 12.6 test-join.r leak_mb pass 0.144 Can now use CRA 2015-01-08 14:09:43 119 | 13.1 basic case works max_mem_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 120 | 13.2 basic case works leak_mb pass 0.040 Can now use CRA 2015-01-08 14:09:43 121 | 13.3 NULLs are dropped max_mem_mb pass 0.048 Can now use CRA 2015-01-08 14:09:43 122 | 13.4 NULLs are dropped leak_mb pass 0.048 Can now use CRA 2015-01-08 14:09:43 123 | 13.5 test-join.r max_mem_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 124 | 13.6 test-join.r leak_mb pass 0.148 Can now use CRA 2015-01-08 14:09:43 125 | ``` 126 | -------------------------------------------------------------------------------- /R/branch_metrics.R: -------------------------------------------------------------------------------- 1 | ## ----------------------------------------------------------------------------------------- 2 | ## TIME CHUNK BEGINS 3 | ## ----------------------------------------------------------------------------------------- 4 | 5 | #' Run-times of a file on the given branch. 6 | #' 7 | #' Given a test-file and branch, returns the run-time details of the file over 8 | #' the given number of commits on the branch. 9 | #' 10 | #' @param test_path File-path for the test file to be tested. 11 | #' @param branch Branch against whose commits the test file is to be 12 | #' tested (with master being the default). 13 | #' @param num_commits Number of commits on the branch against which the test 14 | #' file is to be tested. 15 | #' 16 | #' @examples 17 | #' 18 | #' \dontrun{ 19 | #' # Set the current directory to the git repository concerned. 20 | #' setwd("./Path/to/repository") 21 | #' 22 | #' # Set the file-path 23 | #' t_path <- "Path/to/file" 24 | #' 25 | #' # Load the library and pass the parameters to the function 26 | #' library(Rperform) 27 | #' time_branch(test_path = t_path, branch_name = "helper", num_commits = 10) 28 | #' } 29 | #' @section Value: 30 | #' time_branch returns an object of class "data.frame". 31 | #' The data-frame consists of the following columns: 32 | #' \code{test_name} 33 | #' \code{metric_name} 34 | #' \code{status} 35 | #' \code{metric_val} 36 | #' \code{message} 37 | #' \code{date_time} 38 | #' \code{branch} 39 | #' 40 | #' @section Warning: 41 | #' Function assumes the current directory to be the root directory of the 42 | #' package being tested. 43 | #' 44 | 45 | # Given a test and branch, time_branch returns the run-time details of the test 46 | # over the given number of commits on the specified branch. 47 | 48 | time_branch <- function(test_path, branch = "master", num_commits = 5) { 49 | stopifnot(is.character(test_path)) 50 | stopifnot(length(test_path) == 1) 51 | stopifnot(is.character(branch)) 52 | stopifnot(length(branch) == 1) 53 | stopifnot(is.numeric(num_commits)) 54 | stopifnot(length(num_commits) == 1) 55 | num_commits <- floor(num_commits) 56 | 57 | # Git operations 58 | target <- git2r::repository("./") 59 | origin_state <- git2r::repository_head(target) 60 | git2r::checkout(target, branch) 61 | on.exit(expr = git2r::checkout(origin_state)) 62 | 63 | # We use time_compare() from R/repo_metrics.R to obtain the required 64 | # results. 65 | test_results_df <- time_compare(test_path, num_commits) 66 | test_results_df$branch <- branch 67 | ## ----------------------------------------------------------------------- 68 | 69 | test_results_df 70 | } 71 | 72 | ## ----------------------------------------------------------------------------------------- 73 | #' Run-time details across branches. 74 | #' 75 | #' Given a test-file and two branches, returns the run-time details of the file 76 | #' against the first commit till the latest common commit in branch1, and 77 | #' against the latest commit in branch2. 78 | #' 79 | #' @param test_path File-path for the test file to be tested. 80 | #' @param branch1 Branch against whose commits the test file is to be 81 | #' tested. 82 | #' @param branch2 Branch into which branch1 is supposedly to be merged. 83 | #' 84 | #' @examples 85 | #' 86 | #' \dontrun{ 87 | #' # Set the current directory to the git repository concerned. 88 | #' setwd("./Path/to/repository") 89 | #' 90 | #' # Set the file-path 91 | #' t_path <- "Path/to/file" 92 | #' 93 | #' # Load the library and pass the parameters to the function 94 | #' library(Rperform) 95 | #' compare_brancht(test_path = t_path, branch1 = "helper", branch2 = "master") 96 | #' } 97 | #' 98 | #' @section Value: 99 | #' compare_brancht returns an object of class "data.frame". 100 | #' The data-frame consists of the following columns: 101 | #' \code{test_name} 102 | #' \code{metric_name} 103 | #' \code{status} 104 | #' \code{metric_val} 105 | #' \code{message} 106 | #' \code{date_time} 107 | #' \code{branch} 108 | #' 109 | #' @section Warning: 110 | #' Function assumes the current directory to be the root directory of the 111 | #' package being tested. 112 | #' 113 | 114 | compare_brancht <- function(test_path, branch1, branch2 = "master") { 115 | stopifnot(is.character(test_path)) 116 | stopifnot(length(test_path) == 1) 117 | stopifnot(is.character(branch1)) 118 | stopifnot(length(branch1) == 1) 119 | stopifnot(is.character(branch2)) 120 | stopifnot(length(branch2) == 1) 121 | 122 | same_commit <- .common_commit(branch1 = branch1, branch2 = branch2) 123 | # same_commit 124 | # --------------------------------------------- 125 | # common_datetime, cnum_b1, cnum_b2 126 | 127 | if (same_commit$cnum_b2 == 1) { 128 | branch1_df <- time_branch(test_path = test_path, branch = branch1, 129 | num_commits = (same_commit$cnum_b1 - 1)) 130 | } 131 | else { 132 | branch1_df <- time_branch(test_path = test_path, branch = branch1, 133 | num_commits = same_commit$cnum_b1) 134 | } 135 | branch2_df <- time_branch(test_path = test_path, branch = branch2, 136 | num_commits = 1) 137 | 138 | rbind(branch1_df, branch2_df) 139 | } 140 | 141 | 142 | ## ----------------------------------------------------------------------------------------- 143 | ## MEMORY CHUNK BEGINS 144 | ## ----------------------------------------------------------------------------------------- 145 | 146 | #' Memory metrics across branches. 147 | #' 148 | #' Given a test-file and two branches, returns the memory metrics of the file 149 | #' against the first commit till the latest common commit in branch1, and 150 | #' against the latest commit in branch2. Memory metrics returned are the memory 151 | #' leaked and maximum memory utilized during its execution. 152 | #' 153 | #' @param test_path File-path for the test file to be tested. 154 | #' @param branch1 Branch against whose commits the test file is to be 155 | #' tested. 156 | #' @param branch2 Branch into which branch1 is supposedly to be merged. 157 | #' 158 | #' @examples 159 | #' 160 | #' \dontrun{ 161 | #' # Set the current directory to the git repository concerned. 162 | #' setwd("./Path/to/repository") 163 | #' 164 | #' # Set the file-path 165 | #' t_path <- "Path/to/file" 166 | #' 167 | #' # Load the library and pass the parameters to the function 168 | #' library(Rperform) 169 | #' compare_branchm(test_path = t_path, branch1 = "helper", branch2 = "master") 170 | #' } 171 | #' 172 | #' @section Value: 173 | #' compare_branchm returns an object of class "data.frame". 174 | #' The data-frame consists of the following columns: 175 | #' \code{test_name} 176 | #' \code{metric_name} 177 | #' \code{status} 178 | #' \code{metric_val} 179 | #' \code{message} 180 | #' \code{date_time} 181 | #' \code{time_branch} 182 | #' 183 | #' @section Warning: 184 | #' Function assumes the current directory to be the root directory of the 185 | #' package being tested. 186 | #' 187 | 188 | compare_branchm <- function(test_path, branch1, branch2 = "master") { 189 | stopifnot(is.character(test_path)) 190 | stopifnot(length(test_path) == 1) 191 | stopifnot(is.character(branch1)) 192 | stopifnot(length(branch1) == 1) 193 | stopifnot(is.character(branch2)) 194 | stopifnot(length(branch2) == 1) 195 | 196 | target <- git2r::repository("./") 197 | original_state <- git2r::repository_head(target) 198 | same_commit <- .common_commit(branch1 = branch1, branch2 = branch2) 199 | # same_commit 200 | # --------------------------------------------- 201 | # common_datetime, cnum_b1, cnum_b2 202 | 203 | # For branch1 204 | git2r::checkout(target, branch1) 205 | if (same_commit$cnum_b2 == 1) { 206 | branch1_df <- mem_compare(test_path = test_path, num_commits = (same_commit$cnum_b1 - 1)) 207 | } 208 | else { 209 | branch1_df <- mem_compare(test_path = test_path, num_commits = same_commit$cnum_b1) 210 | } 211 | branch1_df$branch <- rep(branch1, times = nrow(branch1_df)) 212 | git2r::checkout(original_state) 213 | 214 | # For branch2 215 | git2r::checkout(target, branch2) 216 | branch2_df <- mem_compare(test_path = test_path, num_commits = 1) 217 | branch2_df$branch <- "master" 218 | git2r::checkout(original_state) 219 | 220 | rbind(branch1_df, branch2_df) 221 | } 222 | 223 | ## ----------------------------------------------------------------------------------------- 224 | 225 | ## ----------------------------------------------------------------------------------------- 226 | ## Function to find the latest common commit given two branches of a repository 227 | ## ---------------------------------------------------------------------------- 228 | 229 | .common_commit <- function(dir1 = NULL, dir2 = NULL, branch1 = NULL, branch2 = NULL, 230 | PR = F) { 231 | 232 | curr_dir <- file.path("./../") 233 | 234 | ## Git operations 235 | # Change into the first target directory 236 | if (!is.null(dir1)) { 237 | setwd(dir1) 238 | } 239 | target1 <- git2r::repository(file.path("./")) 240 | # If branch1 is specified, check out to it and obtain commit list 241 | if (!is.null(branch1)) { 242 | original_state1 <- git2r::repository_head(target1) 243 | git2r::checkout(object = target1, branch = branch1) 244 | } 245 | commitlist1 <- git2r::commits(target1) 246 | # Revert to the original state if checked out to branch1 before 247 | if (!is.null(branch1)) { 248 | git2r::checkout(original_state1) 249 | } 250 | 251 | # Change into the second target directory 252 | if (!is.null(dir2)) { 253 | setwd(curr_dir) 254 | setwd(dir2) 255 | } 256 | target2 <- git2r::repository(file.path("./")) 257 | # If branch2 is specified, check out to it and obtain commit list 258 | if (!is.null(branch2)) { 259 | original_state2 <- git2r::repository_head(target2) 260 | git2r::checkout(object = target2, branch = branch2) 261 | } 262 | commitlist2 <- git2r::commits(target2) 263 | # Revert to the original state if checked out to branch2 before 264 | if (!is.null(branch2)) { 265 | git2r::checkout(original_state2) 266 | } 267 | # Change to original directory if changed into directory 2 268 | if (!is.null(dir2)) { 269 | setwd(curr_dir) 270 | } 271 | 272 | dtime_list1 <- lapply(commitlist1, FUN = get_datetime) 273 | dtime_list2 <- lapply(commitlist2, FUN = get_datetime) 274 | 275 | for (c1 in seq(dtime_list1)) { 276 | if (PR == T) { 277 | if (c1 == 1) { 278 | next 279 | } 280 | } 281 | search_result <- .b_search(dtime_list2, dtime_list1[[c1]], 282 | 1, length(dtime_list2)) 283 | if (search_result$status) { 284 | commit1 <- c1 285 | commit2 <- search_result$sequence 286 | break 287 | } 288 | } 289 | 290 | info_df <- data.frame(common_datetime = dtime_list1[[commit1]], 291 | common_message = get_msg(commitlist1[[commit1]]), 292 | cnum_b1 = commit1, cnum_b2 = commit2) 293 | info_df 294 | } 295 | 296 | ## ----------------------------------------------------------------------------------------- 297 | 298 | ## ----------------------------------------------------------------------------------------- 299 | ## Customized (recursive) implementation of Binary Search 300 | ## ------------------------------------------------------ 301 | 302 | .b_search <- function(search_list, search_target, start, end) { 303 | mid_val <- as.integer((end + start) / 2) 304 | if (mid_val < 1) { 305 | info_df <- data.frame(status= F, sequence = mid_val) 306 | } else if (search_list[[mid_val]] == search_target) { 307 | info_df <- data.frame(status = T, sequence = mid_val) 308 | return(info_df) 309 | } else if (search_target < search_list[[mid_val]]) { 310 | return(.b_search(search_list, search_target, mid_val + 1, end)) 311 | } else { 312 | return(.b_search(search_list, search_target, start, mid_val - 1)) 313 | } 314 | } 315 | 316 | ## ----------------------------------------------------------------------------------------- 317 | -------------------------------------------------------------------------------- /R/travisPR_metrics.R: -------------------------------------------------------------------------------- 1 | #' Compare performance across directories/repositories. 2 | #' 3 | #' Given a test-file, two directories and the required metric, returns the 4 | #' metric details of the file against the first commit till the latest common 5 | #' commit for dir1, and against the latest commit for dir2. It also returns 6 | #' information regarding the latest common commit for the two directories. 7 | #' 8 | #' @param dir1 Path to the first directory/repository. 9 | #' @param dir2 Path to the second directory/repository. 10 | #' @param test_path File-path, relative to the directories, for the test file to 11 | #' be tested. 12 | #' @param metric The metric (runtime or memory) for which the file is to be 13 | #' tested. 14 | #' @param PR When set to True, it performs the analysis in accordance with the setup 15 | #' on Travis-CI environment for testing Pull Requests. 16 | #' 17 | #' @examples 18 | #' 19 | #' \dontrun{ 20 | #' # Set the current directory to the parent directory of the concerned repositories. 21 | #' setwd("./Path/to/parent/directory") 22 | #' 23 | #' # Set the directory paths 24 | #' d_path1 <- "Path/to/first/directory" 25 | #' d_path2 <- "Path/to/second/directory" 26 | #' 27 | #' # Set the file-path 28 | #' t_path <- "Path/to/file" 29 | #' 30 | #' # Load the library and pass the parameters to the function 31 | #' library(Rperform) 32 | #' compare_dir(d_path1, d_path2, t_path, metric = "time") 33 | #' } 34 | #' 35 | #' @section Value: compare_brancht returns an object of class "list" containing 36 | #' two members. 37 | #' 38 | #' The first member is a data-frame and consists of the following columns: 39 | #' \code{test_name} 40 | #' \code{metric_name} 41 | #' \code{status} 42 | #' \code{metric_val} 43 | #' \code{message} 44 | #' \code{sha} 45 | #' \code{date_time} 46 | #' \code{directory} 47 | #' 48 | #' The second member is a data-frame and consists of the following columns: 49 | #' \code{common_datetime} 50 | #' \code{common_message} 51 | #' \code{cnum_b1} 52 | #' \code{cnum_b2} 53 | #' 54 | #' @section Warning: 55 | #' Function assumes the current directory to be the parent directory of both 56 | #' the repositories being tested. That means both the repositories should be 57 | #' inside the same directory. 58 | 59 | compare_dir <- function(dir1, dir2, test_path, metric = "time", PR = F) { 60 | 61 | # Obtain information about the latest common commit. 62 | same_commit <- .common_commit(dir1=dir1, dir2=dir2, PR=PR) 63 | # same_commit 64 | # --------------------------------------------- 65 | # common_datetime, cnum_b1, cnum_b2 66 | 67 | # print("Printing same commit") 68 | # print(same_commit) 69 | 70 | curr_dir <- "../." 71 | 72 | setwd(dir1) 73 | if (metric == "time") { 74 | if (same_commit$cnum_b2 == 1 & same_commit$cnum_b1 != 1) { 75 | dir1_df <- time_compare(test_path = test_path, num_commits = same_commit$cnum_b1 - 1) 76 | } 77 | else { 78 | dir1_df <- time_compare(test_path = test_path, num_commits = same_commit$cnum_b1) 79 | } 80 | } 81 | else if (metric == "memory") { 82 | if (same_commit$cnum_b2 == 1 & same_commit$cnum_b1 != 1) { 83 | dir1_df <- mem_compare(test_path = test_path, num_commits = same_commit$cnum_b1 - 1) 84 | } 85 | else { 86 | dir1_df <- mem_compare(test_path = test_path, num_commits = same_commit$cnum_b1) 87 | } 88 | } 89 | dir1_df$directory <- rep(basename(dir1), times = nrow(dir1_df)) 90 | setwd(curr_dir) 91 | 92 | setwd(dir2) 93 | if (metric == "time") { 94 | dir2_df <- time_compare(test_path = test_path, num_commits = 1) 95 | } 96 | else if (metric == "memory") { 97 | dir2_df <- mem_compare(test_path = test_path, num_commits = 1) 98 | } 99 | dir2_df$directory <- rep(basename(dir2), times = nrow(dir2_df)) 100 | setwd(curr_dir) 101 | 102 | dir_df <- rbind(dir1_df, dir2_df) 103 | dir_list <- list(dir_df, same_commit) 104 | 105 | } 106 | 107 | ## ----------------------------------------------------------------------------------------- 108 | ## FUNCTIONS DESIGNED FOR TRAVIS PRs 109 | ## ----------------------------------------------------------------------------------------- 110 | 111 | #' Generate a webpage containing a visualization detailing PR's impact on 112 | #' performance without haveing to merge. 113 | #' 114 | #' The function must be called from a directory containing only a single git 115 | #' repository checked out to the branch which is meant to be tested against the 116 | #' master branch of the repository's remote repo. This function is designed keeping 117 | #' in mind the PR testing methodlogy of Travis-CI. 118 | #' Given a test-file path and the required metric, it creates a webpage 119 | #' visualizing the metric details of the file against the first commit till the 120 | #' latest common commit for the git repo, and against the latest commit for 121 | #' master branch of the repo's remote. 122 | #' 123 | #' @param test_path File-path, relative to the git repo, for the test file to 124 | #' be tested. 125 | #' @param metric The metric (runtime or memory) for which the file is to be 126 | #' tested. 127 | #' 128 | #' @examples 129 | #' 130 | #' \dontrun{ 131 | #' # Set the current directory to the parent directory of the concerned repository. 132 | #' setwd("./Path/to/parent/directory") 133 | #' 134 | #' # Set the file-path 135 | #' t_path <- "Path/to/file" 136 | #' 137 | #' # Load the library and pass the parameters to the function 138 | #' library(Rperform) 139 | #' plot_PR_webpage(t_path, metric = "time") 140 | #' } 141 | #' 142 | #' @section Value: None 143 | #' 144 | #' @section Warning: 145 | #' Function assumes the current directory to be the parent directory of the 146 | #' the repository being tested. 147 | 148 | plot_PR_webpage <- function(test_path, metric = "time") { 149 | 150 | out_file <- paste0("PR", ".Rmd") 151 | 152 | line_p1 <- "---\ntitle: \"plot\"\noutput: html_document\n---\n\n```{r}\nRperform::plot_PR(\"" 153 | line_p3 <- "\", metric = \"" 154 | line_p5 <- "\")\n```" 155 | file_lines <- paste0(line_p1, test_path, line_p3, metric, line_p5) 156 | writeLines(file_lines, con = out_file) 157 | rmarkdown::render(input = out_file, output_format = "html_document", 158 | output_file = paste0("RperformTest", ".html")) 159 | } 160 | 161 | ## ----------------------------------------------------------------------------------------- 162 | 163 | ## FUNCTION TO VISUALIZE METRIC DETAILS FOR A PR ON TRAVIS-CI 164 | 165 | #' Visualize PR's impact on performance without having to merge. 166 | #' 167 | #' The function must be called from a directory containing only a single git 168 | #' repository checked out to the branch which is meant to be tested against the 169 | #' master branch of the repository's remote repo. This function is designed keeping 170 | #' in mind the PR testing methodlogy of Travis-CI. 171 | #' Given a test-file path and the required metric, it plots the metric details 172 | #' of the file against the first commit till the latest common commit for the 173 | #' git repo, and against the latest commit for master branch of the repo's 174 | #' remote. 175 | #' 176 | #' @param test_path File-path, relative to the git repo, for the test file to 177 | #' be tested. 178 | #' @param metric The metric (runtime or memory) for which the file is to be 179 | #' tested. 180 | #' 181 | #' @examples 182 | #' 183 | #' \dontrun{ 184 | #' # Set the current directory to the parent directory of the concerned repository. 185 | #' setwd("./Path/to/parent/directory") 186 | #' 187 | #' # Set the file-path 188 | #' t_path <- "Path/to/file" 189 | #' 190 | #' # Load the library and pass the parameters to the function 191 | #' library(Rperform) 192 | #' plot_PR(t_path, metric = "time") 193 | #' } 194 | #' 195 | #' @section Value: None 196 | #' 197 | #' @section Warning: 198 | #' Function assumes the current directory to be the parent directory of the 199 | #' the repository being tested. 200 | 201 | plot_PR <- function(test_path, metric = "time") { 202 | 203 | dir_list <- compare_PR(test_path) 204 | same_commit <- dir_list[[2]] 205 | PR_data <- dir_list[[1]] 206 | dir1 <- unique(PR_data$directory)[1] 207 | dir2 <- unique(PR_data$directory)[2] 208 | 209 | curr_name <- gsub(pattern = " ", replacement = "_", x = basename(test_path)) 210 | curr_name <- gsub(pattern = "*.[rR]$", replacement = paste0("_", dir1, "_", dir2), 211 | x = curr_name) 212 | 213 | # Trying to find the min and max vals for each test 214 | ################################################### 215 | 216 | extremes_frame <- .find_midvals(data = PR_data) 217 | ## extremes_frame 218 | ## test_name | max_val | min_val | mid_val 219 | ## ---------------------------------------------------------- 220 | 221 | ################################################### 222 | 223 | # Plot the dires' metric data 224 | tryCatch(expr = {test_plot <- 225 | ggplot2::ggplot(data = PR_data, mapping = ggplot2::aes(message, metric_val)) + 226 | ggplot2::geom_point(color = "blue") + 227 | ggplot2::facet_grid(test_name ~ metric_name, scales = "free") + 228 | ggplot2::geom_text(data = extremes_frame, 229 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.3, 230 | y = mid_val, 231 | label = dir2, angle = 90)) + 232 | ggplot2::geom_text(data = extremes_frame, 233 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.7, 234 | y = mid_val, 235 | label = dir1, angle = -90)) + 236 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90), 237 | strip.text.x = ggplot2::element_text(size = 10, face = "bold")) + 238 | ggplot2::geom_vline(mapping = ggplot2::aes(xintercept = same_commit$cnum_b2 + 0.5)) + 239 | ggplot2::scale_x_discrete(limits = rev(levels(PR_data$message))) + 240 | # In the above 8 lines of code, the first line creates 241 | # the basic plot. The sixth and eigth lines display the 242 | # x-axis labels at 90 degrees to the horizontal and 243 | # correct the order of message labels on the x -axis, 244 | # respectively. The seventh line plots a vertical seperator between 245 | # the commit from dir2 and the commits from dir1. 246 | ggplot2::xlab(label = "Commit messages") + 247 | ggplot2::ylab(label = "Time (in seconds)") + 248 | ggplot2::ggtitle(label = paste0("Variation in ", metric, " metrics across branches ", 249 | dir2, " and PR# ", Sys.getenv("TRAVIS_PULL_REQUEST"))) 250 | 251 | print(test_plot) 252 | 253 | }, 254 | error = function(e){ 255 | print("Encountered an error!") 256 | }) 257 | 258 | } 259 | 260 | ## ----------------------------------------------------------------------------------------- 261 | 262 | ## FUNCTION TO OBTAIN METRIC DETAILS FOR THE COMMITS FROM A PR ON TRAVIS-CI 263 | 264 | #' Analyze PR's impact on performance without having to merge. 265 | #' 266 | #' The function must be called from a directory containing only a single git 267 | #' repository checked out to the branch which is meant to be tested against the 268 | #' master branch of the repository's remote repo. This function is designed keeping 269 | #' in mind the PR testing methodlogy of Travis-CI. 270 | #' Given a test-file path and the required metric, it returns the metric details 271 | #' of the file against the first commit till the latest common commit for the 272 | #' git repo, and against the latest commit for master branch of the repo's 273 | #' remote. It also returns information regarding the latest common commit for 274 | #' the two directories. 275 | #' 276 | #' @param test_path File-path, relative to the git repo, for the test file to 277 | #' be tested. 278 | #' @param metric The metric (runtime or memory) for which the file is to be 279 | #' tested. 280 | #' 281 | #' @examples 282 | #' 283 | #' \dontrun{ 284 | #' # Set the current directory to the parent directory of the concerned repository. 285 | #' setwd("./Path/to/parent/directory") 286 | #' 287 | #' # Set the file-path 288 | #' t_path <- "Path/to/file" 289 | #' 290 | #' # Load the library and pass the parameters to the function 291 | #' library(Rperform) 292 | #' compare_PR(t_path, metric = "time") 293 | #' } 294 | #' 295 | #' @section Value: compare_brancht returns an object of class "list" containing 296 | #' two members. 297 | #' 298 | #' The first member is a data-frame and consists of the following columns: 299 | #' \code{test_name} 300 | #' \code{metric_name} 301 | #' \code{status} 302 | #' \code{metric_val} 303 | #' \code{message} 304 | #' \code{sha} 305 | #' \code{date_time} 306 | #' \code{directory} 307 | #' 308 | #' The second member is a data-frame and consists of the following columns: 309 | #' \code{common_datetime} 310 | #' \code{common_message} 311 | #' \code{cnum_b1} 312 | #' \code{cnum_b2} 313 | #' 314 | #' @section Warning: 315 | #' Function assumes the current directory to be the parent directory of the 316 | #' the repository being tested. 317 | 318 | compare_PR <- function(test_path, metric = "time") { 319 | 320 | # Obtain remote 321 | targetdir_1 <- "Rperform_copy" 322 | remote <- git2r::remote_url(repo = git2r::repository(path = targetdir_1))[1] 323 | 324 | # Clone master branch as a directory 325 | git2r::clone(url = remote, local_path = "./master", branch = "master") 326 | targetdir_2 <- "master" 327 | 328 | # Compare the two directories 329 | dir_list <- compare_dir(targetdir_1, targetdir_2, test_path, metric, PR=T) 330 | 331 | unlink(x = "master/", recursive = T, force = T) 332 | 333 | dir_list 334 | } 335 | -------------------------------------------------------------------------------- /R/repo_metrics.R: -------------------------------------------------------------------------------- 1 | ## The below function call results in the listed variables being treated 2 | ## as global variables when the 'check' tool is applied. Here, this results 3 | ## in reduction of NOTEs being returned when R CMD CHECK is applied to the 4 | ## package. 5 | ## Sample NOTE: 6 | ## mem_compate: no visible binding for global variable ‘mem_result’ 7 | utils::globalVariables(c("mem_result")) 8 | 9 | 10 | ## ----------------------------------------------------------------------------------------- 11 | 12 | #' Commits' details. 13 | #' 14 | #' Given a repository path and number of commits (n), returns a data frame containing 15 | #' the date, SHA1 values and commit messages of the last n commits in the repo. 16 | #' 17 | #' @param path File-path to the git repository whose commits are to be summarized. 18 | #' @param num_commits Number of commits to be summarized. The default is 20. 19 | #' 20 | #' @examples 21 | #' 22 | #' \dontrun{ 23 | #' ## Example-1 24 | #' 25 | #' # Set the current directory to the git repository concerned. 26 | #' setwd("./Path/to/repository") 27 | #' 28 | #' # Obtained details of the last 10 commits in the repository 29 | #' list_commits(num_commits = 10) 30 | #' 31 | #' ## Example-2 32 | #' 33 | #' # Obtained the details of the last 20 (default value) commits in the repository 34 | #' # specified by path. 35 | #' list_commits(path) 36 | #' } 37 | #' 38 | 39 | # The list_commits function, given a repository path and number of commits (n), 40 | # returns a data frame containing the dates, SHA1 values and summary of the last 41 | # n commits in the repo. 42 | 43 | list_commits <- function(path = "./", num_commits = 20){ 44 | stopifnot(is.character(path)) 45 | stopifnot(length(path) == 1) 46 | stopifnot(is.numeric(num_commits)) 47 | stopifnot(length(num_commits) == 1) 48 | num_commits <- floor(num_commits) 49 | 50 | target <- git2r::repository(path) 51 | 52 | commit_list <- git2r::commits(target, n = num_commits) 53 | sha_list <- list() 54 | msg_list <- list() 55 | date_list <- list() 56 | 57 | for (i in 1:num_commits) { 58 | com <- commit_list[[i]]$sha 59 | msg <- commit_list[[i]]$summary 60 | com_date <- commit_list[[i]]$author$when 61 | 62 | sha_list[i] <- com 63 | msg_list[i] <- msg 64 | date_list[i] <- com_date 65 | } 66 | 67 | as.data.frame(cbind(msg_list, date_list, sha_list), stringsAsFactors = F) 68 | } 69 | 70 | ## ----------------------------------------------------------------------------------------- 71 | ## TIME CHUNK BEGINS ## 72 | ## ----------------------------------------------------------------------------------------- 73 | 74 | #' Test file's run-time. 75 | #' 76 | #' Given a test-file's path, measures its run-time against the commit specified by the 77 | #' commit \code{object} passed as a parameter. 78 | #' 79 | #' @param test_path File-path for the test file whose run-time is to be checked. 80 | #' @param test_commit git2r commit \code{object} corresponding to which the 81 | #' run-time is to be checked. 82 | #' 83 | #' @examples 84 | # 85 | #' \dontrun{ 86 | #' ## Example-1 87 | #' 88 | #' # Set the current directory to the git repository concerned. 89 | #' setwd("./Path/to/repository") 90 | #' 91 | #' # Obtain the commit object 92 | #' commit_list <- git2r::commits() 93 | #' t_commit <- commit_list[[1]] 94 | #' 95 | #' # Specify the test-file path 96 | #' t_path <- "Path/to/file" 97 | #' 98 | #' # Pass the parameters and obtain the run-time details 99 | #' library(Rperform) 100 | #' time_commit(t_path, t_commit) 101 | #' } 102 | #' 103 | #' @section Value: 104 | #' time_commit returns an object of class "data.frame". 105 | #' The data-frame consists of the following columns: 106 | #' \code{test_name} 107 | #' \code{metric_name} 108 | #' \code{status} 109 | #' \code{metric_val} 110 | #' \code{message} 111 | #' \code{sha} 112 | #' \code{date_time} 113 | #' 114 | #' @section Warning: 115 | #' Function assumes the current directory to be the root directory of the 116 | #' package being tested. 117 | #' 118 | #' @seealso \code{\link[git2r]{commits}} 119 | 120 | # The time_commit function, given a test-file path, checks its run-time details 121 | # against the specified commit in the current git repository. 122 | 123 | time_commit <- function(test_path, test_commit) { 124 | 125 | stopifnot(is.character(test_path)) 126 | stopifnot(length(test_path) == 1) 127 | stopifnot(!is.null(test_commit)) 128 | stopifnot(git2r::is_commit(test_commit)) 129 | 130 | # Get the meta-information from the commit 131 | sha_val <- get_sha(test_commit) 132 | msg_val <- get_msg(test_commit) 133 | commit_dtime <- get_datetime(test_commit) 134 | # Create the tempfiles 135 | t_lines <- readLines(test_path) 136 | q_lines <- sub("test_that(", "testthatQuantity(", t_lines, fixed=TRUE) 137 | 138 | # These lines of code allow us to load and attach the testthat library 139 | # (in case the test file uses it) without having to explicitly doing so 140 | # in our source code. We do so by appending a conditional statement to 141 | # the contents of the relevant file. This statement loads the testthat 142 | # library if installed. 143 | concat_string <- "if(requireNamespace(\"testthat\", quietly = TRUE)) { 144 | require(testthat)\n }\n" 145 | t_lines <- c(concat_string, t_lines) 146 | q_lines <- c(concat_string, q_lines) 147 | 148 | temp_file_original <- tempfile() 149 | temp_file_subbed <- tempfile() 150 | writeLines(t_lines, temp_file_original) 151 | writeLines(q_lines, temp_file_subbed) 152 | 153 | target <- git2r::repository("./") 154 | # Reverting to the current branch on exit from the function 155 | ###################################################################### 156 | original_state <- git2r::repository_head(target) 157 | git2r::checkout(test_commit) 158 | on.exit(expr = git2r::checkout(original_state)) 159 | ###################################################################### 160 | test_results <- list() 161 | 162 | # Loads the functions from the repository for the package to be tested 163 | suppressPackageStartupMessages(devtools::load_all(file.path("./"))) 164 | 165 | 166 | # Code block measuring the run-time for the test file as a whole 167 | # -------------------------------------------------------------- 168 | 169 | # require(testthat) 170 | file_status = "pass" 171 | # We have used tryCatch so that execution doesn't stop in case of an error 172 | # in the test file. Rather we will modify the values in the result data frame 173 | # (time as NA, status as 'fail') to let the user know of the error. 174 | seconds_file <- tryCatch(expr = { 175 | if(requireNamespace('microbenchmark')){ 176 | times <- microbenchmark::microbenchmark(test = { 177 | base::source(temp_file_original, local = T) 178 | }, times = 3) 179 | times$time/1e9 180 | } else { 181 | replicate(3, { 182 | time_vec <- system.time( { 183 | source(temp_file_original, local = T) 184 | } ) 185 | time_vec[["elapsed"]] 186 | }) 187 | } 188 | }, 189 | error = function(e){ 190 | file_status = "fail" 191 | NA 192 | } 193 | ) 194 | 195 | # --------------------------------------------------------------- 196 | 197 | # Code block measuring the run-time of the testthat code blocks (if present) 198 | # -------------------------------------------------------------------------- 199 | 200 | testthatQuantity <- function(test_name, code){ 201 | e <- parent.frame() 202 | code_subs <- substitute(code) 203 | run <- function(){ 204 | testthat:::test_code(test_name, code_subs, env=e) 205 | } 206 | status = "pass" 207 | # We have used tryCatch so that execution doesn't stop in case of an error 208 | # in a testthat block. Rather we modify the values in the result data frame 209 | # (time as NA, status as 'fail') to let the user know of the error. 210 | seconds <- tryCatch(expr = { 211 | if(requireNamespace('microbenchmark')){ 212 | times <- microbenchmark::microbenchmark(test = { 213 | run() 214 | }, times = 3) 215 | times$time/1e9 216 | } else { 217 | replicate(3, { 218 | time_vec <- system.time( { 219 | run() 220 | } ) 221 | time_vec[["elapsed"]] 222 | }) 223 | } 224 | }, 225 | error = function(e){ 226 | status = "fail" 227 | NA 228 | } 229 | ) 230 | 231 | time_df <- data.frame(test_name, metric_name = "runtime (in seconds)", status, 232 | metric_val = seconds, message = msg_val, 233 | sha = sha_val, date_time = commit_dtime) 234 | test_results[[test_name]] <<- time_df 235 | } 236 | 237 | source(temp_file_subbed, local = T) 238 | 239 | # -------------------------------------------------------------------------- 240 | 241 | 242 | # Formatting the output 243 | # -------------------------------------------------------------------------- 244 | 245 | test_results_df <- do.call(rbind, test_results) 246 | # test_results_df["file runtime"] <- seconds_file 247 | # test_results_df["file runtime-2"] <- seconds_file2 248 | test_results_df <- rbind(test_results_df, data.frame(test_name = basename(test_path), 249 | metric_name = "runtime (in seconds)", status = file_status, 250 | metric_val = seconds_file, message = msg_val, 251 | sha = sha_val, date_time = commit_dtime)) 252 | rownames(test_results_df) <- NULL 253 | test_results_df 254 | 255 | } 256 | 257 | ## ----------------------------------------------------------------------------------------- 258 | ## ----------------------------------------------------------------------------------------- 259 | 260 | 261 | #' Run-time across versions. 262 | #' 263 | #' Given a test-file path, checks its run-time against the specified number of commits 264 | #' in the current git repository and returns a data-frame comprised of the test name, 265 | #' status of test run, time (if successful) and SHA1 value corresponding to the commit 266 | #' the value is for. 267 | #' 268 | #' @param test_path File-path of the test-file which is to be used for run-time 269 | #' comparisons. 270 | #' @param num_commits Number of commits (versions) against which the file is to 271 | #' be tested, with default being 10. 272 | #' 273 | #' @examples 274 | #' 275 | #' \dontrun{ 276 | #' ## Example-1 277 | #' 278 | #' # Set the current directory to the git repository concerned. 279 | #' setwd("./Path/to/repository") 280 | #' 281 | #' # Specify the test-file path 282 | #' t_path <- "Path/to/file" 283 | #' 284 | #' # Pass the parameters and obtain the run-time details against 10 commits 285 | #' library(Rperform) 286 | #' time_compare(test_path = t_path, num_commits = 10) 287 | #' } 288 | #' 289 | #' @section Value: 290 | #' time_compare returns an object of class "data.frame". 291 | #' The data-frame consists of the following columns: 292 | #' \code{test_name} 293 | #' \code{metric_name} 294 | #' \code{status} 295 | #' \code{metric_val} 296 | #' \code{message} 297 | #' \code{sha} 298 | #' \code{date_time} 299 | #' 300 | #' @section Warning: 301 | #' Function assumes the current directory to be the root directory of the 302 | #' package being tested. 303 | #' 304 | 305 | # The time_compare function, given a test-file path, checks its run-time against 306 | # the specified number of commits in the current git repository and returns a 307 | # data-frame comprised of the test name, status of test run, runtime in seconds 308 | # (if successful), datetime and message corresponding to the commit the value is 309 | # for. 310 | 311 | time_compare <- function(test_path, num_commits = 10) { 312 | stopifnot(is.character(test_path)) 313 | stopifnot(length(test_path) == 1) 314 | stopifnot(is.numeric(num_commits)) 315 | stopifnot(length(num_commits) == 1) 316 | num_commits <- floor(num_commits) 317 | 318 | target <- git2r::repository("./") 319 | commit_list <- git2r::commits(target, n = num_commits) 320 | result_list <- list() 321 | 322 | for(commit_i in seq_along(commit_list)){ 323 | one_commit <- commit_list[[commit_i]] 324 | suppressMessages(result_list[[commit_i]] <- time_commit(test_path, one_commit)) 325 | } 326 | 327 | test_results <- do.call(rbind, result_list) 328 | test_results 329 | } 330 | 331 | ## ----------------------------------------------------------------------------------------- 332 | ## MEMORY CHUNK BEGINS ## 333 | ## ----------------------------------------------------------------------------------------- 334 | 335 | #' Test-file's memory statistics. 336 | #' 337 | #' Given a test-file's path, checks its memory metrics against the commit 338 | #' specified by the commit \code{object} passed as a parameter. Memory 339 | #' metrics returned are the memory leaked and maximum memory utilized during 340 | #' its execution. 341 | #' 342 | #' @param test_path File-path for the test file which is to be checked. 343 | #' @param test_commit git2r commit \code{object} corresponding to which the 344 | #' memory stats are to be checked. 345 | #' 346 | #' @examples 347 | #' 348 | #' \dontrun{ 349 | #' ## Example-1 350 | #' 351 | #' # Set the current directory to the git repository concerned. 352 | #' setwd("./Path/to/repository") 353 | #' 354 | #' # Obtain the commit object 355 | #' commit_list <- git2r::commits() 356 | #' t_commit <- commit_list[[1]] 357 | #' 358 | #' # Specify the test-file path 359 | #' t_path <- "Path/to/file" 360 | #' 361 | #' # Pass the parameters and obtain the memory stats 362 | #' library(Rperform) 363 | #' mem_commit(t_path, t_commit) 364 | #' } 365 | #' 366 | #' @section Value: 367 | #' mem_commit returns an object of class "data.frame". 368 | #' The data-frame consists of the following columns: 369 | #' \code{test_name} 370 | #' \code{metric_name} 371 | #' \code{status} 372 | #' \code{metric_val} 373 | #' \code{message} 374 | #' \code{sha} 375 | #' \code{date_time} 376 | #' 377 | #' @section Warning: 378 | #' Function assumes the current directory to be the root directory of the 379 | #' package being tested. 380 | #' 381 | #' @seealso \code{\link[git2r]{commits}} 382 | 383 | # The mem_commit function, given a test-file path, checks its memory details, 384 | # more specifically the memory leaked and maximum memory utilized during its 385 | # execution. It does so against the specified commit in the current git 386 | # repository. 387 | 388 | mem_commit <- function(test_path, test_commit) { 389 | stopifnot(is.character(test_path)) 390 | stopifnot(length(test_path) == 1) 391 | stopifnot(!is.null(test_commit)) 392 | stopifnot(git2r::is_commit(test_commit)) 393 | 394 | ## Creating the tempfiles 395 | target <- git2r::repository("./") 396 | sha_val <- get_sha(test_commit) 397 | msg_val <- get_msg(test_commit) 398 | commit_dtime <- get_datetime(test_commit) 399 | t_lines <- readLines(test_path) 400 | q_lines <- sub("test_that(", "testthatQuantity(", t_lines, fixed=TRUE) 401 | 402 | # These lines of code allow us to load and attach the testthat library 403 | # (in case the test file uses it) without having to explicitly doing so 404 | # in our source code. We do so by appending a conditional statement to 405 | # the contents of the relevant file. This statement loads the testthat 406 | # library if installed. 407 | concat_string <- "if(requireNamespace(\"testthat\", quietly = TRUE)) { 408 | require(testthat)\n }\n" 409 | t_lines <- c(concat_string, t_lines) 410 | q_lines <- c(concat_string, q_lines) 411 | 412 | temp_file_original <- tempfile() 413 | temp_file_subbed <- tempfile() 414 | writeLines(t_lines, temp_file_original) 415 | writeLines(q_lines, temp_file_subbed) 416 | 417 | ## Git operations 418 | target <- git2r::repository("./") 419 | original_state <- git2r::repository_head(target) 420 | git2r::checkout(test_commit) 421 | on.exit(expr = git2r::checkout(original_state)) 422 | test_results <- list() 423 | testthat_rss_list <- list() 424 | rss_list <- list() 425 | 426 | # Loads the functions from the repository for the package to be tested 427 | suppressPackageStartupMessages(devtools::load_all(file.path("./"))) 428 | 429 | ## Function for obtaining the memory metrics for testthat blocks 430 | # require(testthat) 431 | testthatQuantity <- function(test_name, code){ 432 | e <- parent.frame() 433 | code_subs <- substitute(code) 434 | run <- function(){ 435 | testthat:::test_code(test_name, code_subs, env=e) 436 | } 437 | new_name <- gsub(pattern = " ", replacement = "", x = test_name) 438 | 439 | test_status <- "pass" 440 | # We have used tryCatch so that execution doesn't stop in case of an error 441 | # in a testthat block. Rather we modify the values in the result data frame 442 | # (memories as NA, status as 'fail') to let the user know of the error. 443 | testthat_rss_list <- 444 | tryCatch(expr = { 445 | .rss.profile.start(paste0(new_name, ".RSS")) 446 | run() 447 | .rss.profile.stop(paste0(new_name, ".RSS")) 448 | }, 449 | error = function(e){ 450 | # The below line is required in order to stop the ps process which was started 451 | # by .rss.profile.start earlier. In case of an error, the .rss.profile.stop 452 | # function in the above code-block won't be executed resulting in an infinite 453 | # loop. (Check out /R/mem_operations.R for better understanding.) 454 | .rss.profile.stop(paste0(new_name, ".RSS")) 455 | 456 | test_status <- "fail" 457 | list(max_mem = NA, leak = NA) 458 | } 459 | ) 460 | 461 | testthat_maxmem_df <- data.frame(test_name, metric_name = "max_mem", status = test_status, 462 | metric_val = testthat_rss_list$max_mem/1000, 463 | message = msg_val, sha = sha_val, date_time = commit_dtime) 464 | testthat_leak_df <- data.frame(test_name, metric_name = "leak_mem", status = test_status, 465 | metric_val = testthat_rss_list$leak/1000, 466 | message = msg_val, sha = sha_val, date_time = commit_dtime) 467 | 468 | test_results[[test_name]] <<- rbind(testthat_maxmem_df, testthat_leak_df) 469 | } 470 | 471 | # source(temp_file_subbed, local = TRUE) 472 | 473 | ## Obtaining the memory metrics for the file 474 | file_name <- basename(test_path) 475 | file_status <- "pass" 476 | rss_list <- 477 | tryCatch(expr = { 478 | .rss.profile.start(paste0(file_name, ".RSS")) 479 | source(temp_file_subbed, local = TRUE) 480 | .rss.profile.stop(paste0(file_name, ".RSS")) 481 | }, 482 | error = function(e) { 483 | file_status <- "fail" 484 | list(max_mem = NA, leak = NA) 485 | } 486 | ) 487 | # Check /R/mem_operations.R for source code for the functions .rss.profile.* 488 | 489 | testfile_maxmem_df <- data.frame(test_name = file_name, metric_name = "max_mem", 490 | status = file_status, metric_val = rss_list$max_mem/1000, 491 | message = msg_val, sha = sha_val, date_time = commit_dtime) 492 | testfile_leak_df <- data.frame(test_name = file_name, metric_name = "leak_mem", 493 | status = file_status, metric_val = rss_list$leak/1000, 494 | message = msg_val, sha = sha_val, date_time = commit_dtime) 495 | 496 | #Formatting the result dataframe 497 | testfile_df <- rbind(testfile_maxmem_df, testfile_leak_df) 498 | testthat_df <- do.call(rbind, test_results) 499 | mem_df <- rbind(testthat_df, testfile_df) 500 | rownames(mem_df) <- NULL 501 | mem_df 502 | } 503 | 504 | ## ----------------------------------------------------------------------------------------- 505 | ## ----------------------------------------------------------------------------------------- 506 | 507 | ## TO GET MEMORY DETAILS FOR MULTIPLE COMMITS ## 508 | 509 | #' Test-file's memory statistics. 510 | #' 511 | #' Given a test-file's path, checks its memory metrics against the commit 512 | #' specified by the commit number passed as a parameter. Memory metrics returned 513 | #' are the memory leaked and maximum memory utilized during its execution. A 514 | #' commit number,n, would correspond to the nth commit in the commit log of the 515 | #' current git repository. 516 | #' 517 | #' @param test_path File-path for the test file which is to be checked. 518 | #' @param commit_num commit number in the git log for the current git repository 519 | #' against which the memory stats are to be checked, with the commit number 520 | #' for the most recent commit being 1. 521 | #' 522 | #' @examples 523 | #' 524 | #' \dontrun{ 525 | #' ## Example-1 526 | #' 527 | #' # Set the current directory to the git repository concerned. 528 | #' setwd("./Path/to/repository") 529 | #' 530 | #' # Specify the test-file path 531 | #' t_path <- "Path/to/file" 532 | #' 533 | #' # Pass the parameters and obtain the memory stats 534 | #' library(Rperform) 535 | #' get_mem(t_path, 3) 536 | #' } 537 | #' 538 | #' @section Value: 539 | #' get_mem returns an object of class "data.frame". 540 | #' The data-frame consists of the following columns: 541 | #' \code{test_name} 542 | #' \code{metric_name} 543 | #' \code{status} 544 | #' \code{metric_val} 545 | #' \code{message} 546 | #' \code{sha} 547 | #' \code{date_time} 548 | #' 549 | #' @section Warning: 550 | #' Function assumes the current directory to be the root directory of the 551 | #' package being tested. 552 | #' 553 | 554 | # The get_mem function, given a test-file path, checks its memory details, more 555 | # specifically the memory leaked and maximum memory utilized during its 556 | # execution. It does so against the nth commit in the current git repository, n 557 | # being the commit_num parameter (with default value equal to 1). 558 | 559 | get_mem <- function(test_path, commit_num = 1) { 560 | stopifnot(is.character(test_path)) 561 | stopifnot(length(test_path) == 1) 562 | stopifnot(is.numeric(commit_num)) 563 | stopifnot(length(commit_num) == 1) 564 | commit_num <- floor(commit_num) 565 | 566 | target <- git2r::repository("./") 567 | target_commit <- git2r::commits(target)[[commit_num]] 568 | result_list <- list() 569 | 570 | test_results <- mem_commit(test_path, target_commit) 571 | test_results 572 | } 573 | 574 | ## ----------------------------------------------------------------------------------------- 575 | 576 | ## ----------------------------------------------------------------------------------------- 577 | 578 | #' Test-file's memory statistics for multiple commits. 579 | #' 580 | #' Given a test-file's path, checks its memory metrics against the number of 581 | #' commits specified by the parameter num_commits with default being 10. Memory 582 | #' metrics returned are the memory leaked and maximum meory utilized during its 583 | #' execution. 584 | #' 585 | #' @param test_path File-path for the test file which is to be checked. 586 | #' @param num_commits number of commits against all of which the memory stats 587 | #' are to be checked starting from the most recent one. 588 | #' 589 | #' @examples 590 | #' 591 | #' \dontrun{ 592 | #' ## Example-1 593 | #' 594 | #' # Set the current directory to the git repository concerned. 595 | #' setwd("./Path/to/repository") 596 | #' 597 | #' # Specify the test-file path 598 | #' t_path <- "Path/to/file" 599 | #' 600 | #' # Pass the parameters and obtain the run-time details 601 | #' library(Rperform) 602 | #' mem_compare(t_path, 10) 603 | #' } 604 | #' 605 | #' @section Value: 606 | #' time_commit returns an object of class "data.frame". 607 | #' The data-frame consists of the following columns: 608 | #' \code{test_name} 609 | #' \code{metric_name} 610 | #' \code{status} 611 | #' \code{metric_val} 612 | #' \code{message} 613 | #' \code{sha} 614 | #' \code{date_time} 615 | #' 616 | #' @section Warning: 617 | #' Function assumes the current directory to be the root directory of the 618 | #' package being tested. 619 | #' 620 | 621 | # The mem_compare function, given a test-file path, returns its memory details. 622 | # Specifically it obtains the values for memory leaked and maximum memory 623 | # utilized during the file's execution. It does so against the specified number 624 | # of commits from the git log in the current git repository. 625 | 626 | mem_compare <- function(test_path, num_commits = 10) { 627 | stopifnot(is.character(test_path)) 628 | stopifnot(length(test_path) == 1) 629 | stopifnot(is.numeric(num_commits)) 630 | stopifnot(length(num_commits) == 1) 631 | num_commits <- floor(num_commits) 632 | 633 | script.R <- system.file("exec", "get_mem.R", package="Rperform") 634 | # Check out the code for above script in /exec/get_mem.R 635 | Rscript <- file.path(R.home("bin"), "Rscript") 636 | result_list <- list() 637 | 638 | for (commit_i in 1:num_commits) { 639 | cmd <- paste(Rscript, script.R, test_path, 640 | as.character(commit_i)) 641 | for (test_t in 1:3) { 642 | system(cmd) 643 | load("mem_result.RData") 644 | result_list[[paste0(commit_i, as.character(test_t))]] <- mem_result 645 | } 646 | } 647 | 648 | system("rm *RSS*") 649 | system("rm mem_result.RData") 650 | do.call(what = rbind, args = result_list) 651 | 652 | } 653 | 654 | ## ----------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /R/plot_metrics.R: -------------------------------------------------------------------------------- 1 | ## The below function call results in the listed variables being treated 2 | ## as global variables when the 'check' tool is applied. Here, this results 3 | ## in reduction of NOTEs being returned when R CMD CHECK is applied to the 4 | ## package. 5 | ## Sample NOTE: 6 | ## plot_bmemory: no visible binding for global variable ‘metric_val’ 7 | utils::globalVariables(c("metric_val", "test_name")) 8 | 9 | #' Plot test-file metrics across versions. 10 | #' 11 | #' Given a test-file path, plot the metrics of entire file and individual 12 | #' testthat blocks against the commit message summaries of the specified number 13 | #' of commits in the current git repository. If the parameter save_data is set 14 | #' to true, it also stores the corresponding data-frames in an RData file in a 15 | #' folder 'Rperform_Data' in the current directory.The metrics plotted are in 16 | #' accordance with those specified using the parameter metric. 17 | #' 18 | #' @param test_path File-path of the test-file which is to be used for run-time 19 | #' comparisons. 20 | #' @param metric Type of plot(s) desired. This can be set to \code{time}, 21 | #' \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 22 | #' for more details) 23 | #' @param num_commits Number of commits (versions) against which the file is to 24 | #' be tested, with default being 5. 25 | #' @param save_data If set to TRUE, the data frame containing the metrics 26 | #' information is stored in the 'Rperform_Data' directory in the root of the 27 | #' repo. (default set to FALSE) 28 | #' @param save_plots If set to TRUE, the plots generated are stored in the 29 | #' 'Rperform_plots' directory in the root of the repo rather than being 30 | #' printed. (default set to TRUE) 31 | #' @param interactive If set to TRUE, the plots generated are interactive. The 32 | #' resulting plot is rendered in the default browser. 33 | #' 34 | #' @examples 35 | #' 36 | #' \dontrun{ 37 | #' # Set the current directory to the git repository concerned. 38 | #' setwd("./Path/to/repository") 39 | #' 40 | #' # Specify the test-file path 41 | #' t_path <- "Path/to/file" 42 | #' 43 | #' # Load the library 44 | #' library(Rperform) 45 | #' 46 | #' ## Example-1 47 | #' 48 | #' # Pass the parameters and obtain the run-time followed by memory details against 10 commits 49 | #' plot_metrics(test_path = t_path, metric = "time", n_commits = 10, save_data = F) 50 | #' plot_metrics(test_path = t_path, metric = "memory", n_commits = 10, save_data = F) 51 | #' 52 | #' ## Example-2 53 | #' 54 | #' # Obtain both memory and time metrics for each individual testthat block 55 | #' # inside a file and the file itself. The plots get stored in a directory 56 | #' # 'Rperform_testMetrics' in the repo's root directory. 57 | #' plot_metrics(test_path = t_path, metric = "testMetrics", n_commits = 5, save_data = F) 58 | #' } 59 | #' 60 | #' @section WARNING: 61 | #' Function assumes the current directory to be the root directory of the 62 | #' repository/package being tested. 63 | #' 64 | 65 | plot_metrics <- function(test_path, metric, num_commits = 5, save_data = FALSE, save_plots = FALSE, 66 | interactive = FALSE){ 67 | stopifnot(is.character(test_path)) 68 | stopifnot(length(test_path) == 1) 69 | stopifnot(is.character(metric)) 70 | stopifnot(length(metric) == 1) 71 | stopifnot(is.numeric(num_commits)) 72 | stopifnot(length(num_commits) == 1) 73 | stopifnot(is.logical(save_data)) 74 | stopifnot(length(save_data) == 1) 75 | stopifnot(is.logical(save_plots)) 76 | stopifnot(length(save_plots) == 1) 77 | floor(num_commits) 78 | 79 | if (metric == "time") { 80 | if (interactive) { 81 | temp_out <- capture.output(.plot_interactive_time(test_path, num_commits, save_data, save_plots)) 82 | } else { 83 | temp_out <- capture.output(.plot_time(test_path, num_commits, save_data, save_plots)) 84 | } 85 | } 86 | else if (metric == "memory") { 87 | if (interactive) { 88 | temp_out <- capture.output(.plot_interactive_mem(test_path, num_commits, save_data, save_plots)) 89 | } else { 90 | temp_out <- capture.output(.plot_mem(test_path, num_commits, save_data, save_plots)) 91 | } 92 | } 93 | else if (metric == "memtime") { 94 | if (interactive) { 95 | temp_out <- capture.output(.plot_interactive_time(test_path, num_commits, save_data, save_plots)) 96 | temp_out <- capture.output(.plot_interactive_mem(test_path, num_commits, save_data, save_plots)) 97 | } else { 98 | temp_out <- capture.output(.plot_time(test_path, num_commits, save_data, save_plots)) 99 | temp_out <- capture.output(.plot_mem(test_path, num_commits, save_data, save_plots)) 100 | } 101 | } 102 | else if (metric == "testMetrics") { 103 | if (interactive) { 104 | cat("Interactive mode not available for this metric!\nPrinting static plots instead.") 105 | } 106 | temp_out <- capture.output(.plot_testMetrics(test_path, num_commits, save_data, save_plots)) 107 | } 108 | else { 109 | temp_out <- NULL 110 | print("Input a valid metric parameter!") 111 | } 112 | remove(temp_out) 113 | } 114 | 115 | ## ----------------------------------------------------------------------------------------- 116 | 117 | .plot_testMetrics <- function(test_path, num_commits, save_data, save_plots) { 118 | suppressMessages(mem_data <- mem_compare(test_path, num_commits)) 119 | suppressMessages(time_data <- time_compare(test_path, num_commits)) 120 | 121 | # Store the metrics data if save_data is TRUE 122 | if (save_data){ 123 | 124 | # Store the metric data 125 | .save_data(time_data, pattern = "*.[rR]$", replacement = "_time.RData", 126 | replace_string = basename(test_path)) 127 | .save_data(mem_data, pattern = "*.[rR]$", replacement = "_mem.RData", 128 | replace_string = basename(test_path)) 129 | } 130 | 131 | metric_data <- rbind(time_data, mem_data) 132 | t_names <- levels(metric_data$test_name) 133 | 134 | for (num in seq(t_names)) { 135 | test_frame <- metric_data[metric_data$test_name == t_names[num],] 136 | 137 | tryCatch(expr = {test_plot <- 138 | ggplot2::ggplot(data = test_frame, mapping = ggplot2::aes(message, metric_val)) + 139 | ggplot2::geom_point(color = "blue") + 140 | ggplot2::facet_grid(facets = metric_name ~ ., scales = "free") + 141 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90)) + 142 | ggplot2::scale_x_discrete(limits = rev(levels(test_frame$message))) + 143 | # In the above 5 lines of code, the first line creates the basic qplot. The 144 | # fourth and fifth lines display the x-axis labels at 90 degrees to the 145 | # horizontal and correct the order of message labels on the x -axis, 146 | # respectively. 147 | ggplot2::xlab("Commit message") + 148 | ggplot2::ylab("Metric value") + 149 | ggplot2::ggtitle(label = paste0("Variation in metrics for ", t_names[num])) 150 | 151 | 152 | if (save_plots == TRUE) { 153 | .save_plots(test_plot = test_plot, test_name = t_names[num], metric = "testMetrics") 154 | print(test_plot) 155 | } 156 | else { 157 | print(test_plot) 158 | } 159 | }, 160 | error = function(e) { 161 | print("Encountered an error!") 162 | }) 163 | } 164 | } 165 | 166 | ## ----------------------------------------------------------------------------------------- 167 | 168 | .plot_interactive_time <- function(test_path, num_commits, save_data, save_plots) { 169 | 170 | # Obtain the metrics data 171 | suppressMessages(time_data <- time_compare(test_path, num_commits)) 172 | 173 | # Store the metrics data if save_data is TRUE 174 | if (save_data){ 175 | 176 | # Store the metric data 177 | .save_data(time_data, pattern = "*.[rR]$", replacement = "_time.RData", 178 | replace_string = basename(test_path)) 179 | } 180 | 181 | # Add links to the github page for each commit to data 182 | remoteUrl <- git2r::remote_url(repo = git2r::repository(path = "./")) 183 | remoteUrl <- (paste0(remoteUrl, "/commit/")) 184 | time_data$remoteUrl <- paste0(remoteUrl, time_data$sha) 185 | 186 | levels(time_data$test_name) <- paste0(substr(levels(time_data$test_name), start = 0, stop = 4), 187 | "...", 188 | substr(levels(time_data$test_name), 189 | start = nchar(levels(time_data$test_name)) - 4, 190 | stop = nchar(levels(time_data$test_name)))) 191 | 192 | test_plot <- ggplot2::ggplot() + 193 | ggplot2::geom_point(mapping = ggplot2::aes(x = message, y = metric_val, 194 | href = remoteUrl), 195 | color = "blue", 196 | data = time_data) + 197 | ggplot2::theme(axis.text.x = ggplot2::element_blank()) + 198 | ggplot2::facet_grid(facets = test_name~., scales = "free") + 199 | ggplot2::scale_x_discrete(limits = rev(levels(time_data$message))) + 200 | ggplot2::xlab("Commit message") + 201 | ggplot2::ylab("Runtime value") + 202 | ggplot2::ggtitle(label = paste0("Variation in runtime for ", basename(test_path))) 203 | 204 | if (length(levels(time_data$test_name)) > 6) { 205 | test_plot <- test_plot + 206 | animint2::theme_animint(height = 700) 207 | } 208 | else if (length(levels(time_data$test_name)) > 3) { 209 | test_plot <- test_plot + 210 | animint2::theme_animint(height = 650) 211 | } 212 | 213 | viz.list <- list(timeplot = test_plot) 214 | 215 | print("Loaded animint") 216 | animint2::animint2dir(plot.list = viz.list, out.dir = paste0(basename(getwd()), "_", "time_animint")) 217 | unlink(x = paste0(basename(getwd()), "_", "time_animint"), recursive = T, force = T) 218 | } 219 | 220 | ## ----------------------------------------------------------------------------------------- 221 | 222 | 223 | .plot_time <- function(test_path, num_commits, save_data, save_plots) { 224 | # Obtain the metrics data 225 | suppressMessages(time_data <- time_compare(test_path, num_commits)) 226 | # Store the metrics data if save_data is TRUE 227 | if (save_data){ 228 | 229 | # Store the metric data 230 | .save_data(time_data, pattern = "*.[rR]$", replacement = "_time.RData", 231 | replace_string = basename(test_path)) 232 | } 233 | 234 | curr_name <- gsub(pattern = " ", replacement = "_", x = basename(test_path)) 235 | curr_name <- gsub(pattern = ".[rR]$", replacement = "", x = curr_name) 236 | 237 | # Plot the metric data 238 | tryCatch(expr = {test_plot <- 239 | ggplot2::ggplot() + 240 | ggplot2::geom_point(mapping = ggplot2::aes(message, metric_val), 241 | data = time_data, color = "blue") + 242 | ggplot2::facet_grid(facets = test_name ~ ., scales = "free") + 243 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90)) + 244 | ggplot2::scale_x_discrete(limits = rev(levels(time_data$message))) + 245 | # In the above 5 lines of code, the first line creates the basic qplot. The 246 | # fourth and fifth lines display the x-axis labels at 90 degrees to the 247 | # horizontal and correct the order of message labels on the x -axis, 248 | # respectively. 249 | ggplot2::xlab("Commit message") + 250 | ggplot2::ylab("Time (in seconds)") + 251 | ggplot2::ggtitle(label = paste0("Variation in time metrics for ", curr_name)) 252 | 253 | if (save_plots == TRUE) { 254 | .save_plots(test_plot = test_plot, test_name = curr_name, metric = "time", 255 | width = 1600, height = 1200) 256 | print(test_plot) 257 | } 258 | else { 259 | print(test_plot) 260 | } 261 | }, 262 | error = function(e){ 263 | print("Encountered an error!") 264 | }) 265 | 266 | } 267 | 268 | ## ----------------------------------------------------------------------------------------- 269 | 270 | .plot_interactive_mem <- function(test_path, num_commits, save_data, save_plots) { 271 | 272 | # Obtain the metrics data 273 | suppressMessages(mem_data <- mem_compare(test_path, num_commits)) 274 | 275 | # Store the metrics data if save_data is TRUE 276 | if (save_data){ 277 | 278 | # Store the metric data 279 | .save_data(mem_data, pattern = "*.[rR]$", replacement = "_mem.RData", 280 | replace_string = basename(test_path)) 281 | } 282 | 283 | # Add links to the github page for each commit to data 284 | remoteUrl <- git2r::remote_url(repo = git2r::repository(path = "./")) 285 | remoteUrl <- (paste0(remoteUrl, "/commit/")) 286 | mem_data$remoteUrl <- paste0(remoteUrl, mem_data$sha) 287 | 288 | levels(mem_data$test_name) <- paste0(substr(levels(mem_data$test_name), start = 0, stop = 4), 289 | "...", 290 | substr(levels(mem_data$test_name), 291 | start = nchar(levels(mem_data$test_name)) - 4, 292 | stop = nchar(levels(mem_data$test_name)))) 293 | 294 | test_plot <- ggplot2::ggplot() + 295 | ggplot2::geom_point(mapping = ggplot2::aes(x = message, y = metric_val, 296 | href = remoteUrl), 297 | color = "blue", 298 | data = mem_data) + 299 | ggplot2::theme(axis.text.x = ggplot2::element_blank()) + 300 | ggplot2::facet_grid(facets = test_name~metric_name, scales = "free") + 301 | ggplot2::scale_x_discrete(limits = rev(levels(mem_data$message))) + 302 | ggplot2::xlab("Commit message") + 303 | ggplot2::ylab("Memory usage (in mb)") + 304 | ggplot2::ggtitle(label = paste0("Variation in memory usage for ", basename(test_path))) 305 | 306 | if (length(levels(mem_data$test_name)) > 6) { 307 | test_plot <- test_plot + 308 | animint2::theme_animint(height = 700) 309 | } 310 | else if (length(levels(mem_data$test_name)) > 3) { 311 | test_plot <- test_plot + 312 | animint2::theme_animint(height = 650) 313 | } 314 | 315 | viz.list <- list(memplot = test_plot) 316 | 317 | print("Loaded animint") 318 | animint2::animint2dir(plot.list = viz.list, out.dir = paste0(basename(getwd()), "_", "mem_animint")) 319 | unlink(x = paste0(basename(getwd()), "_", "mem_animint"), recursive = T, force = T) 320 | } 321 | 322 | ## ----------------------------------------------------------------------------------------- 323 | 324 | .plot_mem <- function(test_path, num_commits, save_data, save_plots) { 325 | 326 | # Obtain the metrics data 327 | suppressMessages(mem_data <- mem_compare(test_path, num_commits)) 328 | 329 | # Store the metrics data if save_data is TRUE 330 | if (save_data){ 331 | 332 | # Store the metric data 333 | .save_data(mem_data, pattern = "*.[rR]$", replacement = "_mem.RData", 334 | replace_string = basename(test_path)) 335 | } 336 | 337 | curr_name <- gsub(pattern = " ", replacement = "_", x = basename(test_path)) 338 | curr_name <- gsub(pattern = ".[rR]$", replacement = "", x = curr_name) 339 | 340 | tryCatch(expr = {test_plot <- 341 | ggplot2::ggplot(data = mem_data, mapping = ggplot2::aes(message, metric_val)) + 342 | ggplot2::geom_point(color = "blue") + 343 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90), 344 | strip.text.x = ggplot2::element_text(size = 10, face = "bold")) + 345 | ggplot2::scale_x_discrete(limits = rev(levels(mem_data$message))) + 346 | ggplot2::facet_grid(test_name ~ metric_name, scales = "free") + 347 | # In the above 5 lines of code, the first line creates the basic qplot. The 348 | # third and fourth lines display the x-axis labels at 90 degrees to the 349 | # horizontal and correct the order of message labels on the x -axis, 350 | # respectively. The fourth line creates a facet grid so as to seperate 351 | # the plots for the max memory and leak memory metrics. 352 | ggplot2::ylab(label = "Memory (in Mb)") + 353 | ggplot2::xlab(label = "Commit messages") + 354 | ggplot2::ggtitle(label = paste0("Variation in memory metrics for ", curr_name)) 355 | 356 | if (save_plots == TRUE) { 357 | .save_plots(test_plot = test_plot, test_name = curr_name, metric = "memory") 358 | print(test_plot) 359 | } 360 | else { 361 | print(test_plot) 362 | } 363 | }, 364 | error = function(e) { 365 | print("Encountered an error!") 366 | }) 367 | 368 | } 369 | 370 | ## ----------------------------------------------------------------------------------------- 371 | ## ----------------------------------------------------------------------------------------- 372 | 373 | #' Plot the specified metrics of all test files in a specified directory on a 374 | #' webpage. 375 | #' 376 | #' It plots specified metrics for all the tests present in the specified 377 | #' directory of the current git repository on a webpage. 378 | #' 379 | #' @param test_directory Path of the directory containing the test files. 380 | #' @param metric Type of plot(s) desired. This can be set to \code{time}, 381 | #' \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 382 | #' for more details) 383 | #' @param output_name Name of the output .html file. 384 | #' 385 | #' @examples 386 | #' 387 | #' \dontrun{ 388 | #' # Set to the git repository in consideration. 389 | #' setwd("path/to/repo") 390 | #' d_path <- "path/to/tests" 391 | #' 392 | #' # Load the library 393 | #' library(Rperform) 394 | #' 395 | #' ## Example-1 396 | #' 397 | #' # Pass the parameters and obtain the run-time followed by memory details against 10 commits 398 | #' # on two seperate webpages (html files). 399 | #' plot_webpage(test_directory = d_path, metric = "time", output_name = "timePage") 400 | #' plot_metrics(test_directory = d_path, metric = "memory", output_name = "memPage") 401 | #' 402 | #' ## Example-2 403 | #' 404 | #' # Obtain both memory and time metrics for each individual testthat block 405 | #' # inside a file and the file itself. 406 | #' plot_webpage(d_path, metric = "testMetrics", output_name = "testMetricsPage") 407 | #' } 408 | #' 409 | #' @section WARNING: 410 | #' Function assumes the current directory to be the root directory of the 411 | #' repository being tested. 412 | #' 413 | 414 | plot_webpage <- function(test_directory = "tests/testthat", metric = "testMetrics", 415 | output_name = "RperformTest.html"){ 416 | stopifnot(is.character(test_directory)) 417 | stopifnot(is.character(output_name)) 418 | stopifnot(is.character(metric)) 419 | stopifnot(length(test_directory) == 1) 420 | stopifnot(length(output_name) == 1) 421 | stopifnot(length(metric) == 1) 422 | 423 | out_file <- paste0(output_name, ".Rmd") 424 | 425 | if(!file.exists(out_file)){ 426 | file.create(out_file) 427 | } 428 | 429 | line_p1 <- "---\ntitle: \"plot\"\noutput: html_document\n---\n\n```{r}\nRperform::plot_directory(\"" 430 | line_p3 <- "\", metric = \"" 431 | line_p5 <- "\", save_plots = FALSE)\n```" 432 | file_lines <- paste0(line_p1, test_directory, line_p3, metric, line_p5) 433 | writeLines(file_lines, con = out_file) 434 | rmarkdown::render(input = out_file, output_format = "html_document", output_file = output_name) 435 | } 436 | 437 | ## ----------------------------------------------------------------------------------------- 438 | ## ----------------------------------------------------------------------------------------- 439 | 440 | #' Plot metrics across versions for all files in a given directory. 441 | #' 442 | #' Given a directory path, plot the memory and time usage statistics of all files 443 | #' in the directory against the commit message summaries of the specified number 444 | #' of commits in the current git repository. 445 | #' 446 | #' 447 | #' @param test_directory Directory containing the test-files which are to be used. 448 | #' @param metric Type of plot(s) desired. This can be set to \code{time}, 449 | #' \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 450 | #' for more details) 451 | #' @param num_commits Number of commits (versions) against which the files are to 452 | #' be tested, with default being 5. 453 | #' @param save_data If set to TRUE, the metrics data is saved in a folder 'Rperform_Data' 454 | #' in the current directory. 455 | #' @param save_plots If set to TRUE, the plots generated are stored in the 456 | #' 'Rperform_plots' directory in the root of the repo rather than being 457 | #' printed. 458 | #' 459 | #' @examples 460 | #' 461 | #' \dontrun{ 462 | #' # Set to the git repository in consideration. 463 | #' setwd("path/to/repo") 464 | #' d_path <- "path/to/tests" 465 | #' 466 | #' # Load the library 467 | #' library(Rperform) 468 | #' 469 | #' ## Example-1 470 | #' 471 | #' # Pass the parameters and obtain the run-time followed by memory details against 10 commits. 472 | #' plot_directory(test_directory = d_path, metric = "time", num_commits = 10, 473 | #' save_data = F, save_plots = T) 474 | #' plot_directory(test_directory = d_path, metric = "memory", num_commits = 10, 475 | #' save_data = F, save_plots = T) 476 | #' 477 | #' ## Example-2 478 | #' 479 | #' # Obtain both memory and time metrics for each individual testthat block 480 | #' # inside a file and the file itself ,and save the resulting plot as well as 481 | #' # data. 482 | #' plot_directory(d_path, metric = "testMetrics", num_commits = 5, save_data = F, 483 | #' save_plots = T) 484 | #' } 485 | #' 486 | #' @section WARNING: 487 | #' Library assumes the current directory to be the root directory of the 488 | #' package being tested. 489 | #' 490 | 491 | ## The plot_directory function, given a test-directory path, plots the time 492 | ## taken by all the test files present inside the directory (including those of 493 | ## the individual testthat blocks) against the corresponding commit messages for 494 | ## the given number of commits. 495 | 496 | plot_directory <- function(test_directory, metric = "testMetrics", num_commits = 5, save_data = FALSE, 497 | save_plots = TRUE) { 498 | stopifnot(is.character(test_directory)) 499 | stopifnot(is.character(metric)) 500 | stopifnot(is.numeric(num_commits)) 501 | stopifnot(is.logical(save_data)) 502 | stopifnot(is.logical(save_plots)) 503 | stopifnot(length(test_directory) == 1) 504 | stopifnot(length(metric) == 1) 505 | stopifnot(length(save_data) == 1) 506 | stopifnot(length(save_plots) == 1) 507 | floor(num_commits) 508 | 509 | file_names <- list.files(test_directory) 510 | 511 | # For each file, plots for both time and space metrics are plotted and stored 512 | # in the folder Rperform_Graphs in png format 513 | for (file_i in seq_along(file_names)) { 514 | 515 | # Print the plots as per the metric parameter. 516 | plot_metrics(test_path = file.path(test_directory, file_names[file_i]), 517 | metric = metric, num_commits = num_commits, 518 | save_data = save_data, save_plots = save_plots) 519 | 520 | } 521 | } 522 | 523 | ## ----------------------------------------------------------------------------------------- 524 | ## ----------------------------------------------------------------------------------------- 525 | ## BRANCH FUNCTIONS 526 | ## ----------------------------------------------------------------------------------------- 527 | ## ----------------------------------------------------------------------------------------- 528 | 529 | #' Plot and compare different versions of test files across two branches. 530 | #' 531 | #' Given a test-file path and two branches, plot and compare the metrics of the 532 | #' file across the two branches. The chosen metric values are plotted against the 533 | #' commit message summaries. For the first branch, metrics are plotted upto the 534 | #' latest common commit of both the branches. For the second branch, metrics for 535 | #' only the latest commit are plotted, which may or may not be the latest common 536 | #' commit. If the parameter save_data is set 537 | #' to true, it also stores the corresponding data-frames in an RData file in a 538 | #' folder 'Rperform_Data' in the current directory.The metrics plotted are in 539 | #' accordance with those specified using the parameter metric. 540 | #' 541 | #' 542 | #' @param test_path File-path of the test-file which is to be used for run-time 543 | #' comparisons. 544 | #' @param metric Type of plot(s) desired. This can be set to \code{time}, 545 | #' \code{memory}, \code{memtime} or \code{testMetrics}. (See examples below 546 | #' for more details) 547 | #' @param branch1 Name of the first branch whose commits are to be analyzed. 548 | #' @param branch2 Name of the second branch whose commits are to be analyzed. 549 | #' This is supposedly the branch into which branch1 is to be merged and its 550 | #' default value is set to 'master'. 551 | #' @param save_data If set to TRUE, the data frame containing the metrics 552 | #' information is stored in the 'Rperform_Data' directory in the root of the 553 | #' repo. (default set to FALSE) 554 | #' @param save_plots If set to TRUE, the plots generated are stored in the 555 | #' 'Rperform_plots' directory in the root of the repo rather than being 556 | #' printed. (default set to TRUE) 557 | #' 558 | #' @section NOTE: 559 | #' This function can be helpful when analyzing how would merging a branch into 560 | #' the master branch would affect performance. 'branch2' is assumed to be the 561 | #' branch into which 'branch1' is to be merged. 562 | #' This function is useful only when branch1 had branched off from branch2 at some 563 | #' point, that is they have at least one common commit. 564 | #' 565 | #' @examples 566 | #' 567 | #' \dontrun{ 568 | #' # Set the current directory to the git repository concerned. 569 | #' setwd("./Path/to/repository") 570 | #' 571 | #' # Specify the test-file path 572 | #' t_path <- "Path/to/file" 573 | #' 574 | #' # Load the library 575 | #' library(Rperform) 576 | #' 577 | #' ## Example-1 578 | #' 579 | #' # Pass the parameters and obtain the run-time details for branches, 'experiment' and 580 | #' 'master'. 581 | #' # Since branch2 is not specified in this case, it's assumed to be 'master'. 582 | #' plot_branchmetrics(test_path = t_path, metric = "time", branch1 = 'experiment', save_data = F) 583 | #' 584 | #' # Pass the parameters and obtain the memory details. 585 | #' plot_branchmetrics(test_path = t_path, metric = 'memory', 586 | #' branch1 = 'experiment_1', branch2 = 'experiment_2') 587 | #' 588 | #' ## Example-2 589 | #' 590 | #' # Obtain both memory and time metrics for each individual testthat block 591 | #' # inside a file, and those for the file itself. Same as the other metric 592 | #' # cases, metrics both the commits are plotted. The plots get stored in a 593 | #' # directory 'Rperform_testMetrics' in the repo's root directory. 594 | #' plot_metrics(test_path = t_path, metric = "testMetrics", branch1 = "experiment") 595 | #' } 596 | #' 597 | #' @section WARNING: 598 | #' Function assumes the current directory to be the root directory of the 599 | #' repository/package being tested. 600 | #' 601 | 602 | 603 | plot_branchmetrics <- function(test_path, metric, branch1, branch2 = "master", 604 | save_data = FALSE, save_plots = TRUE) { 605 | stopifnot(is.character(test_path)) 606 | stopifnot(length(test_path) == 1) 607 | stopifnot(is.character(branch1)) 608 | stopifnot(length(branch1) == 1) 609 | stopifnot(is.character(branch2)) 610 | stopifnot(length(branch2) == 1) 611 | stopifnot(is.character(metric)) 612 | stopifnot(length(metric) == 1) 613 | stopifnot(is.logical(save_data)) 614 | stopifnot(length(save_data) == 1) 615 | stopifnot(is.logical(save_plots)) 616 | stopifnot(length(save_plots) == 1) 617 | 618 | if (metric == "time") { 619 | temp_out <- capture.output(.plot_btime(test_path, branch1, branch2, save_data, save_plots)) 620 | } 621 | else if (metric == "memory") { 622 | temp_out <- capture.output(.plot_bmem(test_path, branch1, branch2, save_data, save_plots)) 623 | } 624 | else if (metric == "memtime") { 625 | temp_out <- capture.output(.plot_btime(test_path, branch1, branch2, save_data, save_plots)) 626 | temp_out <- capture.output(.plot_bmem(test_path, branch1, branch2, save_data, save_plots)) 627 | } 628 | else if (metric == "testMetrics") { 629 | temp_out <- capture.output(.plot_btestMetrics(test_path, branch1, branch2, save_data, save_plots)) 630 | } 631 | else { 632 | temp_out <- NULL 633 | print("Enter valid metric!") 634 | } 635 | remove(temp_out) 636 | } 637 | 638 | ## ----------------------------------------------------------------------------------------- 639 | 640 | .plot_btestMetrics <- function(test_path, branch1, branch2, save_data, save_plots) { 641 | 642 | suppressMessages(time_data <- compare_brancht(test_path = test_path, 643 | branch1 = branch1, branch2 = branch2)) 644 | suppressMessages(mem_data <- compare_branchm(test_path = test_path, 645 | branch1 = branch1, branch2 = branch2)) 646 | suppressMessages(same_commit <- .common_commit(branch1 = branch1, branch2 = branch2)) 647 | 648 | # Store the metrics data if save_data is TRUE 649 | if (save_data){ 650 | 651 | # Store the metric data 652 | .save_data(time_data, pattern = "*.[rR]$", 653 | replacement = paste0("_", branch1, "_", branch2, "_time.RData"), 654 | replace_string = basename(test_path)) 655 | .save_data(mem_data, pattern = "*.[rR]$", 656 | replacement = paste0("_", branch1, "_", branch2, "_mem.RData"), 657 | replace_string = basename(test_path)) 658 | } 659 | 660 | metric_data <- rbind(time_data, mem_data) 661 | t_names <- levels(metric_data$test_name) 662 | 663 | for(num in seq(t_names)) { 664 | test_frame <- metric_data[metric_data$test_name == t_names[num],] 665 | extremes_frame <- .find_midvals(data = test_frame) 666 | 667 | tryCatch(expr = {test_plot <- 668 | ggplot2::ggplot(data = test_frame, mapping = ggplot2::aes(message, metric_val)) + 669 | ggplot2::geom_point(color = "blue") + 670 | ggplot2::facet_grid(facets = metric_name ~ ., scales = "free") + 671 | ggplot2::geom_text(data = extremes_frame, 672 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.3, 673 | y = mid_val, 674 | label = branch2, angle = 90)) + 675 | ggplot2::geom_text(data = extremes_frame, 676 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.7, 677 | y = mid_val, 678 | label = branch1, angle = -90)) + 679 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90)) + 680 | ggplot2::geom_vline(mapping = ggplot2::aes(xintercept = same_commit$cnum_b2 + 0.5)) + 681 | ggplot2::scale_x_discrete(limits = rev(levels(test_frame$message))) + 682 | # In the above 6 lines of code, the first line creates 683 | # the basic qplot. The fourth and sixth lines display the 684 | # x-axis labels at 90 degrees to the horizontal and 685 | # correct the order of message labels on the x -axis, 686 | # respectively. The fifth line plots a vertical seperator between 687 | # the commit from branch2 and the commits from branch1. 688 | ggplot2::xlab("Commit message") + 689 | ggplot2::ylab("Metric value") + 690 | ggplot2::ggtitle(label = paste0("Variation in metrics for ", t_names[num])) 691 | 692 | curr_name <- paste0(branch1, "_", branch2, "_", t_names[num]) 693 | 694 | if (save_plots == TRUE) { 695 | .save_plots(test_plot = test_plot, test_name = t_names[num], 696 | metric = "testMetrics") 697 | print(test_plot) 698 | } else { 699 | print(test_plot) 700 | } 701 | }, 702 | error = function(e){ 703 | print("Encountered an error!") 704 | }) 705 | } 706 | 707 | } 708 | 709 | ## ----------------------------------------------------------------------------------------- 710 | 711 | 712 | .plot_btime <- function(test_path, branch1, branch2, save_data, save_plots) { 713 | 714 | suppressMessages(time_data <- compare_brancht(test_path = test_path, 715 | branch1 = branch1, branch2 = branch2)) 716 | suppressMessages(same_commit <- .common_commit(branch1 = branch1, branch2 = branch2)) 717 | # same_commit 718 | # --------------------------------------------- 719 | # common_datetime, cnum_b1, cnum_b2 720 | 721 | # Store the metrics data if save_data is TRUE 722 | if (save_data) { 723 | .save_data(metric_frame = time_data, pattern = "*.[rR]$", 724 | replacement = paste0("_", branch1, "_", branch2, "_time.RData"), 725 | replace_string = basename(test_path)) 726 | } 727 | 728 | curr_name <- gsub(pattern = " ", replacement = "_", x = basename(test_path)) 729 | curr_name <- gsub(pattern = "*.[rR]$", replacement = paste0("_", branch1, "_", branch2), 730 | x = curr_name) 731 | 732 | # Trying to find the min and max vals for each test 733 | ################################################### 734 | 735 | extremes_frame <- .find_midvals(data = time_data) 736 | ## extremes_frame 737 | ## test_name | max_val | min_val | mid_val 738 | ## ---------------------------------------------------------- 739 | 740 | ################################################### 741 | 742 | # Plot the branches' metric data 743 | tryCatch(expr = {test_plot <- 744 | ggplot2::ggplot(data = time_data, mapping = ggplot2::aes(message, metric_val)) + 745 | ggplot2::geom_point(color = "blue") + 746 | ggplot2::facet_grid(test_name ~ ., scales = "free") + 747 | ggplot2::geom_text(data = extremes_frame, 748 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.3, 749 | y = mid_val, 750 | label = branch2, angle = 90)) + 751 | ggplot2::geom_text(data = extremes_frame, 752 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.7, 753 | y = mid_val, 754 | label = branch1, angle = -90)) + 755 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90)) + 756 | ggplot2::geom_vline(mapping = ggplot2::aes(xintercept = same_commit$cnum_b2 + 0.5)) + 757 | ggplot2::scale_x_discrete(limits = rev(levels(time_data$message))) + 758 | # In the above 8 lines of code, the first line creates 759 | # the basic plot. The sixth and eigth lines display the 760 | # x-axis labels at 90 degrees to the horizontal and 761 | # correct the order of message labels on the x -axis, 762 | # respectively. The seventh line plots a vertical seperator between 763 | # the commit from branch2 and the commits from branch1. 764 | ggplot2::xlab(label = "Commit messages") + 765 | ggplot2::ylab(label = "Time (in seconds)") + 766 | ggplot2::ggtitle(label = paste0("Variation in time metrics across branches ", 767 | branch2, " and ", branch1)) 768 | 769 | if (save_plots == TRUE) { 770 | .save_plots(test_plot = test_plot, test_name = curr_name, metric = "time", 771 | width = 1600, height = 900) 772 | print(test_plot) 773 | } 774 | else { 775 | print(test_plot) 776 | } 777 | }, 778 | error = function(e){ 779 | print("Encountered an error!") 780 | }) 781 | 782 | } 783 | 784 | ## ----------------------------------------------------------------------------------------- 785 | 786 | .plot_bmem <- function(test_path, branch1, branch2, save_data, save_plots) { 787 | 788 | suppressMessages(mem_data <- compare_branchm(test_path = test_path, 789 | branch1 = branch1, branch2 = branch2)) 790 | suppressMessages(same_commit <- .common_commit(branch1 = branch1, branch2 = branch2)) 791 | # same_commit 792 | # --------------------------------------------- 793 | # common_datetime, cnum_b1, cnum_b2 794 | 795 | # Store the metrics data if save_data is TRUE 796 | if (save_data) { 797 | .save_data(metric_frame = mem_data, pattern = "*.[rR]$", 798 | replacement = paste0("_", branch1, "_", branch2, "_mem.RData"), 799 | replace_string = basename(test_path)) 800 | } 801 | 802 | curr_name <- gsub(pattern = " ", replacement = "_", x = basename(test_path)) 803 | curr_name <- gsub(pattern = "*.[rR]$", replacement = paste0("_", branch1, "_", branch2), 804 | x = curr_name) 805 | 806 | # Trying to find the min and max vals for each test 807 | ################################################### 808 | 809 | extremes_frame <- .find_midvals(data = mem_data) 810 | ## extremes_frame 811 | ## test_name | max_val | min_val | mid_val 812 | ## ---------------------------------------------------------- 813 | 814 | ################################################### 815 | 816 | # Plot the branches' metric data 817 | tryCatch(expr = {test_plot <- 818 | ggplot2::ggplot(data = mem_data, mapping = ggplot2::aes(message, metric_val)) + 819 | ggplot2::geom_point(color = "blue") + 820 | ggplot2::facet_grid(test_name ~ metric_name, scales = "free") + 821 | ggplot2::geom_text(data = extremes_frame, 822 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.3, 823 | y = mid_val, 824 | label = branch2, angle = 90)) + 825 | ggplot2::geom_text(data = extremes_frame, 826 | mapping = ggplot2::aes(x = same_commit$cnum_b2 + 0.7, 827 | y = mid_val, 828 | label = branch1, angle = -90)) + 829 | ggplot2::theme(axis.text.x = ggplot2::element_text(angle = -90), 830 | strip.text.x = ggplot2::element_text(size = 10, face = "bold")) + 831 | ggplot2::geom_vline(mapping = ggplot2::aes(xintercept = same_commit$cnum_b2 + 0.5)) + 832 | ggplot2::scale_x_discrete(limits = rev(levels(mem_data$message))) + 833 | # In the above 8 lines of code, the first line creates 834 | # the basic plot. The sixth and eigth lines display the 835 | # x-axis labels at 90 degrees to the horizontal and 836 | # correct the order of message labels on the x -axis, 837 | # respectively. The seventh line plots a vertical seperator between 838 | # the commit from branch2 and the commits from branch1. 839 | ggplot2::xlab(label = "Commit messages") + 840 | ggplot2::ylab(label = "Memory (in Mb") + 841 | ggplot2::ggtitle(label = paste0("Variation in memory metrics across branches ", 842 | branch2, " and ", branch1)) 843 | 844 | if (save_plots == TRUE) { 845 | .save_plots(test_plot = test_plot, test_name = curr_name, metric = "memory") 846 | print(test_plot) 847 | } 848 | else { 849 | print(test_plot) 850 | } 851 | }, 852 | error = function(e){ 853 | print("Encountered an error!") 854 | }) 855 | 856 | } 857 | 858 | ## ----------------------------------------------------------------------------------------- 859 | 860 | 861 | ## ----------------------------------------------------------------------------------------- 862 | ## ----------------------------------------------------------------------------------------- 863 | 864 | .save_data <- function(metric_frame, pattern = "*.[rR]$", replacement, replace_string) { 865 | 866 | # Create a directory for storing the metric data 867 | if (!dir.exists("./Rperform_Data")){ 868 | dir.create(path = "./Rperform_Data") 869 | } 870 | 871 | if(grepl(pattern = "time", x = replacement) > 0) { 872 | time_frame <- metric_frame 873 | save(time_frame, file = file.path("Rperform_Data", sub(pattern = pattern, 874 | replacement = replacement, 875 | x = basename(replace_string)))) 876 | } 877 | else if(grepl(pattern = "mem", x = replacement) > 0){ 878 | mem_frame <- metric_frame 879 | save(mem_frame, file = file.path("Rperform_Data", sub(pattern = pattern, 880 | replacement = replacement, 881 | x = basename(replace_string)))) 882 | } 883 | } 884 | 885 | ## ----------------------------------------------------------------------------------------- 886 | 887 | .save_plots <- function(test_plot, test_name, metric, width = 1024, height = 768, units = "px") { 888 | 889 | if (metric == "time") { 890 | if(!dir.exists(paths = "./Rperform_timeMetrics")) { 891 | dir.create(path = "./Rperform_timeMetrics") 892 | } 893 | target_dir <- "Rperform_timeMetrics" 894 | } 895 | else if (metric == "memory") { 896 | if(!dir.exists(paths = "./Rperform_memoryMetrics")) { 897 | dir.create(path = "./Rperform_memoryMetrics") 898 | } 899 | target_dir <- "Rperform_memoryMetrics" 900 | } 901 | else if (metric == "testMetrics") { 902 | if(!dir.exists(paths = "./Rperform_testMetrics")) { 903 | dir.create(path = "./Rperform_testMetrics") 904 | } 905 | target_dir <- "Rperform_testMetrics" 906 | } 907 | 908 | curr_name <- gsub(pattern = " ", replacement = "_", x = test_name) 909 | curr_name <- gsub(pattern = ".[rR]$", replacement = "", curr_name) 910 | png.file <- file.path(target_dir, paste0("Test_", curr_name, ".png")) 911 | grDevices::png(filename = png.file, width = width, height = height, units = units) 912 | print(test_plot) 913 | grDevices::dev.off() 914 | } 915 | 916 | ## ----------------------------------------------------------------------------------------- 917 | 918 | .find_midvals <- function(data) { 919 | extremes_list <- list() 920 | t_names <- as.character(unique(data$test_name)) 921 | m_names <- as.character(unique(data$metric_name)) 922 | for (test_num in seq(t_names)) { 923 | for (metric_num in seq(m_names)) { 924 | max_val <- max(data[data$test_name == t_names[test_num] & data$metric_name == m_names[metric_num], 925 | "metric_val"]) 926 | min_val <- min(data[data$test_name == t_names[test_num] & data$metric_name == m_names[metric_num], 927 | "metric_val"]) 928 | mid_val <- (max_val + min_val) / 2 929 | extremes_list[[paste0(t_names[test_num], m_names[metric_num])]] <- 930 | (data.frame(test_name = t_names[test_num], 931 | metric_name = m_names[metric_num], 932 | mid_val = mid_val)) 933 | } 934 | } 935 | 936 | extremes_frame <- do.call(rbind, extremes_list) 937 | row.names(extremes_frame) <- NULL 938 | 939 | extremes_frame 940 | } 941 | --------------------------------------------------------------------------------