# Examples and use-cases

#### Rich FitzJohn

#### 2018-11-08

## Using a database in testing

This example requires the `redux` package, which is available on CRAN and can be installed with

    install.packages("redux")

    docker <- stevedore::docker_client()

    redis <- docker$container$run("redis", name = "redis", ports = "6379",
                                  detach = TRUE, rm = TRUE)

We now have a Redis server running on 32775

    redis$ports()

    ##   container_port protocol host_ip host_port
    ## 1           6379      tcp 0.0.0.0     32775

Make a connection to the Redis server:

    con <- redux::hiredis(port = redis$ports()$host_port)

and we can communicate with the Redis server:

    con$PING()

    ## [Redis: PONG]

Because this is a brand new container we can write data without worrying about clobbering data that already exists:

    con$SET("key", "hello redis")

    ## [Redis: OK]

    con$GET("key")

    ## [1] "hello redis"

And we can get a fresh copy of Redis by simply starting a new copy of redis

    redis$kill()

    ## NULL

    redis <- docker$container$run("redis", name = "redis", ports = "6379",
                                  detach = TRUE, rm = TRUE)
    con <- redux::hiredis(port = redis$ports()$host_port)
    con$KEYS("*")

    ## list()

    redis$kill()

    ## NULL

The same approach works for other database that might be large or awkward to install, such as Postgres or MySQL.

## Testing shiny apps

(this section is not actually run because doing anything with it requires looking in a web browser)

The rocker project has a shiny container that provides an easy to use version of the shiny server. If we have a directory `app` that contains a shiny application we can map this with:

    volumes <- sprintf("%s:%s", normalizePath("app"), "/srv/shiny-server/")

(see the shiny docker image documentation for the destination path.

We can start this with

    shiny <- docker$container$run("rocker/shiny", name = "shiny", ports = "3838",
                                  volumes = volumes,
                                  detach = TRUE, rm = TRUE)

In an interactive session, you can visit the shiny server:

    browseURL(sprintf("http://localhost:%s", shiny$ports()$host_port))

## Package testing

You can build an image that contains all the bits required to test your package and then run tests using that image. This ensures that you’re running in a totally clean environment (and that you really know what all the dependencies of your package are)

For this we have a Dockerfile, which contains

    FROM r-base

    RUN apt-get update && \
            apt-get install -yy \
                    git \
                    libcurl4-openssl-dev \
                    libssl-dev \
                    qpdf

    RUN install2.r --error \
            curl \
            knitr \
            openssl \
            rcmdcheck \
            rmarkdown \
            rcorpora \
            testthat \
            uuid

    COPY tester.sh /usr/local/bin
    RUN chmod +x /usr/local/bin/tester.sh

    ENTRYPOINT ["/usr/local/bin/tester.sh"]

And the script `tester.sh` (copied into the docker image above) which contains:

    lang_output(readLines("tester/tester.sh"), "shell")

    ## ```shell
    ## #!/usr/bin/env bash
    ## set -e
    ##
    ## if [[ "$#" -ne 1 ]]; then
    ##     echo "Expected one argument"
    ##     exit 1
    ## fi
    ## SRC=$1
    ##
    ## if echo $SRC | grep -q "^https://"; then
    ##     DEST=$(mktemp -d)
    ##     git clone $SRC $DEST
    ##     SRC=$DEST
    ## fi
    ##
    ## if [ ! -f "$SRC/DESCRIPTION" ]; then
    ##     echo "Did not find a DESCRIPTION" file
    ##     exit 1
    ## fi
    ##
    ## PACKAGE=$(grep '^Package:' "$SRC/DESCRIPTION" | \
    ##                  sed -E 's/^Package:[[:space:]]+//')
    ## R CMD build $SRC
    ## PACKAGE_TGZ=$(ls -1tr ${PACKAGE}*gz | tail -n1)
    ##
    ## export _R_CHECK_CRAN_INCOMING_=FALSE
    ## Rscript -e "rcmdcheck::rcmdcheck('$PACKAGE_TGZ', args = c('--as-cran', '--no-manual'))"
    ## ```

    img <- docker$image$build("tester", tag = "richfitz/tester")

    ## Step 1/6 : FROM r-base
    ##  ---> e4a158184cc5
    ## Step 2/6 : RUN apt-get update &&         apt-get install -yy                 git                 libcurl4-openssl-dev                 libssl-dev                 qpdf
    ##  ---> Using cache
    ##  ---> ef239ce55ffe
    ## Step 3/6 : RUN install2.r --error         curl         knitr         openssl         rcmdcheck         rmarkdown         rcorpora         testthat         uuid
    ##  ---> Using cache
    ##  ---> 412ea6a1c8a4
    ## Step 4/6 : COPY tester.sh /usr/local/bin
    ##  ---> Using cache
    ##  ---> 079439ae1bae
    ## Step 5/6 : RUN chmod +x /usr/local/bin/tester.sh
    ##  ---> Using cache
    ##  ---> 9d56dca452f9
    ## Step 6/6 : ENTRYPOINT ["/usr/local/bin/tester.sh"]
    ##  ---> Using cache
    ##  ---> 68e1b48c1b2c
    ## Successfully built 68e1b48c1b2c
    ## Successfully tagged richfitz/tester:latest

With this image we can then test packages off github:

    invisible(docker$container$run(img, "https://github.com/richfitz/ids",
                                   rm = TRUE, stream = stdout()))

    ## E> Cloning into '/tmp/tmp.3r6OVHu5N6'...
    ## O> * checking for file ‘/tmp/tmp.3r6OVHu5N6/DESCRIPTION’ ... OK
    ## O> * preparing ‘ids’:
    ## O> * checking DESCRIPTION meta-information ... OK
    ## O> * installing the package to build vignettes
    ## O> * creating vignettes ... OK
    ## O> * checking for LF line-endings in source and make files and shell scripts
    ## O> * checking for empty or unneeded directories
    ## O> * looking to see if a ‘data/datalist’ file should be added
    ## O> * building ‘ids_1.0.1.tar.gz’
    ## E>
    ## O> ── R CMD check ─────────────────────────────────────────────────────────────────
    ## O>
    ─  using log directory ‘/tmp/Rtmp9phSht/file3f69a6c616/ids.Rcheck’
    ## O> ─  using R version 3.5.1 (2018-07-02)
    ## O>

    ─  using platform: x86_64-pc-linux-gnu (64-bit)
    ## O> ─  using session charset: UTF-8
    ## O>

    ─  using options ‘--no-manual --as-cran’
    ## O>

    ✔  checking for file ‘ids/DESCRIPTION’
    ## O> ─  this is package ‘ids’ version ‘1.0.1’
    ## O>    checking package namespace information ...

    ✔  checking package namespace information
    ## O>    checking package dependencies ...

    ✔  checking package dependencies (1.2s)
    ## O>    checking if this is a source package ...

    ✔  checking if this is a source package
    ## O> ✔  checking if there is a namespace
    ## O>

    ✔  checking for .dll and .exe files
    ## O> ✔  checking for hidden files and directories
    ## O>    checking for portable file names ...

    ✔  checking for portable file names
    ## O> ✔  checking for sufficient/correct file permissions
    ## O> ✔  checking serialization versions
    ## O>    checking whether package ‘ids’ can be installed ...

    ✔  checking whether package ‘ids’ can be installed (854ms)
    ## O>

    ✔  checking installed package size
    ## O>

       checking package directory ...

    ✔  checking package directory
    ## O> ✔  checking ‘build’ directory
    ## O>    checking DESCRIPTION meta-information ...

    ✔  checking DESCRIPTION meta-information
    ## O>

    ✔  checking top-level files
    ## O> ✔  checking for left-over files
    ## O>    checking index information ...

    ✔  checking index information
    ## O>

       checking package subdirectories ...

    ✔  checking package subdirectories
    ## O>

       checking R files for non-ASCII characters ...

    ✔  checking R files for non-ASCII characters
    ## O>

       checking R files for syntax errors ...

    ✔  checking R files for syntax errors
    ## O>

       checking whether the package can be loaded ...

    ✔  checking whether the package can be loaded
    ## O>

       checking whether the package can be loaded with stated dependencies ...

    ✔  checking whether the package can be loaded with stated dependencies
    ## O>

       checking whether the package can be unloaded cleanly ...

    ✔  checking whether the package can be unloaded cleanly
    ## O>

       checking whether the namespace can be loaded with stated dependencies ...

    ✔  checking whether the namespace can be loaded with stated dependencies
    ## O>

       checking whether the namespace can be unloaded cleanly ...

    ✔  checking whether the namespace can be unloaded cleanly
    ## O>

       checking loading without being on the library search path ...

    ✔  checking loading without being on the library search path
    ## O>

       checking dependencies in R code ...

    ✔  checking dependencies in R code
    ## O>

       checking S3 generic/method consistency ...

    ✔  checking S3 generic/method consistency (416ms)
    ## O>

       checking replacement functions ...

    ✔  checking replacement functions
    ## O>

       checking foreign function calls ...

    ✔  checking foreign function calls
    ## O>

       checking R code for possible problems ...

    ✔  checking R code for possible problems (2s)
    ## O>

       checking Rd files ...

    ✔  checking Rd files
    ## O>

       checking Rd metadata ...

    ✔  checking Rd metadata
    ## O>

       checking Rd line widths ...

    ✔  checking Rd line widths
    ## O>

       checking Rd cross-references ...

    ✔  checking Rd cross-references
    ## O>

       checking for missing documentation entries ...

    ✔  checking for missing documentation entries
    ## O>

       checking for code/documentation mismatches ...

    ✔  checking for code/documentation mismatches (347ms)
    ## O>

       checking Rd \usage sections ...

    ✔  checking Rd \usage sections (541ms)
    ## O>

       checking Rd contents ...

    ✔  checking Rd contents
    ## O>

       checking for unstated dependencies in examples ...

    ✔  checking for unstated dependencies in examples
    ## O>

       checking R/sysdata.rda ...

    ✔  checking R/sysdata.rda
    ## O>

       checking installed files from ‘inst/doc’ ...

    ✔  checking installed files from ‘inst/doc’
    ## O>

       checking files in ‘vignettes’ ...

       checking files in ‘vignettes’ ...

    ✔  checking files in ‘vignettes’
    ## O>    checking examples ...

       checking examples ...

    ✔  checking examples (732ms)
    ## O>

       checking for unstated dependencies in ‘tests’ ...

    ✔  checking for unstated dependencies in ‘tests’
    ## O>

       checking tests ...

    ─  checking tests ...
    ## O>

       Running ‘testthat.R’

    ✔  Running ‘testthat.R’
    ## O>

       checking for unstated dependencies in vignettes ...

    ✔  checking for unstated dependencies in vignettes
    ## O>

    ✔  checking package vignettes in ‘inst/doc’
    ## O>

       checking re-building of vignette outputs ...

    ✔  checking re-building of vignette outputs (1.1s)
    ## O>
    ## O>
    ## O>

    ── R CMD check results ────────────────────────────────────────── ids 1.0.1 ────
    ## O> Duration: 12.4s
    ## O>
    ## O> 0 errors ✔ | 0 warnings ✔ | 0 notes ✔

(I have cheated here and put all of the dependencies of `ids` into the docker image via the Dockerfile).

The same approach would work by mounting the package source directory into the container and passing the path (within the container) to `$run()`.