tinytest/0000755000176200001440000000000013543153442012141 5ustar liggesuserstinytest/NAMESPACE0000644000176200001440000000177013543151025013360 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[",tinytests) S3method(as.data.frame,tinytests) S3method(format,tinytest) S3method(print,tinytest) S3method(print,tinytests) S3method(summary,tinytests) export(all_fail) export(all_pass) export(any_fail) export(any_pass) export(at_home) export(build_install_test) export(exit_file) export(expect_equal) export(expect_equivalent) export(expect_error) export(expect_false) export(expect_identical) export(expect_message) export(expect_null) export(expect_silent) export(expect_true) export(expect_warning) export(ignore) export(puppy) export(register_tinytest_extension) export(report_side_effects) export(run_test_dir) export(run_test_file) export(setup_tinytest) export(test_all) export(test_package) export(tinytest) export(using) importFrom(parallel,makeCluster) importFrom(parallel,parLapply) importFrom(parallel,stopCluster) importFrom(utils,capture.output) importFrom(utils,file_test) importFrom(utils,getFromNamespace) importFrom(utils,install.packages) tinytest/README.md0000644000176200001440000001547213530032421013416 0ustar liggesusers### A brief overview of `tinytest` #### Package setup A quick way to set things up is as follows. ``` tinytest::setup_tinytest("pkgdir") ``` where `pkgdir` is a package source directory with a valid `DESCRIPTION` file The setup is as follows. 1. Files having names starting with `test` are in `pkg/inst/tinytest`, e.g. `test_haha.R`. Test files are R scripts interspersed with test commands, such as `expect_equal(myfunc(1), 0)`. 2. `tinytest` is added to `Suggests:` in the `DESCRIPTION` file. 3. A file named `tinytest.R` is set up in `pkg/tests` to make sure that tests will be run by `R CMD check`. A nice way to set up a completely new package that passes `R CMD check` is as follows ``` pkgKitten::kitten("hihi") tinytest::setup_tinytest("hihi") ``` where `hihi` is the name of the new package. #### Interactive package testing | Function | description | |---------------------------------|----------------------------------------------------------| | `test_all("pkgdir")` | run all test files (pkg must be loaded). | | `build_install_test("pkgdir")` | build, install, and test in temp dir. | | `run_test_dir("pkgdir")` | run all test files in a directory (pkg must be loaded). | | `run_test_file("testfile")` | run a single test file (pkg must be loaded). | All functions return an object of class `tinytests`. Results can be printed to screen, summarized with `summary` or converted to data frame with `as.data.frame` for analyses. The option `verbose` (default: `2`) controls showing test progress in the terminal. #### Test functions The syntax of test functions resembles that of [testthat](https://CRAN.R-project.org/package=testthat). For expectations comparing two results, the first argument represents the _observed_ value while the second argument represents the _desired_ value. |Function | description | |-----------------------------|-----------------------------------------------| | `expect_true` | Argument must evaluate to `TRUE` | | `expect_false` | Argument must evaluate to `FALSE` | | `expect_equal` | Data and attributes of arguments must be equal| | `expect_equivalent` | Data of arguments must be equal | | `expect_identical` | Target and current must be `identical` | | `expect_null` | Expression must evaluate to `NULL` | | `expect_message` | Expression must yield a message | | `expect_warning` | Expression must yield a warning | | `expect_error` | Expression must yield an error | | `expect_silent` | Expect no errors, no warnings | For tests in a script there is an alternative syntax in the style of [RUnit](https://CRAN.R-project.org/package=RUnit). For each function of the form `expect_lol` there is a function of the form `checkLol`. #### Monitor side-effects Side-effects, such as changing environment variables or changing the working directory can cause hard-to-trace bugs. Add the statement ``` report_side_effects() ``` to a test file and certain types of side-effects, if any, are reported. Alternatively, use the `side_effects` argument to any of the test runners, for example ``` test_all("/path/to/package", side_effects=TRUE) ``` #### Print options Test results (objects of class `tinytests`) have two printing modes: a long format and a short, one-line format. Information that is always shown includes: - File name and line number of failed test. - The test call that resulted in test failure. - The type of failure. This can be 'data' (for differences in variable content), 'attr' (for differences in attributes like column names), or 'xcpt' for exceptions (warnings, errors). In long format, the test call and difference between desired and realized input are shown in full. Global printing options can be set with `options(option=value)`. |Option | default | description | |---------------|----------|-------------------------------| | `tt.pr.passes`| FALSE | print passing tests? | | `tt.pr.limit` | 10 | how many results to print? | | `tt.pr.nlong` | 3 | how many tests in long format?| | `tt.pr.color` | TRUE | print colored output? | It is also possible to influence these options using `print.tinytest`. Colored output is suppressed on systems with a [`"dumb"`](https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals) terminal. #### Run tests for an installed package For a package called `haha` that is tested with `tinytest`, any user that has `haha` and `tinytest` installed can run tests as follows. ``` tinytest::test_package("haha") ``` #### Run tests in parallel Run tests in parallel over files. ``` tinytest::test_package("haha", ncpu=3) ``` Or, for more control: ``` cl <- parallel::makeCluster(4) parallel::clusterCall(cl, source, "R/functions.R") test_all(cluster=cl) stopCluster(cl) ``` #### Use extension packages Add the following to a test file to use assertions exported by `checkmate.tinytest`. ``` using(checkmate.tinytest) ``` #### Skipping or ignoring tests Use `exit_file()` to stop executing a test file, with an optional message. ``` exit_file("I'm too tired to test today") ``` Use `ignore(testfunction)` to run a test but not include the result in the output. ``` # both tests run, but only second is recorded. if ( ignore(expect_equal)(1 + 1, 2) ){ expect_true( 1 > 0 ) } ``` Note the placement of brackets. Use `at_home()` to detect whether a test is running interactively, or via `test_package()` (i.e. the way `R CMD check` will run it). ``` if ( at_home() ){ # run tests requiring lots of time. } ``` The package vignette has some tips on how to use this feature, and how you can set up your package so `R CMD check` also runs tests protected by `at_home()` in your environment. #### Comparing with data stored on file Data can be loaded from `pkg/inst/tinytest` (or subdirectories). A simple test file might look like this. ``` desired <- read.csv("mycsvoutput.csv", stringsAsFactors=FALSE) obtained <- compute_my_result() expect_equal(obtained, desired) ``` If you wish to publish the package on CRAN, make sure that the files are small enough for the package to be acceptable. See the [cran repository policy](https://cran.r-project.org/web/packages/policies.html) for explicit bounds on package size. Alternatively you can avoid installing the data and associated test files by adding them to [.Rbuildignore](https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Building-package-tarballs). #### More information See the vignette. ``` vignette("using_tinytest", package="tinytest") ``` tinytest/man/0000755000176200001440000000000013530032425012705 5ustar liggesuserstinytest/man/tinytests.Rd0000644000176200001440000000611013525334051015244 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/methods.R \name{summary.tinytests} \alias{summary.tinytests} \alias{all_pass} \alias{any_pass} \alias{all_fail} \alias{any_fail} \alias{[.tinytests} \alias{tinytests} \alias{print.tinytests} \alias{as.data.frame.tinytests} \title{Tinytests object} \usage{ \method{summary}{tinytests}(object, ...) all_pass(x) any_pass(x) all_fail(x) any_fail(x) \method{[}{tinytests}(x, i) \method{print}{tinytests}(x, passes = getOption("tt.pr.passes", FALSE), sidefx = getOption("tt.pr.sidefx", TRUE), limit = getOption("tt.pr.limit", 7), nlong = getOption("tt.pr.nlong", 3), ...) \method{as.data.frame}{tinytests}(x, ...) } \arguments{ \item{object}{a \code{tinytests} object} \item{...}{passed to \code{\link{format.tinytest}}} \item{x}{a \code{tinytests} object} \item{i}{an index} \item{passes}{\code{[logical]} Toggle: print passing tests?} \item{sidefx}{\code{[logical]} Toggle: print side effects?} \item{limit}{\code{[numeric]} Max number of results to print} \item{nlong}{\code{[numeric]} First \code{nlong} results are printed in long format.} } \value{ For \code{summary} a \code{\link{table}} object For \code{all_pass}, \code{any_pass}, \code{all_fail}, \code{any_fail}: a single \code{logical} For \code{`[.tinytests`} a \code{tinytests} object. For \code{as.data.frame.} a data frame. } \description{ An object of class \code{tinytests} (note: plural) results from running multiple tests from script. E.g. by running \code{\link{run_test_file}}. } \section{Details}{ By default, the first 3 failing test results are printed in long form, the next 7 failing test results are printed in short form and all other failing tests are not printed. These defaults can be changed by passing options to \code{print.tinytest}, or by setting one or more of the following global options: \itemize{ \item{\code{tt.pr.passes} Set to \code{TRUE} to print output of non-failing tests.} \item{\code{tt.pr.limit} Max number of results to print (e.g. \code{Inf})} \item{\code{tt.pr.nlong} The number of results to print in long format (e.g. \code{Inf}).} } For example, set \code{options(tt.pr.limit=Inf)} to print all test results. Furthermore, there is the option \itemize{ \item{\code{tt.pr.color},} } which determines whether colored output is printed. If R is running in a dumb terminal (detected by comparing environment variable \code{"TERM"} to \code{"dumb"}), then this option is set to \code{FALSE} when the package is loaded. } \examples{ # create a test file in tempdir tests <- " addOne <- function(x) x + 2 expect_true(addOne(0) > 0) expect_equal(2, addOne(1)) " testfile <- tempfile(pattern="test_", fileext=".R") write(tests, testfile) # extract testdir testdir <- dirname(testfile) # run all files starting with 'test' in testdir out <- run_test_dir(testdir) # # print results print(out) summary(out) dat <- as.data.frame(out) out[1] } \seealso{ Other test-files: \code{\link{build_install_test}}, \code{\link{exit_file}}, \code{\link{run_test_dir}}, \code{\link{run_test_file}}, \code{\link{test_package}} } \concept{test-files} tinytest/man/setup_tinytest.Rd0000644000176200001440000000177613514410021016304 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/setup.R \name{setup_tinytest} \alias{setup_tinytest} \title{Add tinytest to package source directory} \usage{ setup_tinytest(pkgdir, force = FALSE, verbose = TRUE) } \arguments{ \item{pkgdir}{\code{[character]} Package source directory} \item{force}{\code{[logical]} Toggle overwrite existing files? (not folders)} \item{verbose}{\code{[logical]} Toggle print progress} } \value{ \code{NULL}, invisibly. } \description{ Creates \code{inst/tinytest}, and an example test file in that directory. Creates \code{tests/tinytest.R} so the package is tested with \code{R CMD check}. Adds \code{tinytests} as a suggested package to the \code{DESCRIPTION}. } \section{Note on \code{DESCRIPTION}}{ Fails when it does not exist. It is assumed that the package is named in the \code{DESCRIPTION}. } \examples{ \dontrun{ # an easy way to set up a package 'haha' that passes # R CMD check pkgKitten::kitten("haha") tinytest::setup_tinytest("haha") } } tinytest/man/register_tinytest_extension.Rd0000644000176200001440000000523013530044222021055 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{register_tinytest_extension} \alias{register_tinytest_extension} \title{Register or unregister extension functions} \usage{ register_tinytest_extension(pkg, functions) } \arguments{ \item{pkg}{\code{[character]} scalar. Name of the package providing extensions.} \item{functions}{\code{[character]} vector. Name of the functions in the package that must be added.} } \description{ Functions to use in \code{.onLoad} and \code{.onUnload} by packages that extend \pkg{tinytest}. } \section{The tinytest API}{ Packages can extend \pkg{tinytest} with expectation functions \emph{if and only} if the following requirements are satisfied. \enumerate{ \item{The extending functions return a \code{\link{tinytest}} object. This can be created by calling \code{tinytest()} with the arguments \itemize{ \item{\code{result}: A \code{logical} scalar: \code{TRUE} or \code{FALSE} (not \code{NA}) } \item{\code{call}: The \code{call} to the expectation function. Usually the result of \code{sys.call(sys.parent(1))} } \item{\code{diff}: A \code{character} scalar, with a long description of the difference. Sentences may be separated by \code{"\\n"}.} \item{\code{short}: Either \code{"data"}, if the difference is in the data. \code{"attr"} when attributes differ or \code{"xcpt"} when an expectation about an exception is not met. If there are differences in data and in attributes, the attributes take precedence.} } Observe that this requires the extending package to add \pkg{tinytest} to the \code{Imports} field in the package's \code{DESCRIPTION} file (this also holds for the following requirement). } \item{Functions are registered in \code{.onLoad()} using \code{register_tinytest_extension()}. Functions that are already registered, including \pkg{tinytest} functions will be overwritten.} } It is \emph{recommended} to: \enumerate{ \item{Follow the syntax conventions of \pkg{tinytest} so expectation functions start with \code{expect_}.} \item{Explain to users of the extension package how to use the extension (see \code{\link{using}}).} } } \section{Minimal example packages}{ \itemize{ \item{Extending \pkg{tinytest}: \href{https://github.com/markvanderloo/tinytest.extension}{tinytest.extension}.} \item{Using a \pkg{tinytest} extension: \href{https://github.com/markvanderloo/uses.tinytest.extension}{using.tinytest.extension}.} } } \seealso{ Other extensions: \code{\link{tinytest}}, \code{\link{using}} } \concept{extensions} tinytest/man/report_side_effects.Rd0000644000176200001440000000337013532276254017231 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expectations.R \name{report_side_effects} \alias{report_side_effects} \title{Report side effects for expressions in test files} \usage{ report_side_effects(report = TRUE, envvar = report, pwd = report, files = report) } \arguments{ \item{report}{\code{[logical]} report all side-effects} \item{envvar}{\code{[logical]} changes in environment variables} \item{pwd}{\code{[logical]} changes in working directory} \item{files}{\code{[logical]} changes in files in the directory where the test file lives. Also watches subdirectories.} } \value{ A named \code{logical}, indicating which aspects of the environment are tracked, invisibly. } \description{ Call this function from within a test file to report side effects. } \section{Details}{ A side effect causes a change in an external variable outside of the scope of a function, or test file. This includes environment variables, global options, global R variables, creating files or directories, and so on. If this function is called in a test file, side effects are monitored from that point in the file and only for that file. The state of the environment before and after running every expression in the file are compared. There is some performance penalty in tracking external variables especially those that require a system call. } \section{Note}{ There could be side-effects that are untrackable by \pkg{tinytest}. This includes packages that use a global internal state within their namespace or packages that use a global state within compiled code. } \examples{ # switch on report_side_effects() # switch off report_side_effects(FALSE) # only report changes in environment variables report_side_effects(pwd=FALSE) } \concept{sidefx} tinytest/man/expect_equal.Rd0000644000176200001440000000566213533477722015704 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expectations.R \name{expect_equal} \alias{expect_equal} \alias{expect_identical} \alias{expect_equivalent} \alias{expect_true} \alias{expect_false} \alias{expect_silent} \alias{expect_null} \alias{expect_error} \alias{expect_warning} \alias{expect_message} \title{Express expectations} \usage{ expect_equal(current, target, tol = sqrt(.Machine$double.eps), info = NA_character_, ...) expect_identical(current, target, info = NA_character_) expect_equivalent(current, target, tol = sqrt(.Machine$double.eps), info = NA_character_, ...) expect_true(current, info = NA_character_) expect_false(current, info = NA_character_) expect_silent(current, quiet = TRUE, info = NA_character_) expect_null(current, info = NA_character_) expect_error(current, pattern = ".*", info = NA_character_) expect_warning(current, pattern = ".*", info = NA_character_) expect_message(current, pattern = ".*", info = NA_character_) } \arguments{ \item{current}{\code{[R object or expression]} Outcome or expression under scrutiny.} \item{target}{\code{[R object or expression]} Expected outcome} \item{tol}{\code{[numeric]} Test equality to machine rounding. Passed to \code{\link[base]{all.equal} (tolerance)}} \item{info}{\code{[character]} scalar. Optional user-defined message. Must be a single character string. Multiline comments may be separated by \code{"\\n"}.} \item{...}{Passed to \code{all.equal}} \item{quiet}{\code{[logical]} suppress output printed by the \code{current} expression (see examples)} \item{pattern}{\code{[character]} A regular expression to match the message.} } \value{ A \code{\link{tinytest}} object. A tinytest object is a \code{logical} with attributes holding information about the test that was run } \description{ Express expectations } \details{ \code{expect_equivalent} calls \code{expect_equal} with the extra arguments \code{check.attributes=FALSE} and \code{use.names=FALSE} \code{expect_silent} fails when an error or warning is thrown. } \note{ Each \code{expect_haha} function can also be called as \code{checkHaha}. Although the interface is not entirely the same, it is expected that this makes migration from the \code{RUnit} framework a little easier, for those who wish to do so. } \section{More information and examples}{ \itemize{ \item{An overview of tinytest can be found in \code{vignette("using_tinytest")}}. \item{Examples of how tinytest is used in practice can be found in \code{vignette("tinytest_examples")}} } } \examples{ expect_equal(1 + 1, 2) # TRUE expect_equal(1 - 1, 2) # FALSE expect_equivalent(2, c(x=2)) # TRUE expect_equal(2, c(x=2)) # FALSE expect_silent(1+1) # TRUE expect_silent(1+"a") # FALSE expect_silent(print("hihi")) # TRUE, nothing goes to screen expect_silent(print("hihi", quiet=FALSE)) # FALSE, and printed } \seealso{ Other test-functions: \code{\link{ignore}} } \concept{test-functions} tinytest/man/test_package.Rd0000644000176200001440000000360713525602544015645 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{test_package} \alias{test_package} \title{Test a package during R CMD check or after installation} \usage{ test_package(pkgname, testdir = "tinytest", at_home = FALSE, ncpu = NULL, ...) } \arguments{ \item{pkgname}{\code{[character]} scalar. Name of the package} \item{testdir}{\code{[character]} scalar. Path to installed directory, relative to the working directory of \code{R CMD check}.} \item{at_home}{\code{[logical]} scalar. Are we at home? (see Details)} \item{ncpu}{A positive integer, or a \code{\link[parallel]{makeCluster}} object.} \item{...}{extra arguments passed to \code{\link{run_test_dir}} (e.g. \code{ncpu}).} } \value{ If \code{interactive()}, a \code{tinytests} object. If not \code{interactive()}, an error is thrown when at least one test fails. } \description{ Run all tests in an installed package. Throw an error and print all failed test results when one or more tests fail if not in interactive mode (e.g. when R CMD check tests a package). This function is intended to be used by \code{R CMD check} or by a user that installed a package that uses the \pkg{tinytest} test infrastructure. } \section{Details}{ We set \code{at_home=FALSE} by default so \code{R CMD check} will run the same as at CRAN. See the package vignette (Section 4) for tips on how to set up the package structure. \code{vignette("using_tinytest",package="tinytest")}. } \examples{ \dontrun{ # Create a file with the following content, to use # tinytest as your unit testing framework: if (requireNamespace("tinytest", quietly=TRUE)) tinytest::test_package("your package name") } } \seealso{ \code{\link{setup_tinytest}} Other test-files: \code{\link{build_install_test}}, \code{\link{exit_file}}, \code{\link{run_test_dir}}, \code{\link{run_test_file}}, \code{\link{summary.tinytests}} } \concept{test-files} tinytest/man/exit_file.Rd0000644000176200001440000000113413525334051015147 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{exit_file} \alias{exit_file} \title{Stop testing} \usage{ exit_file(msg = "") } \arguments{ \item{msg}{\code{[character]} An optional message to print after exiting.} } \value{ The exit message } \description{ Call this function to exit a test file. } \examples{ exit_file("I'm too tired to test") } \seealso{ Other test-files: \code{\link{build_install_test}}, \code{\link{run_test_dir}}, \code{\link{run_test_file}}, \code{\link{summary.tinytests}}, \code{\link{test_package}} } \concept{test-files} tinytest/man/ignore.Rd0000644000176200001440000000266213511031711014461 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{ignore} \alias{ignore} \title{Ignore the output of an expectation} \usage{ ignore(fun) } \arguments{ \item{fun}{\code{[function]} An \code{expect_} function} } \value{ An ignored \code{function} } \description{ Ignored expectations are not reported in the test results. Ignoring is only useful for test files, and not for use directly at the command-line. See also the package vignette: \code{vignette("using_tinytest")}. } \section{Details}{ \code{ignore} is a higher-order function: a function that returns another function. In particular, it accepts a function and returns a function that is almost identical to the input function. The only difference is that the return value of the function returned by \code{ignore} is not caught by \code{\link{run_test_file}} and friends. For example, \code{ignore(expect_true)} is a function, and we can use it as \code{ignore(expect_true)( 1 == 1)}. The return value of \code{ignore(expect_true)(1==1)} is exactly the same as that for \code{expect_true(1==1)}. } \examples{ \donttest{ ## The result of 'expect_warning' is not stored in the test result when ## this is run from a file. expect_true( ignore(expect_warning)(warning("foo!")) ) ## Note the placement of the brackets in ignore(expect_warning)(...). } } \seealso{ Other test-functions: \code{\link{expect_equal}} } \concept{test-functions} tinytest/man/using.Rd0000644000176200001440000000240313530043224014317 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{using} \alias{using} \title{Use an extension package.} \usage{ using(package, quietly = TRUE) } \arguments{ \item{package}{the name of the extension package, given as name or character string.} \item{quietly}{Passed to \code{\link[base]{require}}.} } \value{ A named \code{list}, with the package name and the names of the functions registered by \code{package} to extend \pkg{tinytest}. A message is emitted when the package registers no extension functions. } \description{ Loads and attaches a package to the search path, and picks up the \pkg{tinytest} extension functions registered by the package. Package authors \emph{must} call this function in \emph{every} test file where an extension is used, or otherwise results from the extension package are not recorded (without a warning). Calling \code{using} in every file where an extension is used also ensures that tests can be run in parallel. } \examples{ \dontrun{ # In interactive session: see which functions are exported # by checkmate.tinytest out <- using(checkmate.tinytest) print(out) } } \seealso{ Other extensions: \code{\link{register_tinytest_extension}}, \code{\link{tinytest}} } \concept{extensions} tinytest/man/tinytest.Rd0000644000176200001440000000330413532276254015073 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expectations.R \name{tinytest} \alias{tinytest} \title{Tinytest constructor} \usage{ tinytest(result, call, diff = NA_character_, short = NA_character_, info = NA_character_, file = NA_character_, fst = NA_integer_, lst = NA_integer_, ...) } \arguments{ \item{result}{\code{[logical]} scalar.} \item{call}{\code{[call]} The call that created \code{result}.} \item{diff}{\code{[character]} difference between current and target value (if any).} \item{short}{\code{[character]} short description of the difference} \item{info}{\code{[character]} other information, to be printed in the long message} \item{file}{\code{[character]} File location of the test.} \item{fst}{\code{[integer]} First line number in the test file.} \item{lst}{\code{[integer]} Last line number in the test file (differs from \code{fst} if the call spans multiple lines).} } \value{ A \code{tinytest} object. } \description{ Each individual test in the package generates a \code{tinytest} object. A \code{tinytest} object is a \code{logical} scalar, with metadata (attributes) about the test. } \section{Details}{ The \pkg{result} can take three values. \itemize{ \item{\code{TRUE}: test was passed.} \item{\code{FALSE}: test was failed.} \item{\code{NA}: A side effect was detected.} } Authors of extension packages should not use \code{NA} as a result value as this part of the interface may change in the future. } \examples{ tt <- expect_equal(1+1, 2) if (isTRUE(tt)){ print("w00p w00p!") } else { print("Oh no!") } } \seealso{ Other extensions: \code{\link{register_tinytest_extension}}, \code{\link{using}} } \concept{extensions} \keyword{internal} tinytest/man/at_home.Rd0000644000176200001440000000061213457665033014626 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{at_home} \alias{at_home} \title{Detect not on CRANity} \usage{ at_home() } \description{ Detect whether we are running at home (i.e. not on CRAN, BioConductor, ...) } \examples{ # test will run locally, but not on CRAN if ( at_home() ){ expect_equal(2, 1+1) } } \concept{test-functions test-file} tinytest/man/run_test_file.Rd0000644000176200001440000000633313532276254016057 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{run_test_file} \alias{run_test_file} \title{Run an R file containing tests; gather results} \usage{ run_test_file(file, at_home = TRUE, verbose = getOption("tt.verbose", 2), color = getOption("tt.pr.color", TRUE), remove_side_effects = TRUE, side_effects = FALSE, ...) } \arguments{ \item{file}{\code{[character]} File location of a .R file.} \item{at_home}{\code{[logical]} toggle local tests.} \item{verbose}{\code{[integer]} verbosity level. 0: be quiet, 1: print status per file, 2: print status per test expression.} \item{color}{\code{[logical]} toggle colorize counts in verbose mode (see Note)} \item{remove_side_effects}{\code{[logical]} toggle remove user-defined side effects? See section on side effects.} \item{side_effects}{\code{[logical|list]} Either a logical, or a list of arguments to pass to \code{\link{report_side_effects}}.} \item{...}{Currently unused} } \value{ A \code{list} of class \code{tinytests}, which is a list of \code{\link{tinytest}} objects. } \description{ Run an R file containing tests; gather results } \details{ In \pkg{tinytest}, a test file is just an R script where some or all of the statements express an \code{\link[=expect_equal]{expectation}}. \code{run_test_file} runs the file while gathering results of the expectations in a \code{\link{tinytests}} object. The graphics device is set to \code{pdf(file=tempfile())} for the run of the test file. } \note{ Not all terminals support ansi escape characters, so colorized output can be switched off. This can also be done globally by setting \code{options(tt.pr.color=FALSE)}. Some terminals that do support ansi escape characters may contain bugs. An example is the RStudio terminal (RStudio 1.1) running on Ubuntu 16.04 (and possibly other OSs). } \section{User-defined side effects}{ All calls to \code{\link[base]{Sys.setenv}} and \code{\link[base]{options}} defined in a test file are captured and undone once the test file has run, if \code{remove_side_effects} is set to \code{TRUE}. } \section{Tracking side effects}{ Certain side effects can be tracked, even when they are not explicitly evoked in the test file. See \code{\link{report_side_effects}} for side effects tracked by \pkg{tinytest}. Calls to \code{report_side_effects} within the test file overrule settings provided with this function. } \examples{ # create a test file, in temp directory tests <- " addOne <- function(x) x + 2 Sys.setenv(lolz=2) expect_true(addOne(0) > 0) expect_equal(2, addOne(1)) Sys.unsetenv('lolz') " testfile <- tempfile(pattern="test_", fileext=".R") write(tests, testfile) # run test file out <- run_test_file(testfile,color=FALSE) out # print everything in short format, include passes in print. print(out, nlong=0, passes=TRUE) # run test file, track supported side-effects run_test_file(testfile, side_effects=TRUE) # run test file, track only changes in working directory run_test_file(testfile, side_effects=list(pwd=TRUE, envvar=FALSE)) } \seealso{ \code{\link{ignore}} Other test-files: \code{\link{build_install_test}}, \code{\link{exit_file}}, \code{\link{run_test_dir}}, \code{\link{summary.tinytests}}, \code{\link{test_package}} } \concept{test-files} tinytest/man/run_test_dir.Rd0000644000176200001440000000767213542110170015706 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{run_test_dir} \alias{run_test_dir} \alias{test_all} \title{Run all tests in a directory} \usage{ run_test_dir(dir = "inst/tinytest", pattern = "^test.*\\\\.[rR]$", at_home = TRUE, verbose = getOption("tt.verbose", 2), color = getOption("tt.pr.color", TRUE), remove_side_effects = TRUE, cluster = NULL, lc_collate = getOption("tt.collate", NA), ...) test_all(pkgdir = "./", testdir = "inst/tinytest", ...) } \arguments{ \item{dir}{\code{[character]} path to directory} \item{pattern}{\code{[character]} A regular expression that is used to find scripts in \code{dir} containing tests (by default \code{.R} or \code{.r} files starting with \code{test}).} \item{at_home}{\code{[logical]} toggle local tests.} \item{verbose}{\code{[logical]} toggle verbosity during execution} \item{color}{\code{[logical]} toggle colorize output} \item{remove_side_effects}{\code{[logical]} toggle remove user-defined side effects. Environment variables (\code{Sys.setenv()}) and options (\code{options()}) defined in a test file are reset before running the next test file (see details).} \item{cluster}{A \code{\link[parallel]{makeCluster}} object.} \item{lc_collate}{\code{[character]} Locale setting used to sort the test files into the order of execution. The default \code{NA} ensures current locale is used. Set this e.g. to \code{"C"} to ensure bytewise and more platform-independent sorting (see details).} \item{...}{Arguments passed to \code{run_test_file}} \item{pkgdir}{\code{[character]} scalar. Root directory of the package (i.e. direcory where \code{DESCRIPTION} and \code{NAMESPACE} reside).} \item{testdir}{\code{[character]} scalar. Subdirectory where test files are stored.} } \value{ A \code{tinytests} object } \description{ \code{run\_test\_dir} runs all test files in a directory. \code{test_all} is a convenience function for package development, that wraps \code{run_test_dir}. By default, it runs all files starting with \code{test} in \code{./inst/tinytest/}. It is assumed that all functions to be tested are loaded. } \section{Details}{ We cannot guarantee that files will be run in any particular order accross all platforms, as it depends on the available collation charts (a chart that determines how alphabets are sorted). For this reason it is a good idea to create test files that run independent of each other so their order of execution does not matter. In tinytest, test files cannot share variables. The default behavior of test runners furher discourages interdependence by resetting environment variables and options that are set in a test file after the file is executed. If an environment variable needs to survive a single file, use \code{base::Sys.setenv()} explicitly. Similarly, if an option setting needs to survive, use \code{base::options} } \section{Parallel tests}{ If \code{inherits(cluster, "cluster")} the tests are paralellized over a cluster of worker nodes. \pkg{tinytest} will be loaded onto each cluster node. All other preparation, including loading code from the tested package, must be done by the user. It is also up to the user to clean up the cluster after running tests. See the 'using tinytest' vignette for examples: \code{vignette("using_tinytest")}. } \examples{ # create a test file in tempdir tests <- " addOne <- function(x) x + 2 expect_true(addOne(0) > 0) expect_equal(2, addOne(1)) " testfile <- tempfile(pattern="test_", fileext=".R") write(tests, testfile) # extract testdir testdir <- dirname(testfile) # run all files starting with 'test' in testdir out <- run_test_dir(testdir) print(out) dat <- as.data.frame(out) } \seealso{ \code{\link[parallel]{makeCluster}}, \code{\link[parallel]{clusterEvalQ}}, \code{\link[parallel]{clusterExport}} Other test-files: \code{\link{build_install_test}}, \code{\link{exit_file}}, \code{\link{run_test_file}}, \code{\link{summary.tinytests}}, \code{\link{test_package}} } \concept{test-files} tinytest/man/puppy.Rd0000644000176200001440000000113213523342312014347 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/setup.R \name{puppy} \alias{puppy} \title{The puppy for a pkgKitten} \usage{ puppy(pkgdir, force = FALSE, verbose = TRUE) } \arguments{ \item{pkgdir}{\code{[character]} Package source directory} \item{force}{\code{[logical]} Toggle overwrite existing files? (not folders)} \item{verbose}{\code{[logical]} Toggle print progress} } \description{ Does exactly the same as \code{\link{setup_tinytest}}, but prints a loving message aferwards (and who doesn't want that!?). Just think about those puppies. } \keyword{internal} tinytest/man/build_install_test.Rd0000644000176200001440000000354013532276254017076 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tinytest.R \name{build_install_test} \alias{build_install_test} \title{build, install and test} \usage{ build_install_test(pkgdir = "./", testdir = "tinytest", at_home = TRUE, verbose = getOption("tt.verbose", 2), ncpu = 1, remove_side_effects = TRUE, side_effects = FALSE, keep_tempdir = FALSE) } \arguments{ \item{pkgdir}{\code{[character]} Package directory} \item{testdir}{\code{[character]} Name of directory under \code{pkgdir/inst} containing test files.} \item{at_home}{\code{[logical]} toggle local tests.} \item{verbose}{\code{[logical]} toggle verbosity during execution} \item{ncpu}{\code{[numeric]} number of CPUs to use during the testing phase.} \item{remove_side_effects}{\code{[logical]} toggle remove user-defined side effects? See section on side effects.} \item{side_effects}{\code{[logical|list]} Either a logical, or a list of arguments to pass to \code{\link{report_side_effects}}.} \item{keep_tempdir}{\code{[logical]} keep directory where the pkg is installed and where tests are run? If \code{TRUE}, the directory is not deleted and it's location is printed.} } \value{ A \code{tinytests} object. } \description{ Builds and installs the package in \code{pkgdir} under a temporary directory. Next, loads the package in a fresh R session and runs all the tests. For this function to work the following system requirements are necessary. \itemize{ \item{\code{R CMD build} is available on your system} \item{\code{Rscript} is available on your system} } } \examples{ \dontrun{ ## If your package source directory is "./pkg" you can run build_install_test("pkg") } } \seealso{ Other test-files: \code{\link{exit_file}}, \code{\link{run_test_dir}}, \code{\link{run_test_file}}, \code{\link{summary.tinytests}}, \code{\link{test_package}} } \concept{test-files} tinytest/man/print.tinytest.Rd0000644000176200001440000000123413530043747016223 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expectations.R \name{format.tinytest} \alias{format.tinytest} \alias{print.tinytest} \title{Print a tinytest object} \usage{ \method{format}{tinytest}(x, type = c("long", "short"), ...) \method{print}{tinytest}(x, ...) } \arguments{ \item{x}{A \code{tinytest} object} \item{type}{\code{[logical]} Toggle format type} \item{...}{passed to \code{\link{format.tinytest}}} } \value{ A character string } \description{ Print a tinytest object } \examples{ tt <- expect_equal(1+1, 3) format(tt,"long") format(tt,"short") print(expect_equal(1+1, 2)) print(expect_equal(1+1, 3), type="long") } tinytest/DESCRIPTION0000644000176200001440000000257213543153442013655 0ustar liggesusersPackage: tinytest Maintainer: Mark van der Loo License: GPL-3 Title: Lightweight and Feature Complete Unit Testing Framework LazyData: no Type: Package LazyLoad: yes Authors@R: c( person("Mark", "van der Loo" , role = c("aut","cre") , email = "mark.vanderloo@gmail.com" , comment = c(ORCID="0000-0002-9807-4686")) ) Description: Provides a lightweight (zero-dependency) and easy to use unit testing framework. Main features: install tests with the package. Test results are treated as data that can be stored and manipulated. Test files are R scripts interspersed with test commands, that can be programmed over. Fully automated build-install-test sequence for packages. Skip tests when not run locally (e.g. on CRAN). Flexible and configurable output printing. Compare computed output with output stored with the package. Run tests in parallel. Extensible by other packages. Report side effects. Version: 1.1.0 URL: https://github.com/markvanderloo/tinytest BugReports: https://github.com/markvanderloo/tinytest/issues Imports: parallel, utils RoxygenNote: 6.1.1 Encoding: UTF-8 NeedsCompilation: no Packaged: 2019-09-26 14:58:33 UTC; mark Author: Mark van der Loo [aut, cre] () Repository: CRAN Date/Publication: 2019-09-26 15:20:02 UTC tinytest/build/0000755000176200001440000000000013543151031013230 5ustar liggesuserstinytest/build/vignette.rds0000644000176200001440000000034413543151031015570 0ustar liggesusersO0,QH4F/+/hT) Bk_5bBO< 3ތp ~>:;Ri}CihGTŎMY~,ejr5\;Ҡbì^Ptinytest/tests/0000755000176200001440000000000013470516743013311 5ustar liggesuserstinytest/tests/tinytest.R0000644000176200001440000000013213470516676015320 0ustar liggesusersif ( requireNamespace("tinytest", quietly=TRUE) ){ tinytest::test_package("tinytest") } tinytest/vignettes/0000755000176200001440000000000013543151031014141 5ustar liggesuserstinytest/vignettes/test_addOne.R0000644000176200001440000000020013473037503016516 0ustar liggesusers# contents of test_addOne.R addOne <- function(x) x + 2 expect_true(addOne(0) > 0) hihi <- 1 expect_equal(addOne(hihi), 2) tinytest/vignettes/using_tinytest.Rnw0000644000176200001440000010045713532276246017746 0ustar liggesusers%\VignetteIndexEntry{Using tinytest} \documentclass[11pt]{article} \usepackage{enumitem} \usepackage{xcolor} % for color definitions \usepackage{sectsty} % to modify heading colors \usepackage{fancyhdr} \setlist{nosep} % simpler, but issue with your margin notes \usepackage[left=1cm,right=3cm, bottom=2cm, top=1cm]{geometry} %\usepackage{vmargin} %\setpapersize{USletter} % or a4 for you % Left Top Right Bottom headheight?? headsep footheight footsep %\setmarginsrb{0.75in}{0.25in}{1.1in}{0.25in}{15pt}{0pt}{10pt}{20pt} \usepackage{hyperref} \definecolor{bluetext}{RGB}{0,101,165} \definecolor{graytext}{RGB}{80,80,80} \hypersetup{ pdfborder={0 0 0} , colorlinks=true , urlcolor=blue , linkcolor=bluetext , linktoc=all , citecolor=blue } \sectionfont{\color{bluetext}} \subsectionfont{\color{bluetext}} \subsubsectionfont{\color{bluetext}} % no serif=better reading from screen. \renewcommand{\familydefault}{\sfdefault} % header and footers \pagestyle{fancy} \fancyhf{} % empty header and footer \renewcommand{\headrulewidth}{0pt} % remove line on top \rfoot{\color{bluetext} tinytest \Sexpr{packageVersion("tinytest")}} \lfoot{\color{black}\thepage} % side-effect of \color{}: lowers the printed text a little(?) \usepackage{fancyvrb} % custom commands make life easier. \newcommand{\code}[1]{\texttt{#1}} \newcommand{\pkg}[1]{\textbf{#1}} \let\oldmarginpar\marginpar \renewcommand{\marginpar}[1]{\oldmarginpar{\color{bluetext}\raggedleft\scriptsize #1}} % skip line at start of new paragraph \setlength{\parindent}{0pt} \setlength{\parskip}{1ex} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \title{Using tinytest} \author{Mark van der Loo} \date{\today{} | Package version \Sexpr{packageVersion("tinytest")}} \begin{document} \DefineVerbatimEnvironment{Sinput}{Verbatim}{fontshape=n,formatcom=\color{graytext}} \DefineVerbatimEnvironment{Soutput}{Verbatim}{fontshape=sl,formatcom=\color{graytext}} \newlength{\fancyvrbtopsep} \newlength{\fancyvrbpartopsep} \makeatletter \FV@AddToHook{\FV@ListParameterHook}{\topsep=\fancyvrbtopsep\partopsep=\fancyvrbpartopsep} \makeatother \setlength{\fancyvrbtopsep}{0pt} \setlength{\fancyvrbpartopsep}{0pt} \maketitle{} \thispagestyle{empty} \tableofcontents{} <>= options(prompt="R> ", continue = " ", width=75) @ \subsection*{Reading guide} Readers of this document are expected to know how to write R functions and have a basic understanding of a package source directory structure. \newpage{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Purpose of this package: unit testing} The purpose of \emph{unit testing} is to check whether a function gives the output you expect, when it is provided with certain input. So unit testing is all about comparing \emph{desired} outputs with \emph{realized} outputs. The purpose of this package is to facilitate writing, executing and analyzing unit tests. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Expressing tests} Suppose we define a function translating pounds (lbs) to kilograms inaccurately. <<>>= lbs2kg <- function(x){ if ( x < 0 ){ stop(sprintf("Expected nonnegative weight, got %g",x)) } x/2.20 } @ We like to check a few things before we trust it. <<>>= library(tinytest) expect_equal(lbs2kg(1), 1/2.2046) expect_error(lbs2kg(-3)) @ The value of an \code{expect\_*} function is a \code{logical}, with some attributes that record differences, if there are any. These attributes are used to pretty-print the results. <<>>= isTRUE( expect_true(2 == 1 + 1) ) @ \subsection{Test functions} Currently, the following expectations are implemented. \begin{center} \begin{tabular}{ll} \textbf{Function} & \textbf{what it expects}\\ \code{expect\_equal(current, target)} & equality (using \code{all.equal})\\ \code{expect\_equivalent(current, target)} & equality, ignoring attributes\\ \code{expect\_identical(current, target)} & equality, (using, \code{identical})\\ \code{expect\_true(current)} & \code{current} evaluates to \code{TRUE}\\ \code{expect\_false(current)} & \code{current} evaluates to \code{FALSE}\\ \code{expect\_null(current)} & \code{current} evaluates to \code{NULL}\\ \code{expect\_error(current, pattern)} & error message matching \code{pattern}\\ \code{expect\_warning(current, pattern)} & warning message matching \code{pattern}\\ \code{expect\_message(current, pattern)} & message matching \code{pattern}\\ \code{expect\_silent(current, pattern)} & expect no warnings or errors (just run)\\ \end{tabular} \end{center} Here, \code{target} is the intended outcome and \code{current} is the observed outcome. Also, \code{pattern} is interpreted as a regular expression. <<>>= expect_error(lbs2kg(-3), pattern="nonnegative") expect_error(lbs2kg(-3), pattern="foo") @ \subsection{Alternative syntax} The syntax of the test functions should be familiar to users of the \code{testthat} package\cite{wickham2016testthat}. In test files only, you can use equivalent functions in the style of \code{RUnit}\cite{burger2016RUnit}. To be precise, for each function of the form \code{expect\_lol} there is a function of the form \code{checkLol}. \subsection{Interpreting the output and print options} Let's have a look at an example again. <<>>= expect_false( 1 + 1 == 2, info="My personal message to the tester" ) @ The output of these functions is pretty self-explanatory, nevertheless we see that the output of these expect-functions consist of \begin{itemize} \item The result: \code{FAILED}, \code{PASSED} or \code{SIDEFX}. The latter only occurs when side effects are monitored (see \S\ref{sect:side}) \item The type of failure (if any) between square brackets. Current options are as follows. \begin{itemize} \item \code{[data]} there are differences between observed and expected values. \item \code{[attr]} there are differences between observed and expected attributes, such as column names. \item \code{[xcpt]} an exception (warning, error) was expected but not observed. \end{itemize} When side effects are monitored, and the result is \code{SIDEFX}, a side effect was observed. The type of side effect is reported between square brackets. \begin{itemize} \item \code{[envv]} An environmental variable was created, changed, or deleted. \item \code{[wdir]} The working directory has changed. \item \code{[file]} A file operation occurred in the test directory or one of its subdirectories. \end{itemize} \item When relevant (see \S\ref{sect:testfiles}), the location of the test file and the relevant line numbers. \item The test call. \item When relevant, a summary of the differences between observed and expected values or attributes, or a summary of the observed side effect. \item When present, a user-defined information message. \end{itemize} The result of an \code{expect\_} function is a \code{tinytest} object. You can print them in long format (default) or in short, one-line format like so. <<>>= print(expect_equal(1+1, 3), type="short") @ \marginpar{\code{print} method} Functions that run multiple tests return an object of class \code{tinytests} (notice the plural). Since there may be a lot of test results, \pkg{tinytest} tries to be smart about printing them. The user has ultimate control over this behaviour. See \code{?print.tinytests} for a full specification of the options. \section{Test files} \label{sect:testfiles} In \pkg{tinytest}, tests are scripts, interspersed with statements that perform checks. An example test file in tinytest can look like this. \begin{verbatim} # contents of test_addOne.R addOne <- function(x) x + 2 expect_true(addOne(0) > 0) hihi <- 1 expect_equal(addOne(hihi), 2) \end{verbatim} A particular file can be run using\marginpar{\code{run\_test\_file}} <>= run_test_file("test_addOne.R", verbose=0) @ We use \code{verbose=0} to avoid cluttering the output in this vignette. By default, verbosity is turned on, and a counter is shown while tests are run. The counter is colorized on terminals supporting ANSI color extensions. If you are uncomfortable reading these colors or prefer colorless output, use \code{color=FALSE} or set \code{options(tt.pr.color=FALSE)}. The numbers between \code{<-->} indicate at what lines in the file the failing test can be found. By default only failing tests are printed. You can store the output and print all of them. <<>>= test_results <- run_test_file("test_addOne.R", verbose=0) print(test_results, passes=TRUE) @ Or you can set <>= options(tt.pr.passes=TRUE) @ to print all results during the active R session. To run all test files in a certain directory, we can use\marginpar{\code{run\_test\_dir}} <>= run_test_dir("/path/to/your/test/directory") @ By default, this will run all files of which the name starts with \code{test\_}, but this is customizable. \subsection{Summarizing test results, getting the data} To create some results, run the tests in this package. <<>>= out <- run_test_dir(system.file("tinytest", package="tinytest") , verbose=0) @ The results can be turned into data using \code{as.data.frame}. \marginpar{\code{as.data.frame}} <<>>= head(as.data.frame(out), 3) @ The last two columns indicate the line numbers where the test was defined. A `summary` of the output gives a table with passes and fails per file. \marginpar{\code{summary}} <<>>= summary(out) @ \subsection{Programming over tests, ignoring test results, exiting early} Test scripts are just R scripts interspersed with tests. The test runners make sure that all test results are caught, unless you tell them not to. For example, since the result of a test is a \code{logical} you can use them as a condition. <>= if ( expect_equal(1 + 1, 2) ){ expect_true( 2 > 0) } @ Here, the second test (\code{expect\_true(2 > 0)}) is only executed if the first test results in \code{TRUE}. In any case the result of the first test will be caught in the test output, when this is run with \code{run\_test\_file} \code{run\_test\_dir}, \code{test\_all}, \code{build\_install\_test} or through \code{R CMD check} using \code{test\_package}. If you want to perform the test, but not record the test result you can do the following \marginpar{\code{ignore}} (note the placement of the brackets). <>= if ( ignore(expect_equal)(1+1, 2) ){ expect_true(2>0) } @ Other cases where this may be useful is to perform tests in a loop, e.g. when there is a systematic set of cases to test. It is possible to exit a test file prematurely. For example when there are a number of tests that are not relevant or possible on some OS, you can do the following. \marginpar{\code{exit\_file}} <<>>= if ( Sys.info()[['sysname']] == "Windows"){ exit_file("Cannot test this on Windows") } @ This will cause \code{run\_test\_file} to stop file execution, print the message, and report the information gathered up to where \code{exit} was called. A function like \code{test\_all} will then continue with the next file, so testing is not aborted completely. \subsection{Running order and side effects} \label{sect:running} It is a generally a good idea to write test files that are independent from each other. This means that the order of running them is unimportant for the test results and test files can be maintained independently. The function \code{run\_test\_file} and by extension \code{run\_test\_dir}, \code{test\_all}, and \code{test\_package} encourage this by resetting \begin{itemize} \item options, set with \code{options()}; \item environment variables, set with \code{Sys.setenv()} \end{itemize} after a test file is executed. To escape this behavior, use \code{base::Sys.setenv()} respectively \code{base::options()}. Alternatively use <>= run_test_dir("/path/to/my/testdir" , remove_side_effects = FALSE) test_all("/path/to/my/testdir" , remove_side_effects = FALSE) # Only in tests/tinytest.R: test_package("PACKAGENAME", remove_side_effects=FALSE) @ Test files are sorted and run based on the current locale. This means that the order of execution is in general not platform-independent. You can control the sorting behavior interactively or by setting \code{options(tt.collate)}. To be precise, adding <>= options(tt.collate="C") @ to \code{/tests/tinytest.R} before running \code{test\_package} will ensure bytewise sorting on most systems. See also \code{help("run\_test\_dir")}. \subsection{Monitoring side effects} \label{sect:side} The term 'side effect' is the technical expression for the situation where a function or expression changes something outside of its scope. Examples include creating, removing, or changing variables in R's global work space, R options, or environment variables of your operating system. We will call such variables or options \emph{external variables}. To test for side-effects once, use the \code{side\_effects} argument to any of the test runners. For example <>= test_package("pkg", side_effects=TRUE) @ There is control over which side-effects to track. For example to prevent tracking changes in the working directory, do the following. <>= test_package("pkg", side_effects=list(pwd=FALSE)) @ If you add \code{report\_side\_effects()} anywhere in a test file, certain external variables are monitored from that point on, and for that file only. It can be switched off again by calling \code{report\_side\_effects(FALSE)} anywhere in the file. The reporting functionality will compare the state of external variables before and after every expression in the test file and report any changes. At the moment, the variables that can be monitored include environment variables and the present working directory. Below is an example of a test file where side effects are recorded. The third line creates an explicit side effect by creating a new environment variable called \code{hihi} with the value \code{"lol"}. \begin{verbatim} # contents of test_se.R report_side_effects() expect_equal(1+1, 2) Sys.setenv(hihi="lol") expect_equal(1+1, 3) Sys.setenv(hihi="lulz ftw") \end{verbatim} Running the test file yields an object of class \code{tinytests} as usual, only now changes in environment variables are reported. <<>>= run_test_file("test_se.R", verbose=1) @ Note that as discussed in Section~\S\ref{sect:running}, \pkg{tinytest} will unset the environment variable \code{hihi} automatically after running the file because it was set directly by the author of the test file using \code{Sys.setenv}. The real value of the reporting functionality is that it also reports on external variables that are touched by other functions than those you call explicitly in the file. Reading and comparing versions of external variables takes some time. Especially when it requires a call to the operating service such as a request for values of environment variables. We therefore recommend this to be used only when you suspect a side effect. Or, for example to execute \code{report\_side\_effects()} conditional on \code{at\_home()}. It is not possible to catch all types of side effects, even in principle, using the \pkg{tinytest} reporting functionality. Examples include: packages that keep a global variable or environment within their namespace to store some state, and packages that rely on compiled code where there are global objects within the shared object. Side effects are to be avoided as a general and strong principle, but sometimes there is little or no choice. In Section~\ref{sect:devil} we give some tips on how to properly handle such situations. \section{Testing packages} Using \pkg{tinytest} for your package is pretty easy. \begin{enumerate} \item Testfiles are placed in \code{/inst/tinytest}. The testfiles all have names starting with \code{test} (for example \code{test\_haha.R}). \item In the file \code{/tests/tinytest.R} you place the code \begin{verbatim} if ( requireNamespace("tinytest", quietly=TRUE) ){ tinytest::test_package("PACKAGENAME") } \end{verbatim} \item In your \code{DESCRIPTION} file, add \pkg{tinytest} to \code{Suggests:}. \end{enumerate} You can automatically create a minimal running test infrastructure with the \code{setup\_tinytest} function. \marginpar{\code{setup\_tinytest}} <>= setup_tinytest("/path/to/your/package") @ In a terminal, you can now do \begin{verbatim} R CMD build /path/to/your/package R CMD check PACKAGENAME_X.Y.Z.tar.gz \end{verbatim} and all tests will run. To run all the tests interactively, make sure that all functions of your new package are loaded. After that, run\marginpar{\code{test\_all}} <>= test_all("/path/to/your/package") @ where the default package directory is the current working directory. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Build--install--test interactively} The most realistic way to unit-test your package is to build it, install it and then run all the tests. The function <>= build_install_test("/path/to/your/package") @ does exactly that. It builds and installs the package in a temporary directory, starts a fresh R session, loads the newly installed package and runs all tests. The return value is a \code{tinytests} object. The package is built without manual or vignettes to speed up the whole process. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Using data stored in files} \label{sect:comparefiles} When your package is tested with \code{test\_package}, \pkg{tinytest} ensures that your working directory is the testing directory (by default \code{tinytest}). This means you can read files that are stored in your folder directly. Suppose that your package directory structure looks like this (default): \begin{itemize} \item[]\code{/inst} \begin{itemize} \item[]\code{/tinytest} \begin{itemize} \item[]\code{/test.R} \item[]\code{/women.csv} \end{itemize} \end{itemize} \end{itemize} Then, to check whether the contents of \code{women.csv} is equal to the built-in \code{women} dataset, the content of \code{test.R} looks as follows. <>= dat <- read.csv("women.csv") expect_equal(dat, women) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Skipping tests on \code{CRAN}} It is not possible to detect whether a test is running on CRAN. This means we are forced to detect that we are running tests in our own environment. In the following example we use the host name to detect if we are running on our own machine and explicitly pass this information to \code{test\_package}. <>= options(prompt=" ", continue=" ") @ <>= # contents of pkgdir/tests/tinytest.R if ( requireNamespace("tinytest", quietly=TRUE) ){ home <- identical( Sys.info()["nodename"], "YOURHOSTNAME" ) tinytest::test_package("PKGNAME", at_home = home) } @ Other ways to detect whether you are running `at home' include \begin{itemize} \item Set a custom environment variable (from your OS) and detect it with \code{Sys.getenv}. <<>>= home <- identical( Sys.getenv("HONEYIMHOME"), "TRUE" ) @ \item Use 4-number package versioning while developing and 3-number versioning for CRAN releases\footnote{As \href{https://stackoverflow.com/questions/36166288/skip-tests-on-cran-but-run-locally}{recommended here} by Dirk Eddelbuettel.}. <>= home <- length(unclass(packageVersion("PKGNAME"))[[1]]) == 4 @ \end{itemize} <>= options(prompt="R> ", continue=" ") @ \subsubsection*{When tests are run interactively} All the interactive test runners have \code{at\_home=TRUE} by default, so while you are developing all tests are run, unless you exclude them explicitly. <<>>= run_test_file("test_hehe.R", verbose=0) run_test_file("test_hehe.R", verbose=0, at_home=FALSE) @ Here is an overview of test runners and their default setting for \code{at\_home}. \begin{center} \begin{tabular}{rll} Function & Default \code{at\_home} & Intended use \\ \hline \code{run\_test\_file} & \code{TRUE} & Interactive by developer\\ \code{run\_test\_dir} & \code{TRUE} & Interactive by developer\\ \code{test\_all} & \code{TRUE} & Interactive by developer\\ \code{build\_install\_test} & \code{TRUE} & Interactive by developer\\ \code{test\_package} & \code{FALSE} & \code{R CMD check}, or after installation by user. \end{tabular} \end{center} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Testing your package after installation} Supposing your package is called \pkg{hehe} and the \pkg{tinytest} infrastructure is used. If the package is installed, the following command runs \pkg{hehe}'s tests. <>= tinytest::test_package("hehe") @ This can come in handy when a user of \pkg{hehe} reports a bug and you want to make sure all tested functionality works on the user's system. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Using extension packages} It is possible for other packages to add custom assertions (\code{expect}-functions). To use such a package: \begin{enumerate} \item Add the extension package to the \code{Suggests:} field in the \code{DESCRIPTION} file. \item Add \code{using(pkg)} to \emph{each} test file that use the extensions (see \code{?using}). \marginpar{\code{using}} \end{enumerate} When multiple extension packages are loaded, and when there are name collisions, the packages loaded later takes precedence over the ones loaded earlier (as usual in R). This includes assertions exported by \pkg{tinytest}. \textbf{Note.} Other than in regular R, it is not possible to disambiguate functions using namespace resolution as in \code{pkg::expect\_something}, because in that case the test result will not be caught by \pkg{tinytest}. The API for building extension packages is described in \code{?register\_tinytest\_extension}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Running tests in parallel} In \pkg{tinytest}, a file should be considered a closed unit: no information created in one test file should be used in another. Under this condition, tests can automatically run in parallel by running different files in different R sessions. Running code in parallel takes some careful consideration around setting up a cluster, running the tests, and closing the cluster of preparing it for the next run. Depending on the test runner used, there are different levels of control and responsibility for the user to prepare the program for parallelization. Below we describe them from less easier to more control. \subsubsection*{\code{build\_install\_test}} This function creates and installs a package in a temporary location. By setting the \code{ncpu} parameter, the number of cores used at the testing phase can be increased. <>= build_install_test("/path/to/your/package", ncpu=2) @ We already mentioned that the order in which files are run is in principle system-dependent and it is a good practice not to rely on it. Under parallel situations, all bets on file order are off. \subsubsection*{\code{test\_package}.} This function assumes that a package is installed. It can gather any information necessary to parallelize a test run. The simplest way to parallelize is to specify the number of CPUs used. <>= test_package("PACKAGENAME", ncpu=2) @ Here, \code{test\_package} will \begin{enumerate} \item Set up a local cluster using \code{parallel::makeCluster}. \item Load the package on each R instance of the cluster. \item Run test files in parallel over the cluster. \item Collect the results and close the cluster. \end{enumerate} In stead of just passing the number of CPUs it is possible to pass a \code{cluster} object. In that case \code{test\_package} will still load the package on each node. However, note that if the package gets updated and reinstalled, it should also be reloaded. It is in general hard to completely unload a package in R (see \code{?detach} and \code{?unloadNamespace} for some details on artifacts that will not be removed). So our advice is to restart a cluster for each test run. \subsubsection*{\code{run\_test\_dir}, \code{test\_all}} These function assumes that all functionality needed to run the tests is loaded. They accept an object of type \code{cluster}. The user is responsible for setting up the nodes. <>= cl <- parallel::makeCluster(4, outfile="") parallel::clusterCall(cl, source, "R/myfunctions.R") run_test_dir("inst/tinytest", cluster=cl) @ where the argument \code{outfile=""} ensures that messages from each node are forwarded to the master node. It is possible to keep the cluster `alive', so modifications can be made to \code{"R/myfunctions.R"} and then run for example the following. <>= parallel::clusterCall(cl, source, "R/myfunctions.R") test_all(cluster=cl) stopCluster(cl) @ For heavy test routines it is thus possible to keep a test cluster up to offload computations. For more complex situations, including packages that use \code{S4} classes, or compiled code, (re)loading takes more effort than sourcing a few R files. In this cases it is often easier to restart a clean cluster for each test round. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{A few tips on packages and unit testing} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Make your package spherical} Larger packages typically consist of functions that are visible to the users (exported functions) and a number of functions that are only used by the exported functions. For example: <<>>= # exported, user-visible function inch2cm <- function(x){ x*conversion_factor("inch") } # not exported function, package-internal conversion_factor <- function(unit){ confac <- c(inch=2.54, pound=1/2.2056) confac[unit] } @ We can think of the exported functions as the \emph{surface} of the package and all the other functions as the \emph{volume}. The surface is what a user sees, the volume is what the developer sees. The surface is how a user interacts with a package. If the surface is small (few functions exported), users are limited in the ways they can interact with your package and that means there is less to test. So as a rule of thumb, it is a good idea to keep the surface small. Since a sphere has the smallest surface-to-volume ratio possible, I refer to this rule as \emph{keep your package spherical}. By the way, the technical term for the surface of a package is API (application program interface). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Test the surface, not the volume} Unexpected behavior (a bug) is often discovered when someone who is not the developer starts using code. Bugfixing implies altering code and it may even require you to refactor large chunks of code that is internal to a package. If you defined extensive tests on non-exported functions, this means you need to rewrite the tests as well. As a rule of thumb, it is a good idea to test only the behaviour at the surface, so as a developer you have more freedom to change the internals. This includes rewriting and renaming internal functions completely. By the way, it is bad practice to change the surface, since that means you are going to break other people's code. Nobody likes to program against an API that changes frequently, and everybody hates to program against an API that changes unexpectedly. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{How many tests do I need?} When you call a function, you can think of its arguments flowing through a certain path from input to output. As an example, let's take a look again at a new, slightly safer unit conversion function. <<>>= pound2kg <- function(x){ stopifnot( is.numeric(x) ) if ( any(x < 0) ){ warning("Found negative input, converting to positive") x <- abs(x) } x/2.2046 } @ If we call \code{lbs2kg} with argument \code{2}, we can write: \begin{verbatim} 2 -> /2.2046 -> output \end{verbatim} If we call \code{lbs2kg} with argument \code{-3} we can write \begin{verbatim} -3 -> abs() -> /2.2046 -> output \end{verbatim} Finally, if we call \code{pound2kg} with \code{"foo"} we can write \begin{verbatim} "foo" -> stop() -> Exception \end{verbatim} So we have three possible paths. In fact, we see that every nonnegative number will follow the first path, every negative number will follow the second path and anything nonnumeric follows the third path. So the following test suite fully tests the behaviour of our function. <>= expect_equal(pound2kg(1), 1/2.2046 ) # test for expected warning, store output expect_warning( out <- pound2kg(-1) ) # test the output expect_equal( out, 1/2.2046) expect_error(pound2kg("foo")) @ The number of paths of a function is called its \emph{cyclomatic complexity}. For larger functions, with multiple arguments, the number of paths typically grows extremely fast, and it quickly becomes impossible to define a test for each and every one of them. If you want to get an impression of how many tests one of your functions in needs in principle, you can have a look at the \pkg{cyclocomp} package of G\'abor Cs\'ardi\cite{csardi2016cyclocomp}. Since full path coverage is out of range in most cases, developers often strive for something simpler, namely \emph{full code coverage}. This simply means that each line of code is run in at least one test. Full code coverage is no guarantee for bugfree code. Besides code coverage it is therefore a good idea to think about the various ways a user might use your code and include tests for that. To measure code coverage, I recommend using the \pkg{covr} package by Jim Hester\cite{hester2018covr}. Since \pkg{covr} is independent of the tools or packages used for testing, it also works fine with \pkg{tinytest}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{It's not a bug, it's a test!} If users of your code are friendly enough to submit a bug report when they find one, it is a good idea to start by writing a small test that reproduces the error and add that to your test suite. That way, whenever you work on your code, you can be sure to be alarmed when a bug reappears. Tests that represent earlier bugs are sometimes called \emph{regression tests}. If a bug reappears during development, software engineers sometimes refer to this as a \emph{regression}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Side effects are the Devil} \label{sect:devil} Since side-effects manipulate variables outside of the scope of a function, or even outside of R, they can cause bugs that are hard to reproduce. R offers a mechanism to ensure that a function leaves the outside world as it was, once your code stops running. Suppose you need to change working directory within a function, \code{source} a file and return to the working directory. A naive way to do this is like so. \marginpar{\code{on.exit}} <<>>= bad_function <- function(file){ oldwd <- getwd() setwd(dirname(file)) source(basename(file)) setwd(oldwd) } @ This all works fine, untill \code{file} contains faulty code and throws an error. As result, the execution of \code{bad\_function} will stop and leave the user in a changed working directory. With \code{on.exit} you can define code that will be carried out before the function exits, either normally or with an error. <<>>= <<>>= good_function <- function(file){ oldwd <- getwd() on.exit(setwd(oldwd)) setwd(dirname(file)) source(basename(file)) } @ \newpage \begin{thebibliography}{5} \bibitem{wickham2016testthat} \href{https://cran.r-project.org/package=testthat}{Unit Testing for R} Hadley Wickham (2016). testthat: Get Started with Testing. The R Journal, vol. 3, no. 1, pp. 5--10, 2011 \bibitem{burger2016RUnit} Matthias Burger, Klaus Juenemann and Thomas Koenig (2018). \href{https://CRAN.R-project.org/package=RUnit}{RUnit: R Unit Test Framework} R package version 0.4.32. \bibitem{csardi2016cyclocomp} \href{https://cran.r-project.org/package=cyclocomp}{cyclocomp: cyclomatic complexity of R code} G\'abor Cs\'ardi (2016) R package version 1.1.0 \bibitem{hester2018covr} \href{https://CRAN.R-project.org/package=covr}{covr: Test Coverage for Packages} Jim Hester (2018) R package version 3.2.1 \end{thebibliography} \end{document} tinytest/vignettes/test_se.R0000644000176200001440000000016413524613204015737 0ustar liggesusersreport_side_effects() expect_equal(1+1, 2) Sys.setenv(hihi="lol") expect_equal(1+1, 3) Sys.setenv(hihi="lulz ftw") tinytest/vignettes/test_hehe.R0000644000176200001440000000006113500004617016231 0ustar liggesusers if ( at_home() ){ expect_equal(1 + 1, 2) } tinytest/vignettes/tinytest_examples.Rnw0000644000176200001440000004036613523346202020427 0ustar liggesusers%\VignetteIndexEntry{tinytest by example} \documentclass[11pt]{article} \usepackage{enumitem} \usepackage{xcolor} % for color definitions \usepackage{sectsty} % to modify heading colors \usepackage{fancyhdr} \setlist{nosep} % simpler, but issue with your margin notes \usepackage[left=1cm,right=3cm, bottom=2cm, top=1cm]{geometry} \usepackage{hyperref} \definecolor{bluetext}{RGB}{0,101,165} \definecolor{graytext}{RGB}{80,80,80} \hypersetup{ pdfborder={0 0 0} , colorlinks=true , urlcolor=blue , linkcolor=bluetext , linktoc=all , citecolor=blue } \sectionfont{\color{bluetext}} \subsectionfont{\color{bluetext}} \subsubsectionfont{\color{bluetext}} % no serif=better reading from screen. \renewcommand{\familydefault}{\sfdefault} % header and footers \pagestyle{fancy} \fancyhf{} % empty header and footer \renewcommand{\headrulewidth}{0pt} % remove line on top \rfoot{\color{bluetext} tinytest \Sexpr{packageVersion("tinytest")}} \lfoot{\color{black}\thepage} % side-effect of \color{}: lowers the printed text a little(?) \usepackage{fancyvrb} % custom commands make life easier. \newcommand{\code}[1]{\texttt{#1}} \newcommand{\pkg}[1]{\textbf{#1}} \let\oldmarginpar\marginpar \renewcommand{\marginpar}[1]{\oldmarginpar{\color{bluetext}\raggedleft\scriptsize #1}} % skip line at start of new paragraph \setlength{\parindent}{0pt} \setlength{\parskip}{1ex} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \title{Tinytest by example} \author{Mark van der Loo} \date{\today{} | Package version \Sexpr{packageVersion("tinytest")}} \begin{document} \DefineVerbatimEnvironment{Sinput}{Verbatim}{fontshape=n,formatcom=\color{graytext}} \DefineVerbatimEnvironment{Soutput}{Verbatim}{fontshape=sl,formatcom=\color{graytext}} \newlength{\fancyvrbtopsep} \newlength{\fancyvrbpartopsep} \makeatletter \FV@AddToHook{\FV@ListParameterHook}{\topsep=\fancyvrbtopsep\partopsep=\fancyvrbpartopsep} \makeatother \setlength{\fancyvrbtopsep}{0pt} \setlength{\fancyvrbpartopsep}{0pt} \maketitle{} \thispagestyle{empty} \tableofcontents{} <>= options(prompt=" ", continue = " ", width=75) library(tinytest) @ \subsection*{Introduction} This document provides a number of real-life examples on how \pkg{tinytest} is used by other packages. The examples aim to illustrate the purpose of testing functions and serve as a complement to the technical documentation and the `using \code{tinytest}' vignette. There is a section for each function. Each section starts with a short example that demonstrates the core purpose of the function. Next, one or more examples from packages that are published on CRAN are shown and explained. Sometimes a few lines of code were modified or deleted for brevity. This is indicated with comment between square brackets, e.g. <>= ## [this is an extra comment, only for this vignette] @ This document is probably not interesting to read front-to-back. It is more aimed to browse once in a while to get an idea on how \pkg{tinytest} can be used in practice. Package authors are invited to contribute new use cases so new users can learn from them. Please contact the author of this package either by email or via the \href{http://github.com/markvanderloo/tinytest}{github repository}. \newpage{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{\code{expect\_equal}} R objects are described by the data they contain and the attributes attached to them. For example, in the vector \code{c(x=1,y=2)}, the data consist of the numbers \code{1} and \code{2} (in that order) and there is a single attribute called \code{names}, consisting of the two strings \code{"x"} and \code{"y"} (in that order). The \code{expect\_equal} function tests whether both the data and the attributes of two objects are the same. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_equal(1,1) expect_equal(1, c(x=1)) @ Numbers do not have to be exactly the same to be equal (by default). <<>>= 0.9-0.7-0.2 expect_equal(0.9-0.7-0.2,0) expect_equal(0.9-0.7-0.2,0, tolerance=0) @ <>= options(prompt=" ", continue = " ", width=75) @ Here is an example from the \pkg{stringdist} package. This package implements various methods to determine how different two strings are. In this test, we check one aspect of the `optimal string alignment' algorithm. In particular, we test if it correctly counts the switch of two adjacent characters as a single operation. <>= expect_equal(stringdist("ab", "ba", method="osa"), 1) @ The \pkg{benchr} package is a package to time R code, and it uses \code{expect\_equal} to extensively check the outputs. Here are a few examples. <>= b <- benchr::benchmark(1 + 1, 2 + 2) m <- mean(b) expect_equal(class(m), c("summaryBenchmark", "data.frame")) expect_equal(dim(m), c(2L, 7L)) expect_equal(names(m), c("expr", "n.eval", "mean", "trimmed", "lw.ci", "up.ci", "relative")) expect_equal(class(m$expr), "factor") expect_equal(levels(m$expr), c("1 + 1", "2 + 2")) expect_true(all(sapply(m[-1], is.numeric))) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_equivalent}} This function ignores the attributes when comparing two R objects. Two objects are equivalent when their data are the same. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_equivalent(1,1) expect_equivalent(1, c(x=1)) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{validate} package offers functions to define restrictions on data, and then confront the data with them. The function \code{values} extracts the boolean results in the form of a matrix with specific row- and column names. In the example below we are only interested in testing whether the \emph{contents} of the matrix is computed correctly. <>= v <- validator(x > 0) d <- data.frame(x=c(1,-1,NA)) expect_equivalent(values(confront(d,v)), matrix(c(TRUE,FALSE,NA)) ) @ The \pkg{anytime} package translates text data into data/time format (\code{Date} or \code{POSIXct}). Here, a test is performed to equivalence, to ignore the timezone label that is attached by anytime but not by \code{as.Date}. <>= refD <- as.Date("2016-01-01")+0:2 expect_equivalent(refD, anydate(20160101L + 0:2)) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_identical}} This is the most strict test for equality. The best way to think about this is that two objects must be byte-by-byte indistinguishable in order to be identical. The differences can be subtle, as shown below. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= La <- list(x=1); Lb <- list(x=1) expect_identical(La, Lb) a <- new.env() a$x <- 1 b <- new.env() b$x <- 1 expect_identical(a,b) @ Here, \code{La} and \code{Lb} are indistinguishable from R's point of view. They only differ in their location in memory. The environments \code{a} and \code{b} \emph{are} distinguishable since they contain an explicit identifier which make them unique. <<>>= print(a) print(b) @ Another difference with \code{expect\_equal} and \code{expect\_equivalent} is that \code{expect\_identical} does not allow any tolerance for numerical differences. <>= options(prompt=" ", continue = " ", width=75) @ The \code{stringdistmatrix} function of \pkg{stringdist} computes a matrix of string dissimilarity measures between all elements of a character vector. Below, it is tested whether the argument \code{useNames="none"} and the legacy (deprecated) argument \code{useName=FALSE}. <>= a <- c(k1 = "aap",k2="noot") expect_identical(stringdistmatrix(a,useNames="none") , stringdistmatrix(a,useNames=FALSE)) @ The \pkg{wand} package can retrieve MIME types for files and directories. This means there are many cases to test. In this particular package this is done by creating two lists, one with input and one with expected results. The tests are then performed as follows: <>= list( ## [long list of results removed for brevity] ) -> results fils <- list.files(system.file("extdat", package="wand"), full.names=TRUE) tst <- lapply(fils, get_content_type) names(tst) <- basename(fils) for(n in names(tst)) expect_identical(results[[n]], tst[[n]]) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_null}} The result of an operation should be \code{NULL}. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_null(iris$hihi) expect_null(iris$Species) @ <>= options(prompt=" ", continue = " ", width=75) @ This function is new in version 0.9.7 and not used in any depending packages yet. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_true}, \code{expect\_false}} The result of an operation should be precisely \code{TRUE} or \code{FALSE}. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_true(1 == 1) expect_false(1 == 2) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{anytime} package converts many types of strings to date/time objects (\code{POSIXct} or \code{Date}). Here is a part of it's \pkg{tinytest} test suite. <>== ## Datetime: factor and ordered (#44) refD <- as.Date("2016-09-01") expect_true(refD == anydate(as.factor("2016-09-01"))) expect_true(refD == anydate(as.ordered("2016-09-01"))) expect_true(refD == utcdate(as.factor("2016-09-01"))) expect_true(refD == utcdate(as.ordered("2016-09-01"))) @ Note that \code{==} used here has subtly different behavior from \code{all.equal} used by \code{expect\_equal}. In the above case, \code{==} does not compare time zone data, which is not added by \code{as.Date} but is added by \code{anytime}. This means that for example <>= expect_equal(anydate(as.factor("2016-09-01")), refD) @ would fail. The \pkg{ulid} package uses \code{expect\_true} to verify the type of a result. <>= x <- ULIDgenerate(20) expect_true(is.character(x)) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_message}} Expect that a message is emitted. Optionally you can specify a regular expression that the message must match. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_message(message("hihi")) expect_message(message("hihi"), pattern = "hi") expect_message(message("hihi"), pattern= "ha") expect_message(print("hihi")) @ <>= options(prompt=" ", continue = " ", width=75) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_warning}} Expect that a warning is emitted. Optionally you can specify a regular expression that the warning must match. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_warning(warning("hihi")) expect_warning(warning("hihi"), pattern = "hi") expect_warning(warning("hihi"), pattern= "ha") expect_warning(1+1) @ <>= options(prompt=" ", continue = " ", width=75) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_error}} Expect that an error is emitted. Optionally you can specify a regular expression that the error must match. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_error(stop("hihi")) expect_error(stop("hihi"), pattern = "hi") expect_error(stop("hihi"), pattern= "ha") expect_error(print("hoho")) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{ChemoSpec2D} package implements exploratory methods for 2D-spectrometry data. Scaled data has negative values, so one cannot take the logarithm. The function \code{centscaleSpectra2D} must eject an error in such cases and this is tested as follows. <>= # Check that log and centering cannot be combined expect_error( centscaleSpectra2D(tiny, center = TRUE, scale = "log"), "Cannot take log of centered data") @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_silent}} Sometimes a test is only run to check that the code does not crash. This function tests that no warnings or errors are emitted when evaluating it's argument. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_silent(print(10)) expect_silent(stop("haha")) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{validate} package defines an object called a \code{validation}, which is the result of confronting a dataset with one or more data quality restrictions in the form of rules. A \pkg{validation} object can be plotted, but this would crash with an error in a certain edge case. Here is a test that was added in response to a reported issue. <>= data <- data.frame(A = 1) rule <- validator(A > 0) cf <- confront(data, rule) expect_silent(plot(rule)) expect_silent(plot(cf)) @ The \pkg{lumberjack} package creats log files that track changes in data. In one test it is first tested whether a file has been generated, next it is tested whether it can be read properly. This is also an example of programming over test results, since the file is deleted if it exists. <>= run("runs/multiple_loggers.R") simple_ok <- expect_true(file.exists("runs/simple_log.csv")) expect_silent(read.csv("runs/simple_log.csv")) if (simple_ok) unlink("runs/simple_log.csv") @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{ignore}} Ignore allows you to not record the result of a test. It is not used very often. Its use is probably almost exclusive to \pkg{tinytest} where it is used while testing the expectation functions. The following result is not recorded (note placement of brackets!) <>= ignore(expect_equal)(1+1, 2) @ The \pkg{digest} package computes hashes of R objects. It uses \code{ignore} in one of it's files. <>= mantissa <- gsub(" [0-9]*$", "", x.hex) ignore(expect_true)(all( sapply( head(seq_along(mantissa), -1), function(i){ all( grepl( paste0("^", mantissa[i], ".*"), tail(mantissa, -i) ) ) } ) )) @ \newpage \begin{thebibliography}{5} \bibitem{anytime} \href{https://cran.r-project.org/package=anytime}{anytime} D. Eddelbuettel (2019) \emph{Anything to `POSIXct' or `Date' Converter}. R package version 0.3.3.5 \bibitem{benchr} \href{https://CRAN.R-project.org/package=RUnit}{benchr} Arttem Klevtsov (2019) \emph{High Precise Measurement of R Expressions Execution Time}. R package version 0.2.3-1. \bibitem{ChemoSpec2D} \href{https://cran.r-project.org/package=cyclocomp}{ChemoSpec2D} B.A. Hanson (2019) \emph{Exploratory Chemometrics for 2D Spectroscopy} R package version 0.3.166 \bibitem{digest} \href{https://cran.r-project.org/package=digest}{digest} D. Eddelbuettel (2019) \emph{Create Compact Hash Digests of R Objects} R package version 0.6.20 \bibitem{stringdist} \href{https://cran.r-project.org/package=stringr}{stringdist} M. van der Loo (2014). \emph{The stringdist package for approximate string matching}. The R Journal 6(1) 111-122 \bibitem{ulid} \href{https://cran.r-project.org/package=ulid}{ulid} B. Rudis (2019) \emph{Generate Universally Unique Lexicographically Sortable Identifiers}. R package version 0.3.0 \bibitem{validate} \href{https://cran.r-project.org/package=validate}{validate} M. van der Loo, E. de Jonge and P. Hsieh (2019) \emph{Data Validation Infrastructure for R}. R package version 0.2.7 \bibitem{wand} \href{https://cran.r-project.org/package=wand}{wand} B. Rudis (2019) \emph{Retrieve `Magic' Attributes from Files and Directories} R package version 0.5.0 \end{thebibliography} \end{document} tinytest/NEWS0000644000176200001440000001253413542661271012650 0ustar liggesusersversion 1.1.0 - Tests are now run with 'pdf(file=tempfile())' as graphics device. This avoids writing 'Rplots.pdf' under the library directory when R CMD check is run. - Side-effects tracking now includes file operations in the test directory or subdirectories thereof. - build_install_test now accepts arguments 'side_effects', 'remove_side_effects' - expect_* functions gain argument 'info': a user-defined message that is printed in long output format. - 'run_test_dir' now selects files with "^test.*\\.[rR]$", not "^test.*\\.[rR]" (thanks to Dirk Eddelbuettel). - Fixed 'diff' message for scalar comparisons in expect_equivalent, expect_equal, expect_identical: 'target' and 'current' were switched in message (thanks to GH user Billy34). - 'setup_tinytest' now keeps formatting for DESCRIPTION files (thanks to Bart-Jan van Rossum). - Fixed crash of 'build_install_test' when pkg was developed under directory with spaces (thanks to Bart-Jan van Rossum). - Fixed issue where R CMD check would hang when packages use paralellization (GH #36) or certain Rcpp features (GH #37). Thanks to George G Vega Yon and Dirk Eddelbuettel. version 1.0.0 - New argument 'side_effects' for test runners: monitor side effects while running tests. - New function 'report_side_effects': toggle monitoring side-effects within test files. - Run test files in parallel, for example: test_package("pkg", ncpu=2). (Thanks to Dirk Eddelbuettel for the suggestion). - New function 'exit_file': stop running a test file with an optional message. - Other packages can now extend the package, see '?register_tinytest_extension' for the API (Thanks to Michel Lang for the suggestion) - New function 'using' loads tinytest extension package. - New function 'expect_null' - Improved reporting for 'expect_true', 'expect_false' - Improved reporting for expect_identical, expect_true, expect_equivalent in the case of scalar values. - Added second vignette with real-life examples of tinytest tests. - build_install_test gains argument 'color' and 'verbose' - test_package returns result visibly in interactive sessions. - Fixed path issue for build_install_test on Windows (thanks to Jan Wijffels) version 0.9.6 - Fixed error caught on CRAN version 0.9.5 - New function 'expect_message'. - New functions 'all_pass', 'all_fail', 'any_pass', 'any_fail' for investigating 'tinytests' objects. - When `interactive()`, 'test_package' will not throw an error upon a failing test. Still throws an error when not `interactive()`. This makes it more usefull for interactive testing of installed packages. - 'test_package()' now returns a 'tinytests' object. - Better 'verbosity' while running test files (thanks to Dirk Eddelbuettel for discussions and feedback) - Added 'test_silent'. Check for lack of errors or warnings (Thanks to Bryan Hanson for the suggestion, #14) - Colored output is turned off automatically when the pkg is loaded in a "dumb" terminal. (Thanks to Dirk Eddelbeutel for the suggestion, #12). - Improved documentation, including on how to skip tests on CRAN. - Improved vignette layout (thanks to Dirk Eddelbuettel's suggestions and initial PR #15 for geometry settings) - Improved behavior of 'setup_tinytest()' when a 'Suggests' field is already present in DESCRIPTION - Fix: toleracence was not passed to 'all.equal' by 'expect_equal' and 'expect_equivalent' (Thanks to An Chu #17) - Fix 'expect_warning' and 'expect_error' would crash when run on the CLI. (Thanks to Bryan Hanson #13) - Fix: method dispatch in packages defining S4 methods now works. - Fix: 'setup' now adds 'tinytest' to 'Suggests:' rather then adding an extra 'Suggests' field in DESCRIPTION. - Corrections in README and vignette, thanks to GH user 'salim-b' PR #18, #19 - Internal: simplified code for 'expect_warning' and 'expect_error' (thanks to Lionel Henry for suggestions). version 0.9.4 - New function 'setup_tinytest' that adds 'tinytest' infrastructure to a package source directory. - Global settings set by 'options' and 'Sys.setenv' in a test file are automatically removed after running a test file. This improves independence between test files. This default can be overwritten, see Chapter 3 of the 'Using tinytest' vignette. - Test file sorting order can be controlled with 'lc_collate' option in test runners or by setting options(tt.collate) globally. - More control over testing packages in 'R CMD check' with extra arguments to 'test_packages'. - Improved call reporting in case of multiline test expressions (for example when many test expressions depend on an 'if' condition). - Fix in 'expect_warning' and 'expect_error'. In some circumstances evaluation argument 'current' evaluated in the wrong scope, resulting in unfound variables. version 0.9.3 - New function 'expect_identical' - All functions of the form 'expect_lol' can now also be called as 'checkLol' (similar to, but not the same as 'RUnit' syntax) - Expect_* functions now have first argument 'current' and second argument 'target' - Added 'summary' method for objects of class 'tinytests' - Default test directory is now 'inst/tinytest' - Fix: 'as.data.frame' crashed on tinytests object with all tests passing. version 0.9.2 - Processed review by CRAN team member. - Output coloring is now optional. - Fixed issue so package works correctly with r-oldrel. version 0.9.1 - Fixed a few CRAN warnings. version 0.9.0 - Initial release. tinytest/R/0000755000176200001440000000000013542661112012337 5ustar liggesuserstinytest/R/methods.R0000644000176200001440000001304313524624153014132 0ustar liggesusers #' @rdname tinytests #' @param object a \code{tinytests} object #' @return For \code{summary} a \code{\link{table}} object #' @export summary.tinytests <- function(object, ...){ result <- factor(sapply(object, c) , levels=c(FALSE, TRUE, NA) , labels=c("fails","passes","sidefx") , exclude=character(0)) file <- sapply(object, function(x) attr(x,"file")) if (length(object) > 0) file <- basename(file) tab <- table(File = file, result) tab <- cbind(tab, Results = rowSums(tab)) tab <- rbind(tab, Total = colSums(tab)) tab <- as.table(tab[,c(4,1:3),drop=FALSE]) # remove side-effect column if it has only zeros if ( sum(tab[,4]) == 0 ) tab <- tab[,-4] n <- dimnames(tab) names(n) <- c("File","") tab <- as.table(tab) dimnames(tab) <- n tab } #' @rdname tinytests #' @return For \code{all_pass}, \code{any_pass}, \code{all_fail}, \code{any_fail}: #' a single \code{logical} #' @export all_pass <- function(x){ stopifnot(inherits(x,'tinytests')) all( sapply(x, function(d) isTRUE(d) || is.na(d)) ) } #' @rdname tinytests #' @export any_pass <- function(x){ stopifnot(inherits(x,'tinytests')) any( sapply(x, function(d) isTRUE(d) || is.na(d)) ) } #' @rdname tinytests #' @export all_fail <- function(x){ stopifnot(inherits(x,'tinytests')) all( sapply(x, function(d) isFALSE(d) || is.na(d)) ) } #' @rdname tinytests #' @export any_fail <- function(x){ stopifnot(inherits(x,'tinytests')) any( sapply(x, function(d) isFALSE(d) || is.na(d)) ) } #' Tinytests object #' #' An object of class \code{tinytests} (note: plural) results #' from running multiple tests from script. E.g. by running #' \code{\link{run_test_file}}. #' #' #' @aliases tinytests #' #' @param i an index #' @param x a \code{tinytests} object #' #' @return For \code{`[.tinytests`} a \code{tinytests} object. #' #' @export #' @rdname tinytests `[.tinytests` <- function(x,i){ structure(unclass(x)[i], class="tinytests") } #' @param passes \code{[logical]} Toggle: print passing tests? #' @param sidefx \code{[logical]} Toggle: print side effects? #' @param limit \code{[numeric]} Max number of results to print #' @param nlong \code{[numeric]} First \code{nlong} results are printed in long format. #' @param ... passed to \code{\link{format.tinytest}} #' #' @section Details: #' #' By default, the first 3 failing test results are printed in long form, #' the next 7 failing test results are printed in short form and all other #' failing tests are not printed. These defaults can be changed by passing options #' to \code{print.tinytest}, or by setting one or more of the following global #' options: #' \itemize{ #' \item{\code{tt.pr.passes} Set to \code{TRUE} to print output of non-failing tests.} #' \item{\code{tt.pr.limit} Max number of results to print (e.g. \code{Inf})} #' \item{\code{tt.pr.nlong} The number of results to print in long format (e.g. \code{Inf}).} #' } #' #' For example, set \code{options(tt.pr.limit=Inf)} to print all test results. #' Furthermore, there is the option #' \itemize{ #' \item{\code{tt.pr.color},} #' } #' which determines whether colored output is printed. #' If R is running in a dumb terminal (detected by comparing #' environment variable \code{"TERM"} to \code{"dumb"}), then #' this option is set to \code{FALSE} when the package is loaded. #' #' @rdname tinytests #' @export print.tinytests <- function(x , passes=getOption("tt.pr.passes", FALSE) , sidefx=getOption("tt.pr.sidefx", TRUE) , limit =getOption("tt.pr.limit", 7) , nlong =getOption("tt.pr.nlong", 3),...){ nrslt <- length(x) ifail <- if (nrslt > 0) sapply(x, isFALSE) else logical(0) iside <- if (nrslt > 0) sapply(x, is.na) else logical(0) ipass <- if (nrslt > 0) sapply(x, isTRUE) else logical(0) iprn <- ifail if (passes) iprn <- iprn | ipass if (sidefx) iprn <- iprn | iside x <- x[iprn] if (sum(iprn)==0){ print(sprintf("All ok, %d results",nrslt)) return(invisible(NULL)) } limit <- min(length(x), limit) nlong <- min(nlong, limit) nshort <- max(limit - nlong,0) x <- x[seq_len(limit)] type <- c( rep("long",nlong) , rep("short",nshort) ) str <- sapply(seq_along(x), function(i) format.tinytest(x[[i]], type=type[i])) cat(paste0(str,"\n"), "\n") if (nrslt > length(str)){ pr1 <- sprintf("Showing %d out of %d results: ", length(x), nrslt) pr2 <- sprintf("%d fails, %d passes", sum(ifail), sum(ipass)) pr3 <- if( any(iside) ) sprintf(", %s side effects", sum(iside)) else "" cat(pr1, pr2, pr3,"\n",sep="") } } #' @return For \code{as.data.frame.} a data frame. #' @family test-files #' #' @examples #' # create a test file in tempdir #' tests <- " #' addOne <- function(x) x + 2 #' #' expect_true(addOne(0) > 0) #' expect_equal(2, addOne(1)) #' " #' testfile <- tempfile(pattern="test_", fileext=".R") #' write(tests, testfile) #' #' # extract testdir #' testdir <- dirname(testfile) #' # run all files starting with 'test' in testdir #' out <- run_test_dir(testdir) #' # #' # print results #' print(out) #' summary(out) #' dat <- as.data.frame(out) #' out[1] #' #' @rdname tinytests #' @export as.data.frame.tinytests <- function(x, ...){ L <- lapply(x, attributes) data.frame( result = sapply(x, c) , call = sapply(L, function(y) gsub(" +"," ",paste0(capture.output(print(y$call)),collapse=" ")) ) , diff = sapply(L, `[[`, "diff") , short = sapply(L, `[[`, "short") , file = sapply(L, `[[`, "file") , first = sapply(L, `[[`, "fst") , last = sapply(L, `[[`, "lst") , stringsAsFactors=FALSE ) } tinytest/R/tinytest.R0000644000176200001440000007613413542660564014372 0ustar liggesusers#' @importFrom utils install.packages file_test capture.output getFromNamespace #' @importFrom parallel makeCluster parLapply stopCluster {} # reference object to store or ignore output # of 'expect' functions output <- function(){ e <- new.env() r <- 0 # number of results n <- 0 # number of tests m <- 0 # number of passes s <- 0 # number of side-effects re <- "^T[0-9]+" e$add <- function(x){ r <<- r + 1 e[[sprintf("T%04d",r)]] <- x if ( isTRUE(x) || isFALSE(x) ){ n <<- n + 1 m <<- m + as.integer(x) } else if (is.na(x)){ s <<- s + 1 } } e$gimme <- function(){ vr <- ls(e,pattern = re) lapply(vr, function(i) e[[i]]) } e$rm_last <- function(){ x <- ls(e,pattern = re) i <- x[length(x)] if ( isTRUE(e[[i]]) ) m <<- m - 1 # note: we never ignore a call to envdiff, # so no need to check for is.na(e[i]). rm(list=i, envir=e) n <<- n-1 r <<- r-1 } e$ntest <- function() n e$npass <- function() m e$nfail <- function() n - m e$nside <- function() s # metadata will be provided by run_test_file e$fst <- 0 e$lst <- 0 e$call <- "" e$file # will be set by exit_file() e$exit <- FALSE e$exitmsg <- "" e$exit_msg <- function(print){ if(print){ plural <- e$lst != e$fst if (plural) catf("\nExited '%s' at lines %d-%d. %s" , basename(e$file), e$fst, e$lst, e$exitmsg) else catf("\nExited '%s' at line %d. %s" , basename(e$file), e$fst, e$exitmsg) } } e } capture <- function(fun, env){ # avoid lazy eval when looping over functions as a variable # e.g. when loading extensions. force(fun) function(...){ out <- fun(...) if ( inherits(out, "tinytest") ){ attr(out,"file") <- env$file attr(out,"fst") <- env$fst attr(out,"lst") <- env$lst attr(out,"call") <- env$call # if not NA, the result is from an expect_ function # if NA, it is a side-effect, and we do not attempt to # improve the call's format if (!is.na(out) && env$lst - env$fst >=3) attr(out,"call") <- match.call(fun) env$add(out) attr(out,"env") <- env } out } } # RUnit style checking functions expect_xfoo -> checkXfoo add_RUnit_style <- function(e){ fns <- ls(e, pattern="^expect_") # snake to camelCase fns_RUnit <- sub("_(.)", "\\U\\1", fns, perl=TRUE) fns_RUnit <- sub("expect","check",fns_RUnit) # add checkHaha for each expect_hihi (lol no for each expect_haha) for (i in seq_along(fns)) assign(fns_RUnit[i], e[[fns[i]]], envir=e) } #' Ignore the output of an expectation #' #' Ignored expectations are not reported in the test results. #' Ignoring is only useful for test files, and not for use directly #' at the command-line. See also the package vignette: \code{vignette("using_tinytest")}. #' #' @param fun \code{[function]} An \code{expect_} function #' #' @return An ignored \code{function} #' @family test-functions #' #' #' @section Details: #' #' \code{ignore} is a higher-order function: a function that returns another function. #' In particular, it accepts a function and returns a function that is almost identical #' to the input function. The only difference is that the return value of the function #' returned by \code{ignore} is not caught by \code{\link{run_test_file}} and friends. #' For example, \code{ignore(expect_true)} is a function, and we can use it as #' \code{ignore(expect_true)( 1 == 1)}. The return value of \code{ignore(expect_true)(1==1)} #' is exactly the same as that for \code{expect_true(1==1)}. #' #' #' @examples #' \donttest{ #' ## The result of 'expect_warning' is not stored in the test result when #' ## this is run from a file. #' expect_true( ignore(expect_warning)(warning("foo!")) ) #' ## Note the placement of the brackets in ignore(expect_warning)(...). #' } #' #' #' @export ignore <- function(fun){ function(...){ out <- fun(...) if ( !is.null(attr(out, "env")) ){ attr(out,"env")$rm_last() attr(out,"env") <- NULL } out } } #' Stop testing #' #' Call this function to exit a test file. #' #' @param msg \code{[character]} An optional message to print after exiting. #' #' #' @return The exit message #' #' @examples #' exit_file("I'm too tired to test") #' #' @family test-files #' @export exit_file <- function(msg="") msg # masking function to to call within run_test_file capture_exit <- function(fun, env){ function(...){ env$exit <- TRUE env$exitmsg <- fun(...) } } # we need a special capture function for # Sys.setenv because it's return value does # not inlcude argument names (it is an unnamed # logical vector). We need the names to be able to # unset the env vars later on. capture_envvar <- function(fun, env){ function(...){ for ( x in names(list(...)) ){ # record the first occurrence so we capture the # original value if ( !x %in% ls(envir=env) ) env[[x]] <- Sys.getenv(x) } out <- fun(...) invisible(out) } } unset_envvar <- function(env){ L <- as.list(env) # Sys.setenv chrashes with empty list if ( length(L)>0 ) do.call(Sys.setenv, L) } capture_options <- function(fun, env){ function(...){ out <- fun(...) for ( x in names(out) ){ # record only the first occurrence so we capture # the original value if (!x %in% ls(envir=env)) env[[x]] <- out[[x]] } invisible(out) } } reset_options <- function(env){ options(as.list(env)) } # envir : an environment where test files are evaluated # output: an environment where test results are captured add_locally_masked_functions <- function(envir, output){ # Local masking of native functions. 'manually' because # it is faster then loading via getFromNamespace() envir$expect_equal <- capture(expect_equal, output) envir$expect_equivalent <- capture(expect_equivalent, output) envir$expect_true <- capture(expect_true, output) envir$expect_false <- capture(expect_false, output) envir$expect_null <- capture(expect_null, output) envir$expect_message <- capture(expect_message, output) envir$expect_warning <- capture(expect_warning, output) envir$expect_error <- capture(expect_error, output) envir$expect_identical <- capture(expect_identical, output) envir$expect_silent <- capture(expect_silent, output) envir$exit_file <- capture_exit(exit_file, output) envir$ignore <- ignore envir$at_home <- tinytest::at_home ## add 'checkFoo' equivalents of 'expect_foo' (native functions only) if ( getOption("tt.RUnitStyle", TRUE) ) add_RUnit_style(envir) envir$using <- capture_using(using, envir, output) } #' Use an extension package. #' #' Loads and attaches a package to the search path, and picks up the #' \pkg{tinytest} extension functions registered by the package. Package #' authors \emph{must} call this function in \emph{every} test file where an #' extension is used, or otherwise results from the extension package are not #' recorded (without a warning). Calling \code{using} in every file #' where an extension is used also ensures that tests can be run in parallel. #' #' #' @param package the name of the extension package, given as name or character string. #' @param quietly Passed to \code{\link[base]{require}}. #' #' @return A named \code{list}, with the package name and the names of the #' functions registered by \code{package} to extend \pkg{tinytest}. A message #' is emitted when the package registers no extension functions. #' #' @examples #' \dontrun{ #' # In interactive session: see which functions are exported #' # by checkmate.tinytest #' out <- using(checkmate.tinytest) #' print(out) #' } #' #' @family extensions #' @export using <- function(package, quietly=TRUE){ pkg <- as.character(substitute(package)) if ( !require(pkg, quietly=TRUE, character.only=TRUE) ){ stopf("Package %s could not be loaded",pkg) } ext <- getOption("tt.extensions", FALSE) out <- if ( isFALSE(ext) ){ msgf("Package '%s' registered no tinytest extensions.") list(character(0)) } else { ext } names(out) <- pkg invisible(out) } capture_using <- function(fun, envir, output){ function(...){ # call user-facing function ext <- fun(...) # get package name pkg <- names(ext) functions <- ext[[pkg]] for ( func in functions ){ # get funcy! # get function object from namespace f <- tryCatch(getFromNamespace(func, pkg) , error = function(e){ msg <- sprintf("Loading '%s' extensions failed with message:\n'%s'" , pkg, e$message) warning(msg, call.=FALSE) }) # mask'm like there's no tomorrow envir[[func]] <- capture(f, output) } invisible(ext) } } #' Register or unregister extension functions #' #' Functions to use in \code{.onLoad} and \code{.onUnload} by packages that #' extend \pkg{tinytest}. #' #' @param pkg \code{[character]} scalar. Name of the package providing extensions. #' @param functions \code{[character]} vector. Name of the functions in the package that must be added. #' #' #' @section The tinytest API: #' #' Packages can extend \pkg{tinytest} with expectation functions \emph{if and only} #' if the following requirements are satisfied. #' #' \enumerate{ #' \item{The extending functions return a \code{\link{tinytest}} object. This #' can be created by calling \code{tinytest()} with the arguments #' \itemize{ #' \item{\code{result}: A \code{logical} scalar: \code{TRUE} or \code{FALSE} (not #' \code{NA}) } #' \item{\code{call}: The \code{call} to the expectation function. Usually the #' result of \code{sys.call(sys.parent(1))} } #' \item{\code{diff}: A \code{character} scalar, with a long description of the #' difference. Sentences may be separated by \code{"\\n"}.} #' \item{\code{short}: Either \code{"data"}, if the difference is in the #' data. \code{"attr"} when attributes differ or \code{"xcpt"} when #' an expectation about an exception is not met. If there are #' differences in data and in attributes, the attributes take #' precedence.} #' } #' Observe that this requires the extending package to add \pkg{tinytest} to #' the \code{Imports} field in the package's \code{DESCRIPTION} file (this #' also holds for the following requirement). #' } #' \item{Functions are registered in \code{.onLoad()} using #' \code{register_tinytest_extension()}. Functions that are already #' registered, including \pkg{tinytest} functions will be overwritten.} #' } #' It is \emph{recommended} to: #' \enumerate{ #' \item{Follow the syntax conventions of \pkg{tinytest} so expectation #' functions start with \code{expect_}.} #' \item{Explain to users of the extension package how to use the extension #' (see \code{\link{using}}).} #' } #' #' #' @section Minimal example packages: #' #' \itemize{ #' \item{Extending \pkg{tinytest}: #' \href{https://github.com/markvanderloo/tinytest.extension}{tinytest.extension}.} #' \item{Using a \pkg{tinytest} extension: #' \href{https://github.com/markvanderloo/uses.tinytest.extension}{using.tinytest.extension}.} #' } #' @family extensions #' @export register_tinytest_extension <- function(pkg, functions){ ext <- getOption("tt.extensions",FALSE) if (isFALSE(ext)){ L <-list(functions) names(L) <- pkg options(tt.extensions = L) } else { ext[[pkg]] <- functions options(tt.extensions = ext) } } #' Run an R file containing tests; gather results #' #' @param file \code{[character]} File location of a .R file. #' @param at_home \code{[logical]} toggle local tests. #' @param verbose \code{[integer]} verbosity level. 0: be quiet, 1: print #' status per file, 2: print status per test expression. #' @param color \code{[logical]} toggle colorize counts in verbose mode (see Note) #' @param remove_side_effects \code{[logical]} toggle remove user-defined side #' effects? See section on side effects. #' @param side_effects \code{[logical|list]} Either a logical, #' or a list of arguments to pass to \code{\link{report_side_effects}}. #' @param ... Currently unused #' #' @details #' #' In \pkg{tinytest}, a test file is just an R script where some or all #' of the statements express an \code{\link[=expect_equal]{expectation}}. #' \code{run_test_file} runs the file while gathering results of the #' expectations in a \code{\link{tinytests}} object. #' #' The graphics device is set to \code{pdf(file=tempfile())} for the run of the #' test file. #' #' @section User-defined side effects: #' #' All calls to \code{\link[base]{Sys.setenv}} and \code{\link[base]{options}} #' defined in a test file are captured and undone once the test file has run, #' if \code{remove_side_effects} is set to \code{TRUE}. #' #' @section Tracking side effects: #' #' Certain side effects can be tracked, even when they are not explicitly evoked #' in the test file. See \code{\link{report_side_effects}} for side effects tracked #' by \pkg{tinytest}. #' Calls to \code{report_side_effects} within the test file overrule #' settings provided with this function. #' #' #' #' @note #' Not all terminals support ansi escape characters, so colorized output can be #' switched off. This can also be done globally by setting \code{options(tt.pr.color=FALSE)}. #' Some terminals that do support ansi escape characters may contain #' bugs. An example is the RStudio terminal (RStudio 1.1) running on Ubuntu 16.04 #' (and possibly other OSs). #' #' @return A \code{list} of class \code{tinytests}, which is a list #' of \code{\link{tinytest}} objects. #' #' @examples #' # create a test file, in temp directory #' tests <- " #' addOne <- function(x) x + 2 #' #' Sys.setenv(lolz=2) #' #' expect_true(addOne(0) > 0) #' expect_equal(2, addOne(1)) #' #' Sys.unsetenv('lolz') #' " #' testfile <- tempfile(pattern="test_", fileext=".R") #' write(tests, testfile) #' #' # run test file #' out <- run_test_file(testfile,color=FALSE) #' out #' # print everything in short format, include passes in print. #' print(out, nlong=0, passes=TRUE) #' #' # run test file, track supported side-effects #' run_test_file(testfile, side_effects=TRUE) #' #' # run test file, track only changes in working directory #' run_test_file(testfile, side_effects=list(pwd=TRUE, envvar=FALSE)) #' #' #' @family test-files #' @seealso \code{\link{ignore}} #' @export run_test_file <- function( file , at_home=TRUE , verbose = getOption("tt.verbose", 2) , color = getOption("tt.pr.color", TRUE) , remove_side_effects = TRUE , side_effects = FALSE , ...){ if (!file_test("-f", file)){ stop(sprintf("'%s' does not exist or is a directory",file),call.=FALSE) } ## where to come back after running the file oldwd <- getwd() # make sure that plots get redirected to oblivion grDevices::pdf(file=tempfile()) ## this will store the names of all environment ## variables created while running the file. envvar <- new.env() ## this will store option values that are overwritten by ## the user when running the file. oldop <- new.env() ## clean up side effects on.exit({ ## Clean up tinytest side effects # go back to the original working directory setwd(oldwd) # unset 'at_home' marker Sys.unsetenv("TT_AT_HOME") if ( remove_side_effects ){ ## Clean up user side effects # unset env vars set by the user in 'file' unset_envvar(envvar) # reset options to the state before running 'file' reset_options(oldop) } grDevices::dev.off() }) setwd(dirname(file)) file <- basename(file) if (at_home) Sys.setenv(TT_AT_HOME=TRUE) # An environment to capture the output in. o <- output() # An environment to run the test scripts in e <- new.env(parent=globalenv()) # We locally mask expectation functions in the evaluation # environment 'e' so their output will be captured in 'o' add_locally_masked_functions(envir = e, output=o) ## Reduce user side effects by making sure that any env var set ## in a test file is unset after running it. e$Sys.setenv <- capture_envvar(Sys.setenv, envvar) ## Reduce user side effects by capturing options that will be reset ## on exit e$options <- capture_options(options, oldop) ## Set useFancyQuotes, which is usually done by startup.Rs, the location ## of which is defined by envvar R_TESTS, which we set to empty now. ## See GH issues 36,37 options(useFancyQuotes=FALSE) Sys.setenv(R_TESTS="") ## Make sure that we catch side-effects if the user asks for it. # an environment to store side-effects, and wheter we report them. sidefx <- new.env() e$report_side_effects <- capture_se(report_side_effects, sidefx) do.call(e$report_side_effects, as.list(side_effects)) # internal side-effect tracker: make sure results are exported to user. local_report_envvar <- capture(report_envvar, o) local_report_cwd <- capture(report_cwd, o) local_report_files <- capture(report_files, o) # parse file, store source reference. parsed <- parse(file=file, keep.source=TRUE) src <- attr(parsed, "srcref") o$file <- file # format file name for printing while running. prfile <- basename(file) if (nchar(prfile) > 30 ){ prfile <- paste0("..",substr(prfile, nchar(prfile)-27,nchar(prfile))) } prfile <- paste("Running",gsub(" ",".",sprintf("%-30s",basename(file)))) for ( i in seq_along(parsed) ){ expr <- parsed[[i]] o$fst <- src[[i]][1] o$lst <- src[[i]][3] o$call <- expr if ( !o$exit ) eval(expr, envir=e) else { o$exit_msg(verbose >= 1) break } local_report_envvar(sidefx) local_report_cwd(sidefx) local_report_files(sidefx) if (verbose == 2) print_status(prfile, o, color) } if (verbose == 1) print_status(prfile, o, color) if (verbose >= 1) catf("\n") # returns a 'list' of 'tinytest' objects test_output <- o$gimme() structure(test_output, class="tinytests") } print_status <- function(filename, env, color){ prefix <- sprintf("\r%s %4d tests", filename, env$ntest()) # print status after counter fails <- if ( env$ntest() == 0 ) " " # print nothing if nothing was tested else if ( env$nfail() == 0 ) sprintf(if(color) "\033[0;32mOK\033[0m" else "OK") else sprintf(if (color) "\033[0;31m%d fails\033[0m" else "%d fails", env$nfail()) side <- if (env$nside() == 0) "" else sprintf(if (color) "\033[0;93m%d side-effects\033[0m" else "%d side-effects", env$nside()) cat(prefix, fails, side, sep=" ") } #' Run all tests in a directory #' #' \code{run\_test\_dir} runs all test files in a directory. #' #' #' @param dir \code{[character]} path to directory #' @param pattern \code{[character]} A regular expression that is used to find #' scripts in \code{dir} containing tests (by default \code{.R} or \code{.r} #' files starting with \code{test}). #' @param at_home \code{[logical]} toggle local tests. #' @param verbose \code{[logical]} toggle verbosity during execution #' @param color \code{[logical]} toggle colorize output #' @param remove_side_effects \code{[logical]} toggle remove user-defined side #' effects. Environment variables (\code{Sys.setenv()}) and options (\code{options()}) #' defined in a test file are reset before running the next test file (see details). #' @param cluster A \code{\link[parallel]{makeCluster}} object. #' @param lc_collate \code{[character]} Locale setting used to sort the #' test files into the order of execution. The default \code{NA} ensures #' current locale is used. Set this e.g. to \code{"C"} to ensure bytewise #' and more platform-independent sorting (see details). #' @param ... Arguments passed to \code{run_test_file} #' #' @section Details: #' #' We cannot guarantee that files will be run in any particular order accross #' all platforms, as it depends on the available collation charts (a chart that #' determines how alphabets are sorted). For this reason it is a good idea to #' create test files that run independent of each other so their order of #' execution does not matter. In tinytest, test files cannot share variables. #' The default behavior of test runners furher discourages interdependence by #' resetting environment variables and options that are set in a test file #' after the file is executed. If an environment variable needs to survive a #' single file, use \code{base::Sys.setenv()} explicitly. Similarly, if an #' option setting needs to survive, use \code{base::options} #' #' @section Parallel tests: #' #' If \code{inherits(cluster, "cluster")} the tests are paralellized over a #' cluster of worker nodes. \pkg{tinytest} will be loaded onto each cluster #' node. All other preparation, including loading code from the tested package, #' must be done by the user. It is also up to the user to clean up the cluster #' after running tests. See the 'using tinytest' vignette for examples: #' \code{vignette("using_tinytest")}. #' #' #' @return A \code{tinytests} object #' #' #' @examples #' # create a test file in tempdir #' tests <- " #' addOne <- function(x) x + 2 #' #' expect_true(addOne(0) > 0) #' expect_equal(2, addOne(1)) #' " #' testfile <- tempfile(pattern="test_", fileext=".R") #' write(tests, testfile) #' #' # extract testdir #' testdir <- dirname(testfile) #' # run all files starting with 'test' in testdir #' out <- run_test_dir(testdir) #' print(out) #' dat <- as.data.frame(out) #' #' @family test-files #' @seealso \code{\link[parallel]{makeCluster}}, #' \code{\link[parallel]{clusterEvalQ}}, \code{\link[parallel]{clusterExport}} #' #' @export run_test_dir <- function(dir="inst/tinytest", pattern="^test.*\\.[rR]$" , at_home = TRUE , verbose = getOption("tt.verbose", 2) , color = getOption("tt.pr.color",TRUE) , remove_side_effects = TRUE , cluster = NULL , lc_collate = getOption("tt.collate",NA) , ... ){ testfiles <- dir(dir, pattern=pattern, full.names=TRUE) testfiles <- locale_sort(testfiles, lc_collate=lc_collate) if ( !inherits(cluster, "cluster") ){ # set pwd here, to save time in run_test_file. oldwd <- getwd() on.exit(setwd(oldwd)) setwd(dir) test_output <- lapply(basename(testfiles), run_test_file , at_home = at_home , verbose = verbose , color = color , remove_side_effects = remove_side_effects , ...) } else { parallel::clusterEvalQ(cluster, library(tinytest)) test_output <- parallel::parLapply(cluster, testfiles , run_test_file, at_home = at_home, verbose = min(verbose,1) , color = color, remove_side_effects = TRUE, ...) } # by using '(parL)|(l)apply' we get a list of tinytests objects. We need to unwind # one level to a list of 'tinytest' objects and class it 'tinytests'. structure(unlist(test_output,recursive=FALSE), class="tinytests") } # Sort according to LC_COLLATE locale_sort <- function(x, lc_collate=NA, ...){ if (is.na(lc_collate)) return(sort(x,...)) # catch current locale old_collate <- Sys.getlocale("LC_COLLATE") # set to user-defined locale if possible, otherwise sort using current locale colset <- tryCatch({ Sys.setlocale("LC_COLLATE", lc_collate) TRUE }, warning=function(e){ msg <- sprintf("Could not sort test files in 'C' locale, using %s\n" , old_collate) message(paste(msg, e$message,"\n")) FALSE }, error=warning) out <- sort(x) # reset to old locale if (colset) Sys.setlocale("LC_COLLATE", old_collate) out } #' Test a package during development #' #' \code{test_all} is a convenience function for package development, that #' wraps \code{run_test_dir}. By default, it runs all files starting with #' \code{test} in \code{./inst/tinytest/}. It is assumed that all functions to #' be tested are loaded. #' #' #' @param pkgdir \code{[character]} scalar. Root directory of the package (i.e. #' direcory where \code{DESCRIPTION} and \code{NAMESPACE} reside). #' @param testdir \code{[character]} scalar. Subdirectory where test files are #' stored. #' #' @rdname run_test_dir #' #' @export test_all <- function(pkgdir="./", testdir="inst/tinytest", ...){ run_test_dir( file.path(pkgdir,testdir), ...) } #' Detect not on CRANity #' #' Detect whether we are running at home (i.e. not on CRAN, BioConductor, ...) #' #' #' @examples #' # test will run locally, but not on CRAN #' if ( at_home() ){ #' expect_equal(2, 1+1) #' } #' @export #' @family test-functions test-file at_home <- function(){ identical(Sys.getenv("TT_AT_HOME"),"TRUE") } #' Test a package during R CMD check or after installation #' #' Run all tests in an installed package. Throw an error and print all failed test #' results when one or more tests fail if not in interactive mode (e.g. when #' R CMD check tests a package). This function is intended to be #' used by \code{R CMD check} or by a user that installed a package that #' uses the \pkg{tinytest} test infrastructure. #' #' @param pkgname \code{[character]} scalar. Name of the package #' @param testdir \code{[character]} scalar. Path to installed directory, relative #' to the working directory of \code{R CMD check}. #' @param at_home \code{[logical]} scalar. Are we at home? (see Details) #' @param ncpu A positive integer, or a \code{\link[parallel]{makeCluster}} object. #' @param ... extra arguments passed to \code{\link{run_test_dir}} (e.g. \code{ncpu}). #' #' #' @section Details: #' We set \code{at_home=FALSE} by default so \code{R CMD check} will run the same #' as at CRAN. See the package vignette (Section 4) for tips on how to set up #' the package structure. #' \code{vignette("using_tinytest",package="tinytest")}. #' #' @return If \code{interactive()}, a \code{tinytests} object. If not #' \code{interactive()}, an error is thrown when at least one test fails. #' #' @family test-files #' @seealso \code{\link{setup_tinytest}} #' @examples #' \dontrun{ #' # Create a file with the following content, to use #' # tinytest as your unit testing framework: #' if (requireNamespace("tinytest", quietly=TRUE)) #' tinytest::test_package("your package name") #' } #' @export test_package <- function(pkgname, testdir = "tinytest" , at_home=FALSE, ncpu=NULL, ...){ on.exit({ if ( is.numeric(ncpu) ) parallel::stopCluster(cluster) }) testdir <- system.file(testdir, package=pkgname) if ( testdir == "" ){ stopf("testdir '%s' not found for package '%s'",testdir, pkgname) } # set up cluster if required cluster <- if (is.null(ncpu)) NULL else if (is.numeric(ncpu)) parallel::makeCluster(ncpu, outfile="") else if (inherits(ncpu, "cluster")) ncpu else stop("ncpu must be NULL, 'numeric', or 'cluster'") # By now we have a cluster, or NULL. Load the pkg under scrutiny. if ( is.null(cluster) ){ library(pkgname, character.only=TRUE) } else { parallel::clusterCall(cluster, library, pkgname, character.only=TRUE) } out <- run_test_dir(testdir, at_home=at_home, cluster=cluster,...) i_fail <- sapply(out, isFALSE) if ( any(i_fail) ){ msg <- paste( sapply(out[i_fail], format.tinytest, type="long"), collapse="\n") msg <- paste(msg, "\n") if (!interactive()) stop(msg, call.=FALSE) } else { out } } #' build, install and test #' #' Builds and installs the package in \code{pkgdir} under a temporary #' directory. Next, loads the package in a fresh R session and runs all the #' tests. For this function to work the following system requirements are #' necessary. #' \itemize{ #' \item{\code{R CMD build} is available on your system} #' \item{\code{Rscript} is available on your system} #' } #' #' @param pkgdir \code{[character]} Package directory #' @param testdir \code{[character]} Name of directory under \code{pkgdir/inst} #' containing test files. #' @param at_home \code{[logical]} toggle local tests. #' @param ncpu \code{[numeric]} number of CPUs to use during the testing phase. #' @param verbose \code{[logical]} toggle verbosity during execution #' @param remove_side_effects \code{[logical]} toggle remove user-defined side #' effects? See section on side effects. #' @param side_effects \code{[logical|list]} Either a logical, #' or a list of arguments to pass to \code{\link{report_side_effects}}. #' @param keep_tempdir \code{[logical]} keep directory where the pkg is #' installed and where tests are run? If \code{TRUE}, the directory is not #' deleted and it's location is printed. #' #' #' @return A \code{tinytests} object. #' #' @examples #' \dontrun{ #' ## If your package source directory is "./pkg" you can run #' build_install_test("pkg") #' } #' @family test-files #' @export build_install_test <- function(pkgdir="./", testdir="tinytest" , at_home=TRUE , verbose=getOption("tt.verbose",2) , ncpu = 1 , remove_side_effects=TRUE , side_effects=FALSE , keep_tempdir=FALSE){ oldwd <- getwd() tdir <- tempfile() on.exit({setwd(oldwd) if (keep_tempdir){ cat(sprintf("tempdir: %s\n",tdir)) } else { unlink(tdir, recursive=TRUE) } }) pkg <- normalizePath(pkgdir, winslash="/") pkgname <- read.dcf(file.path(pkg, "DESCRIPTION"))[1] dir.create(tdir) setwd(tdir) ## build package system2("R", args=c("CMD", "build", "--no-build-vignettes", "--no-manual", shQuote(pkg))) ## find tar.gz and install in temporary folder. pkgfile <- dir("./", pattern=paste0(pkgname, ".*\\.tar\\.gz"), full.names = TRUE) install.packages(pkgfile,lib=tdir, repos=NULL, type="source") ## In a fresh R session, load package and run tests script <- " suppressPackageStartupMessages({ pkgname <- '%s' tdir <- '%s' testdir <- '%s' at_home <- %s verbose <- %d remove_side_effects <- %s side_effects <- %s ncpu <- %d # pkgname tdir library(pkgname, lib.loc=tdir,character.only=TRUE) library('tinytest') }) if (ncpu > 1){ cluster <- parallel::makeCluster(ncpu, outfile='') parallel::clusterCall(cluster, library, pkgname, character.only=TRUE) } else { cluster <- NULL } # testdir pkgname tdir out <- run_test_dir(system.file(testdir, package=pkgname, lib.loc=tdir) , at_home=at_home , verbose=verbose , remove_side_effects=remove_side_effects , side_effects=side_effects , cluster=cluster) saveRDS(out, file='output.RDS') if (!is.null(cluster)) parallel::stopCluster(cluster) " scr <- sprintf(script , pkgname , normalizePath(tdir, winslash="/", mustWork=FALSE) , testdir , at_home , verbose , remove_side_effects , side_effects , ncpu) write(scr, file="test.R") system("Rscript test.R") readRDS(file.path(tdir, "output.RDS")) } tinytest/R/utils.R0000644000176200001440000000041613533476365013641 0ustar liggesusers # standard convenience functions catf <- function(fmt,...) cat(sprintf(fmt,...)) stopf <- function(fmt,...) stop(sprintf(fmt,...), call.=FALSE) warnf <- function(fmt,...) warning(sprintf(fmt,...), call.=FALSE) msgf <- function(fmt, ...) message(sprintf(fmt,...)) tinytest/R/expectations.R0000644000176200001440000004614213533500362015176 0ustar liggesusers # define this internally, since the desired behavior was introduced at R 3.5.0 isTRUE <- function(x){ is.logical(x) && length(x) == 1L && !is.na(x) && x } # define this internally, since it was introduced at R 3.5.0 isFALSE <- function(x){ is.logical(x) && length(x) == 1L && !is.na(x) && !x } #' Tinytest constructor #' #' #' Each individual test in the package generates a \code{tinytest} object. A #' \code{tinytest} object is a \code{logical} scalar, with metadata #' (attributes) about the test. #' #' @param result \code{[logical]} scalar. #' @param call \code{[call]} The call that created \code{result}. #' @param diff \code{[character]} difference between current and target value #' (if any). #' @param short \code{[character]} short description of the difference #' @param info \code{[character]} other information, to be printed in the long message #' @param file \code{[character]} File location of the test. #' @param fst \code{[integer]} First line number in the test file. #' @param lst \code{[integer]} Last line number in the test file (differs #' from \code{fst} if the call spans multiple lines). #' #' @section Details: #' The \pkg{result} can take three values. #' \itemize{ #' \item{\code{TRUE}: test was passed.} #' \item{\code{FALSE}: test was failed.} #' \item{\code{NA}: A side effect was detected.} #' } #' Authors of extension packages should not use \code{NA} as a result value as #' this part of the interface may change in the future. #' #' #' @return A \code{tinytest} object. #' @family extensions #' #' @examples #' tt <- expect_equal(1+1, 2) #' if (isTRUE(tt)){ #' print("w00p w00p!") #' } else { #' print("Oh no!") #' } #' #' #' #' @keywords internal #' @export tinytest <- function(result, call , diff = NA_character_ , short= NA_character_ , info = NA_character_ , file = NA_character_ , fst = NA_integer_ , lst = NA_integer_ ,...){ structure(result # logical TRUE/FALSE , class = "tinytest" , call = call # call creating or motivating the object , diff = diff # diff if isFALSE(result) , short = short # short diff (4 char) , info = info # user-defined info , file = file # test file location , fst = fst # first line of call , lst = lst # last line of call , ...) } na_str <- function(x) if ( is.na(x) ) "" else as.character(x) oneline <- function(x) sub("\\n.+","...",x) indent <- function(x, with=" "){ gsub("\\n *",paste0("\n",with),paste0(with,sub("^ +","",x))) } lineformat <- function(x){ if ( is.na(x) ) "" else sprintf("%d",x) } #' @param type \code{[logical]} Toggle format type #' #' @return A character string #' #' #' @rdname print.tinytest #' @export #' #' @examples #' tt <- expect_equal(1+1, 3) #' format(tt,"long") #' format(tt,"short") format.tinytest <- function(x,type=c("long","short"), ...){ type <- match.arg(type) d <- attributes(x) call <- paste0(deparse(d$call), collapse="\n") fst <- lineformat(d$fst, ...) lst <- lineformat(d$lst, ...) file <- na_str(d$file) short <- na_str(d$short) diff <- d$diff info <- na_str(d$info) result <- if (isTRUE(x)) "PASSED " else if (isFALSE(x)) sprintf("FAILED[%s]",short) else if (is.na(x) ) sprintf("SIDEFX[%s]",short) longfmt <- "----- %s: %s<%s--%s>\n%s" # make room for diff and info fields when necessary if (isFALSE(x)||is.na(x)) longfmt <- paste0(longfmt, "\n%s") if (!is.na(d$info)) longfmt <- paste0(longfmt,"\n%s") if (type == "short"){ sprintf("%s: %s<%s--%s> %s", result, basename(file), fst, lst, oneline(call)) } else { sprintf(longfmt, result, file, fst, lst , indent(call, with=" call| ") , indent(diff, with=" diff| ") , indent(info, with=" info| ")) } } #' Print a tinytest object #' #' @param x A \code{tinytest} object #' @param ... passed to \code{\link{format.tinytest}} #' #' @examples #' print(expect_equal(1+1, 2)) #' print(expect_equal(1+1, 3), type="long") #' #' @export print.tinytest <- function(x,...){ cat(format.tinytest(x,...),"\n") } is_atomic <- function(x){ inherits(x,"POSIXct") || ( length(class(x)) == 1 && class(x) %in% c( "character" , "logical" , "factor" , "ordered" , "integer" , "numeric" , "complex") ) } is_scalar <- function(x){ length(x) == 1 && is_atomic(x) } # alt: alternative output longdiff <- function(current, target, alt){ if ( identical(class(current), class(target)) && is_scalar(current) && is_scalar(target) ){ if ( all(class(current) %in% c("character","ordered","factor", "POSIXt","POSIXct")) ) sprintf("Expected '%s', got '%s'", target, current) else sprintf("Expected %s, got %s", target, current) } else { paste0(" ", alt, collapse="\n") } } # are there differences in data and/or attributes, or just in the attributes? shortdiff <- function(current, target, ...){ equivalent_data <- all.equal(target, current , check_attributes=FALSE , use.names=FALSE,...) if (isTRUE(equivalent_data)) "attr" else "data" } #' Express expectations #' #' @param current \code{[R object or expression]} Outcome or expression under scrutiny. #' @param target \code{[R object or expression]} Expected outcome #' @param tol \code{[numeric]} Test equality to machine rounding. Passed #' to \code{\link[base]{all.equal} (tolerance)} #' @param info \code{[character]} scalar. Optional user-defined message. Must #' be a single character string. Multiline comments may be separated by #' \code{"\\n"}. #' @param ... Passed to \code{all.equal} #' #' @return A \code{\link{tinytest}} object. A tinytest object is a #' \code{logical} with attributes holding information about the #' test that was run #' #' @note #' Each \code{expect_haha} function can also be called as \code{checkHaha}. #' Although the interface is not entirely the same, it is expected that #' this makes migration from the \code{RUnit} framework a little easier, for those #' who wish to do so. #' #' @section More information and examples: #' #' \itemize{ #' \item{An overview of tinytest can be found in \code{vignette("using_tinytest")}}. #' \item{Examples of how tinytest is used in practice can be found in #' \code{vignette("tinytest_examples")}} #' } #' @family test-functions #' #' @examples #' expect_equal(1 + 1, 2) # TRUE #' expect_equal(1 - 1, 2) # FALSE #' expect_equivalent(2, c(x=2)) # TRUE #' expect_equal(2, c(x=2)) # FALSE #' #' @export expect_equal <- function(current, target, tol = sqrt(.Machine$double.eps), info=NA_character_, ...){ check <- all.equal(target, current, tol=tol, ...) equal <- isTRUE(check) diff <- if (equal) NA_character_ else longdiff( current, target, check) short <- if (equal) NA_character_ else shortdiff(current, target, tolerance=tol) tinytest(result = equal, call = sys.call(sys.parent(1)), diff=diff, short=short, info=info) } #' @rdname expect_equal #' @export expect_identical <- function(current, target, info=NA_character_){ result <- identical(current, target) diff <- if (result) NA_character_ else longdiff(current, target, all.equal(target, current)) short <- if (result) NA_character_ else shortdiff(current, target, tolerance=0) tinytest(result=result, call=sys.call(sys.parent(1)), diff=diff , short=short, info=info) } #' @details #' \code{expect_equivalent} calls \code{expect_equal} with the extra #' arguments \code{check.attributes=FALSE} and \code{use.names=FALSE} #' #' #' @rdname expect_equal #' @export expect_equivalent <- function(current, target, tol = sqrt(.Machine$double.eps) , info=NA_character_, ...){ out <- expect_equal(current, target , check.attributes=FALSE,use.names=FALSE , tol=tol, info=info, ...) attr(out, 'call') <- sys.call(sys.parent(1)) out } #' @rdname expect_equal #' @export expect_true <- function(current, info=NA_character_){ result <- isTRUE(current) call <- sys.call(sys.parent(1)) if (!result){ this <- if ( isFALSE(current) ) "FALSE" else if (is.logical(current)) sprintf("'logical' of length %d",length(current)) else sprintf("object of class '%s'",class(current)) diff <- sprintf("Expected TRUE, got %s", this) short <- shortdiff(TRUE, FALSE) tinytest(result, call=call,diff=diff, short=short, info=info) } else { tinytest(result, call = sys.call(sys.parent(1)), info=info) } } #' @rdname expect_equal #' @export expect_false <- function(current, info=NA_character_){ result <- isFALSE(current) call <- sys.call(sys.parent(1)) if (!result){ this <- if ( isFALSE(current) ) "TRUE" else if (is.logical(current)) sprintf("'logical' of length %d",length(current)) else sprintf("object of class '%s'",class(current)) diff <- "Expected FALSE, got TRUE" short <- shortdiff(TRUE, FALSE) tinytest(result, call=call,diff=diff, short=short, info=info) } else { tinytest(result, call = sys.call(sys.parent(1)), info=info) } } #' @rdname expect_equal #' #' @param quiet \code{[logical]} suppress output printed by the \code{current} #' expression (see examples) #' #' @details #' #' \code{expect_silent} fails when an error or warning is thrown. #' #' @examples #' #' expect_silent(1+1) # TRUE #' expect_silent(1+"a") # FALSE #' expect_silent(print("hihi")) # TRUE, nothing goes to screen #' expect_silent(print("hihi", quiet=FALSE)) # FALSE, and printed #' #' @export expect_silent <- function(current, quiet=TRUE, info=NA_character_){ ## Make sure that printed output does not go to screen. # nullfile() was introduced at 3.6.0 and we want to be usable # on older releases as well. has_nullfile <- exists("nullfile") if (quiet){ # we need to use 'do.call' to avoid a NOTE on r-oldrel dumpfile <- if(has_nullfile) do.call("nullfile", list()) else tempfile() sink(dumpfile) } # clean up on.exit({ if (quiet){ sink(NULL) if (!has_nullfile) unlink(dumpfile) } }) # try to evaluate 'current' if that doesn't work properly, store # error or warning message. result <- TRUE msg <- "" type <- "none" tryCatch(current , error = function(e){ result <<- FALSE msg <<- e$message type <<- "An error" } , warning = function(w){ result <<- FALSE msg <<- w$message type <<- "A warning" } ) call <- sys.call(sys.parent(1)) diff <- if (msg != ""){ sprintf("Execution was not silent. %s was thrown with message\n '%s'",type,msg) } else { NA_character_ } tinytest(result , call = sys.call(sys.parent(1)) , short = if (result) NA_character_ else "xcpt" , diff = diff , info = info ) } #' @rdname expect_equal #' @export expect_null <- function(current, info=NA_character_){ call <- sys.call(sys.parent(1)) if (is.null(current)){ tinytest(TRUE, call=call, info=info) } else { tinytest(FALSE, call=call, short="data" , diff = sprintf("Expected NULL, got '%s'", paste(class(current), collapse=", ")) , info = info ) } } #' @rdname expect_equal #' @param pattern \code{[character]} A regular expression to match the message. #' @export expect_error <- function(current, pattern=".*", info=NA_character_){ result <- FALSE diff <- "No error" tryCatch(current, error=function(e){ if (grepl(pattern, e$message)){ result <<- TRUE } else { diff <<- sprintf("The error message:\n '%s'\n does not match pattern '%s'" , e$message, pattern) } }) tinytest(result, call = sys.call(sys.parent(1)) , short= if(result) NA_character_ else "xcpt" , diff = if(result) NA_character_ else diff , info = info) } #' @rdname expect_equal #' @export expect_warning <- function(current, pattern=".*", info=NA_character_){ result <- FALSE diff <- "No warning" withCallingHandlers(current , warning = function(w){ if (grepl(pattern, w$message)){ result <<- TRUE } else { diff <<- sprintf("The warning message\n '%s'\n does not match pattern '%s'" , w$message, pattern) } invokeRestart("muffleWarning") } ) tinytest(result, call=sys.call(sys.parent(1)) , short = if (result) NA_character_ else "xcpt" , diff = if (result) NA_character_ else diff , info = info) } #' @rdname expect_equal #' @export expect_message <- function(current, pattern=".*", info=NA_character_){ value <- "" tc <- textConnection("value", open="w", local=TRUE) sink(file=tc,type="message", split=FALSE) result <- TRUE msg <- "" tryCatch(current , error = function(e){ result <<- FALSE msg <<- sprintf("Expected message, got error:\n '%s'",e$message) } , warning = function(w){ result <<- FALSE msg <<- paste(sprintf("Expected message, got warning:\n '%s'", w$message) , collapse="\n") } ) sink(file = NULL, type="message") close(tc) # collapse the value string in case multiple messages were caught. value <- paste(value, collapse="\n") call <- sys.call(sys.parent(1)) # we got a warning or error instead of a message: if (!result){ tinytest( result , call , diff = msg , short = "xcpt" , info = info ) # we got a message, check if it matches 'pattern' } else if (!isTRUE(grepl(pattern, value)) ){ df <- if (value == "") "No message" else sprintf("The message\n '%s'\n doen not match pattern '%s'",value,pattern) tinytest(FALSE , call , diff = df , short = "xcpt" , info = info ) } else { tinytest(TRUE, call, info=info) } } #' Report side effects for expressions in test files #' #' Call this function from within a test file to report side effects. #' #' @param report \code{[logical]} report all side-effects #' @param envvar \code{[logical]} changes in environment variables #' @param pwd \code{[logical]} changes in working directory #' @param files \code{[logical]} changes in files in the directory where the #' test file lives. Also watches subdirectories. #' #' @section Details: #' A side effect causes a change in an external variable outside of the scope #' of a function, or test file. This includes environment variables, global #' options, global R variables, creating files or directories, and so on. #' #' If this function is called in a test file, side effects are monitored from #' that point in the file and only for that file. The state of the environment #' before and after running every expression in the file are compared. #' #' There is some performance penalty in tracking external variables especially #' those that require a system call. #' #' @section Note: #' There could be side-effects that are untrackable by \pkg{tinytest}. This includes #' packages that use a global internal state within their namespace or packages #' that use a global state within compiled code. #' #' @family sidefx #' #' @return A named \code{logical}, indicating which aspects of the environment #' are tracked, invisibly. #' #' @examples #' # switch on #' report_side_effects() #' # switch off #' report_side_effects(FALSE) #' #' # only report changes in environment variables #' report_side_effects(pwd=FALSE) #' #' @export report_side_effects <- function(report=TRUE, envvar=report, pwd=report, files=report){ stopifnot(is.logical(envvar)) list(envvar=envvar, pwd=pwd, files=files) } # generate user-facing function that captures 'report_side_effects' capture_se <- function(fun, env){ function(...){ out <- fun(...) env$sidefx <- out if (out[['envvar']]) env$envvar <- Sys.getenv() if (out[['pwd']]) env$pwd <- getwd() if (out[['files']]){ env$filesdir <- getwd() env$files <- file.info(dir(env$filesdir, recursive=TRUE, full.names=TRUE)) } out } } # internal function, to be called by run_test_file after local capture. report_envvar <- function(env){ if ( !isTRUE(env$sidefx[['envvar']]) ) return(NULL) old <- env$envvar current <- Sys.getenv() if (identical(old, current)) return(NULL) out <- envdiff(env$envvar, current) env$envvar <- current out } # old and new are Dlist variables, resulting from # calls to Sys.getenv(). The output is a string reporting # added, removed, changed environment variables. Each report # separated by a newline \n envdiff <- function(old, new){ if (identical(old,new)) return() old.vars <- names(old) new.vars <- names(new) removed <- setdiff(old.vars, new.vars) added <- setdiff(new.vars, old.vars) survived <- intersect(old.vars, new.vars) changed <- survived[ old[survived] != new[survived] ] rem <- if (length(removed) == 0 ) NULL else sprintf("Removed envvar '%s' with value '%s'", removed, old[removed]) if(!is.null(rem)) rem <- paste(rem, collapse="\n") add <- if (length(added) == 0) NULL else sprintf("Added envvar '%s' with value '%s'", added, new[added]) if (!is.null(add)) add <- paste(add, collapse="\n") cng <- if ( length(changed) == 0 ) NULL else sprintf("Changed envvar '%s' from '%s' to '%s'" , changed, old[changed], new[changed]) if (!is.null(cng)) cng <- paste(cng, collapse="\n") long <- paste(c(rem, add, cng),collapse="\n") if (long == "") return() tinytest(NA , call = sys.call(sys.parent(1)) , diff = long , short = "envv" ) } # internal function, to be called by run_test_file after local capture. report_cwd <- function(env){ if ( !isTRUE(env$sidefx[['pwd']]) ) return(NULL) old <- env$pwd current <- getwd() if ( identical(old, current) ) return(NULL) msg <- sprintf("Working directory changed from \n '%s'\nto\n '%s'", old, current) out <- tinytest(NA , call = sys.call(sys.parent(1)) , short = "wdir" , diff = msg ) env$pwd <- current out } report_files <- function(env){ if (!isTRUE(env$sidefx[['files']])) return(NULL) old <- env$files new <- file.info(dir(env$filesdir, recursive=TRUE, full.names=TRUE)) if ( identical(old, new) ) return(NULL) on.exit(env$files <- new) oldfiles <- rownames(old) newfiles <- rownames(new) created <- setdiff(newfiles, oldfiles) removed <- setdiff(oldfiles, newfiles) remain <- intersect(oldfiles, newfiles) touched <- remain[old[remain,'mtime'] != new[remain, 'mtime']] cre <- sprintf("Created: %s", if (length(created)>0) paste(created, collapse=", ") else character(0)) rem <- sprintf("Removed: %s", if (length(removed)>0) paste(removed, collapse=", ") else character(0)) alt <- sprintf("Touched: %s", if (length(touched)>0) paste(touched, collapse=", ") else character(0)) diff <- paste(c(cre, rem, alt), collapse="\n") # we do not record status changes, as they may mean different things # on different OSs. if (nchar(diff) == 0) return(NULL) tinytest(NA , call = sys.call(sys.parent(1)) , diff = diff , short = "file" , info = "CRAN policy forbids writing in the package installation folder." ) } tinytest/R/setup.R0000644000176200001440000001031513540176247013632 0ustar liggesusers #' Add tinytest to package source directory #' #' Creates \code{inst/tinytest}, and an example test file in that #' directory. Creates \code{tests/tinytest.R} so the package is #' tested with \code{R CMD check}. Adds \code{tinytests} as a suggested #' package to the \code{DESCRIPTION}. #' #' @param pkgdir \code{[character]} Package source directory #' @param force \code{[logical]} Toggle overwrite existing files? (not folders) #' @param verbose \code{[logical]} Toggle print progress #' #' @section Note on \code{DESCRIPTION}: #' #' Fails when it does not exist. It is assumed that the #' package is named in the \code{DESCRIPTION}. #' #' #' @examples #' \dontrun{ #' # an easy way to set up a package 'haha' that passes #' # R CMD check #' pkgKitten::kitten("haha") #' tinytest::setup_tinytest("haha") #'} #' #' @return \code{NULL}, invisibly. #' #' @export setup_tinytest <- function(pkgdir, force=FALSE, verbose=TRUE){ # local, verbosity-aware catf catf <- function(fmt, ...) if (verbose) cat(sprintf(fmt,...)) if (!dir.exists(pkgdir)){ stopf("%s does not exist or is not a directory", pkgdir) } # fields in DESCRIPTION that escape reformatting kw <- c("Title" , "Maintainer" , "Authors", "Authors@R" , "Description" , "Depends" , "Imports" , "Suggests" , "Enhances") ## Get pkg name form DESCRIPTION dfile <- file.path(pkgdir,"DESCRIPTION") if (file.exists(dfile)){ dcf <- read.dcf(dfile, keep.white=kw) pkgname <- dcf[, "Package"] } else { stopf("No DESCRIPTION file in %s",pkgdir) } ## Create pkgdir/tests testdir <- file.path(pkgdir,'tests') if ( !dir.exists(testdir) ){ catf("Creating %s\n", testdir) dir.create(testdir) } ## Write pkgdir/tests/tinytest.R testfile <- file.path(testdir,"tinytest.R") test_statement <- sprintf(' if ( requireNamespace("tinytest", quietly=TRUE) ){ tinytest::test_package("%s") } ', pkgname) if ( !file.exists(testfile) || force ){ catf("Creating %s\n", testfile ) write(test_statement, file = testfile) } ## Create inst/tinytest # (dir.create with recursive=TRUE does not always work # on the OS that we shall not name) instdir <- file.path(pkgdir, "inst") if (!dir.exists(instdir)){ catf("Creating %s\n", instdir) dir.create(instdir) } ttdir <- file.path(instdir,"tinytest") if (!dir.exists(ttdir)){ catf("Creating %s\n",ttdir) dir.create(ttdir) } ## Write example test file example_test <- ' # Placeholder with simple test expect_equal(1 + 1, 2) ' ttfile <- file.path(ttdir, sprintf("test_%s.R",pkgname)) if ( !file.exists(ttfile) || force ){ catf("Creating %s\n", ttfile) write(example_test, file=ttfile) } ## Add tinytest to DESCRIPTION file suggests <- if ("Suggests" %in% colnames(dcf)) dcf[1,"Suggests"] else NA if (!is.na(suggests) && !grepl("tinytest",suggests)){ catf("Adding 'tinytest' to DESCRIPTION/Suggests\n") dcf[1,"Suggests"] <- sprintf("%s, tinytest",suggests) write.dcf(dcf, dfile, keep.white=kw) } else if ( is.na(suggests) ) { catf("Adding 'Suggests: tinytest' to DESCRIPTION\n") dcf <- cbind(dcf, Suggests = "tinytest") write.dcf(dcf, dfile, keep.white=kw) } # If another test package is already present, perhaps the user # wants to take it out. other_test_package <- c("RUnit","testthat","unity","testit") suggested <- trimws(strsplit(dcf[1,"Suggests"], ",")[[1]]) if (any(other_test_package %in% suggested)){ pkgs <- paste(other_test_package[other_test_package %in% suggested], collapse=", ") catf("You may want to remove the following packages from DESCRIPTION/Suggests: %s\n", pkgs) } invisible(NULL) } #' The puppy for a pkgKitten #' #' Does exactly the same as \code{\link{setup_tinytest}}, but prints #' a loving message aferwards (and who doesn't want that!?). Just #' think about those puppies. #' #' @inheritParams setup_tinytest #' #' #' @keywords internal #' @export puppy <- function(pkgdir, force=FALSE, verbose=TRUE){ setup_tinytest(pkgdir=pkgdir, force=force, verbose=verbose) catf("\nThank you %s, for showing us some PUPPY LOVE <3\n",Sys.info()["user"]) catf(doggy) } doggy <- " ,-.___,-. \\_/_ _\\_/ )O_O( { (_) } W00F! `-^-' " tinytest/R/init.R0000644000176200001440000000032213500000356013411 0ustar liggesusers .onLoad <- function(libname, pkgname){ # turn off color printing for dumb terminals term <- tolower(trimws(Sys.getenv("TERM"))) if ( identical( term, "dumb" ) ){ options(tt.pr.color=FALSE) } } tinytest/MD50000644000176200001440000000614213543153442012454 0ustar liggesusers97f305baf0d3e87a829b2e5b9a3f19fb *DESCRIPTION 971fd160fae6d8a13cdd569ed511efb8 *NAMESPACE 29f691365e4371a743a2edcac5cb905e *NEWS 38b9b79c588dfb4867f5549bbe9eaae2 *R/expectations.R 50e7d02026bee5ac8906b17c7deda44d *R/init.R 94810950d25a672165a99b970ee2b6bd *R/methods.R 8021b11aa26ca42b3059c28f1718d99c *R/setup.R 04abf52e313b52cc18396e803777fd3c *R/tinytest.R 83e7c8ffe7489e0c8ef7b224b4095faf *R/utils.R d614ee5ab779f65c48bb74db8174b06a *README.md bb6ae387f2d0fcb7ef12c9c30f0fbd77 *build/vignette.rds cb00f80749ddc84c1371912b6543dd15 *inst/doc/tinytest_examples.R 90f04266dc338470215e55dfbedbe45c *inst/doc/tinytest_examples.Rnw 6353b73f2f9adfcec59f8007488a4364 *inst/doc/tinytest_examples.pdf 55cf8c4167708877450d7745a27ffb33 *inst/doc/using_tinytest.R 61573109582a2aeaf8c5f273d6ec7006 *inst/doc/using_tinytest.Rnw 889a834c91330020ed4de9d47015388b *inst/doc/using_tinytest.pdf 804b7fef290add675b038cbfc14c71b8 *inst/tinytest/programming.R abcdbcac0c19d4ef571b1957f218d53b *inst/tinytest/runs/test_cwd.R 0fa0632726cbcf95aa903bb51c897299 *inst/tinytest/runs/test_cwd2.R de279aea5113e0adfa6ce18d028f4de5 *inst/tinytest/runs/test_envvar.R 54bb53210a247363436e18d9ec743f41 *inst/tinytest/runs/test_envvar2.R ca0ed1ec5984d8cfbd791f6f4f2103e5 *inst/tinytest/runs/test_exit.R 45cbd2a36488b882408595be6a15b70f *inst/tinytest/test_RUnit_style.R 48ea9ab33a50742606afcb916581158b *inst/tinytest/test_env_A.R 48482c2682b5825157b37b63d7f6f02b *inst/tinytest/test_env_B.R 24d27c406f6097f3da278bb8c8237529 *inst/tinytest/test_extensibility.R 88d031b0289331e62eeb5b84bb67bf35 *inst/tinytest/test_file.R 33c1d437110b761107472fa87633a3ba *inst/tinytest/test_gh_issue_17.R f6a223d1b1d4892ffb11411d40bf7f0c *inst/tinytest/test_gh_issue_32.R b2050f6a8a6c1c222abd5b0878263084 *inst/tinytest/test_init.R 497b03acd7a04f9b7e3da943d723ebdd *inst/tinytest/test_tiny.R a909aa6993cd8a1b12438d59b0847e8e *inst/tinytest/test_utils.R 3d582c8039e89e68e63786c986ac065f *inst/tinytest/women.csv 1f168031ef73faa1f161eb17ff126eb8 *man/at_home.Rd f5c556bdb8338e68bb9c5b68adec52c4 *man/build_install_test.Rd 601940f1e7d364b4c864758e1945339e *man/exit_file.Rd 2d1f22e4b62233cd1324f1ce8ec1640d *man/expect_equal.Rd 48f3086ffecffdd6c81dfb5babab01be *man/ignore.Rd 13dad6d18051767155068fd25820f278 *man/print.tinytest.Rd 5bdc486bde1eb9ee58d791a1b699857e *man/puppy.Rd b5fd2262867b491e6a318ae2f9c39b68 *man/register_tinytest_extension.Rd d1162fb9b96072b573b6d6974fca718f *man/report_side_effects.Rd 1b010460b8eaf9f123494bf33aa7e5ec *man/run_test_dir.Rd 2333bd01e0bc39bd4d5fc7421971ffe5 *man/run_test_file.Rd 1fb123baf551b15ce40b97c3172016d0 *man/setup_tinytest.Rd bebb0eda85b92b8519d915055b84d087 *man/test_package.Rd 56d5d4474cbdb72271c487f62efe6598 *man/tinytest.Rd a8912ac63669eaf109a17e86dab68e3d *man/tinytests.Rd 31d381826fa700b7e3ed345d6c2463dc *man/using.Rd 49c954001384bac33f4dbcfaf35aa650 *tests/tinytest.R fe66ea1ee46d6e95c07c6de8f4961f63 *vignettes/test_addOne.R 283331cc9329d1e36976470087adfa25 *vignettes/test_hehe.R 1398bc6679832399a5e2a8b0666b590f *vignettes/test_se.R 90f04266dc338470215e55dfbedbe45c *vignettes/tinytest_examples.Rnw 61573109582a2aeaf8c5f273d6ec7006 *vignettes/using_tinytest.Rnw tinytest/inst/0000755000176200001440000000000013543151031013106 5ustar liggesuserstinytest/inst/doc/0000755000176200001440000000000013543151031013653 5ustar liggesuserstinytest/inst/doc/using_tinytest.Rnw0000644000176200001440000010045713532276246017460 0ustar liggesusers%\VignetteIndexEntry{Using tinytest} \documentclass[11pt]{article} \usepackage{enumitem} \usepackage{xcolor} % for color definitions \usepackage{sectsty} % to modify heading colors \usepackage{fancyhdr} \setlist{nosep} % simpler, but issue with your margin notes \usepackage[left=1cm,right=3cm, bottom=2cm, top=1cm]{geometry} %\usepackage{vmargin} %\setpapersize{USletter} % or a4 for you % Left Top Right Bottom headheight?? headsep footheight footsep %\setmarginsrb{0.75in}{0.25in}{1.1in}{0.25in}{15pt}{0pt}{10pt}{20pt} \usepackage{hyperref} \definecolor{bluetext}{RGB}{0,101,165} \definecolor{graytext}{RGB}{80,80,80} \hypersetup{ pdfborder={0 0 0} , colorlinks=true , urlcolor=blue , linkcolor=bluetext , linktoc=all , citecolor=blue } \sectionfont{\color{bluetext}} \subsectionfont{\color{bluetext}} \subsubsectionfont{\color{bluetext}} % no serif=better reading from screen. \renewcommand{\familydefault}{\sfdefault} % header and footers \pagestyle{fancy} \fancyhf{} % empty header and footer \renewcommand{\headrulewidth}{0pt} % remove line on top \rfoot{\color{bluetext} tinytest \Sexpr{packageVersion("tinytest")}} \lfoot{\color{black}\thepage} % side-effect of \color{}: lowers the printed text a little(?) \usepackage{fancyvrb} % custom commands make life easier. \newcommand{\code}[1]{\texttt{#1}} \newcommand{\pkg}[1]{\textbf{#1}} \let\oldmarginpar\marginpar \renewcommand{\marginpar}[1]{\oldmarginpar{\color{bluetext}\raggedleft\scriptsize #1}} % skip line at start of new paragraph \setlength{\parindent}{0pt} \setlength{\parskip}{1ex} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \title{Using tinytest} \author{Mark van der Loo} \date{\today{} | Package version \Sexpr{packageVersion("tinytest")}} \begin{document} \DefineVerbatimEnvironment{Sinput}{Verbatim}{fontshape=n,formatcom=\color{graytext}} \DefineVerbatimEnvironment{Soutput}{Verbatim}{fontshape=sl,formatcom=\color{graytext}} \newlength{\fancyvrbtopsep} \newlength{\fancyvrbpartopsep} \makeatletter \FV@AddToHook{\FV@ListParameterHook}{\topsep=\fancyvrbtopsep\partopsep=\fancyvrbpartopsep} \makeatother \setlength{\fancyvrbtopsep}{0pt} \setlength{\fancyvrbpartopsep}{0pt} \maketitle{} \thispagestyle{empty} \tableofcontents{} <>= options(prompt="R> ", continue = " ", width=75) @ \subsection*{Reading guide} Readers of this document are expected to know how to write R functions and have a basic understanding of a package source directory structure. \newpage{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Purpose of this package: unit testing} The purpose of \emph{unit testing} is to check whether a function gives the output you expect, when it is provided with certain input. So unit testing is all about comparing \emph{desired} outputs with \emph{realized} outputs. The purpose of this package is to facilitate writing, executing and analyzing unit tests. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Expressing tests} Suppose we define a function translating pounds (lbs) to kilograms inaccurately. <<>>= lbs2kg <- function(x){ if ( x < 0 ){ stop(sprintf("Expected nonnegative weight, got %g",x)) } x/2.20 } @ We like to check a few things before we trust it. <<>>= library(tinytest) expect_equal(lbs2kg(1), 1/2.2046) expect_error(lbs2kg(-3)) @ The value of an \code{expect\_*} function is a \code{logical}, with some attributes that record differences, if there are any. These attributes are used to pretty-print the results. <<>>= isTRUE( expect_true(2 == 1 + 1) ) @ \subsection{Test functions} Currently, the following expectations are implemented. \begin{center} \begin{tabular}{ll} \textbf{Function} & \textbf{what it expects}\\ \code{expect\_equal(current, target)} & equality (using \code{all.equal})\\ \code{expect\_equivalent(current, target)} & equality, ignoring attributes\\ \code{expect\_identical(current, target)} & equality, (using, \code{identical})\\ \code{expect\_true(current)} & \code{current} evaluates to \code{TRUE}\\ \code{expect\_false(current)} & \code{current} evaluates to \code{FALSE}\\ \code{expect\_null(current)} & \code{current} evaluates to \code{NULL}\\ \code{expect\_error(current, pattern)} & error message matching \code{pattern}\\ \code{expect\_warning(current, pattern)} & warning message matching \code{pattern}\\ \code{expect\_message(current, pattern)} & message matching \code{pattern}\\ \code{expect\_silent(current, pattern)} & expect no warnings or errors (just run)\\ \end{tabular} \end{center} Here, \code{target} is the intended outcome and \code{current} is the observed outcome. Also, \code{pattern} is interpreted as a regular expression. <<>>= expect_error(lbs2kg(-3), pattern="nonnegative") expect_error(lbs2kg(-3), pattern="foo") @ \subsection{Alternative syntax} The syntax of the test functions should be familiar to users of the \code{testthat} package\cite{wickham2016testthat}. In test files only, you can use equivalent functions in the style of \code{RUnit}\cite{burger2016RUnit}. To be precise, for each function of the form \code{expect\_lol} there is a function of the form \code{checkLol}. \subsection{Interpreting the output and print options} Let's have a look at an example again. <<>>= expect_false( 1 + 1 == 2, info="My personal message to the tester" ) @ The output of these functions is pretty self-explanatory, nevertheless we see that the output of these expect-functions consist of \begin{itemize} \item The result: \code{FAILED}, \code{PASSED} or \code{SIDEFX}. The latter only occurs when side effects are monitored (see \S\ref{sect:side}) \item The type of failure (if any) between square brackets. Current options are as follows. \begin{itemize} \item \code{[data]} there are differences between observed and expected values. \item \code{[attr]} there are differences between observed and expected attributes, such as column names. \item \code{[xcpt]} an exception (warning, error) was expected but not observed. \end{itemize} When side effects are monitored, and the result is \code{SIDEFX}, a side effect was observed. The type of side effect is reported between square brackets. \begin{itemize} \item \code{[envv]} An environmental variable was created, changed, or deleted. \item \code{[wdir]} The working directory has changed. \item \code{[file]} A file operation occurred in the test directory or one of its subdirectories. \end{itemize} \item When relevant (see \S\ref{sect:testfiles}), the location of the test file and the relevant line numbers. \item The test call. \item When relevant, a summary of the differences between observed and expected values or attributes, or a summary of the observed side effect. \item When present, a user-defined information message. \end{itemize} The result of an \code{expect\_} function is a \code{tinytest} object. You can print them in long format (default) or in short, one-line format like so. <<>>= print(expect_equal(1+1, 3), type="short") @ \marginpar{\code{print} method} Functions that run multiple tests return an object of class \code{tinytests} (notice the plural). Since there may be a lot of test results, \pkg{tinytest} tries to be smart about printing them. The user has ultimate control over this behaviour. See \code{?print.tinytests} for a full specification of the options. \section{Test files} \label{sect:testfiles} In \pkg{tinytest}, tests are scripts, interspersed with statements that perform checks. An example test file in tinytest can look like this. \begin{verbatim} # contents of test_addOne.R addOne <- function(x) x + 2 expect_true(addOne(0) > 0) hihi <- 1 expect_equal(addOne(hihi), 2) \end{verbatim} A particular file can be run using\marginpar{\code{run\_test\_file}} <>= run_test_file("test_addOne.R", verbose=0) @ We use \code{verbose=0} to avoid cluttering the output in this vignette. By default, verbosity is turned on, and a counter is shown while tests are run. The counter is colorized on terminals supporting ANSI color extensions. If you are uncomfortable reading these colors or prefer colorless output, use \code{color=FALSE} or set \code{options(tt.pr.color=FALSE)}. The numbers between \code{<-->} indicate at what lines in the file the failing test can be found. By default only failing tests are printed. You can store the output and print all of them. <<>>= test_results <- run_test_file("test_addOne.R", verbose=0) print(test_results, passes=TRUE) @ Or you can set <>= options(tt.pr.passes=TRUE) @ to print all results during the active R session. To run all test files in a certain directory, we can use\marginpar{\code{run\_test\_dir}} <>= run_test_dir("/path/to/your/test/directory") @ By default, this will run all files of which the name starts with \code{test\_}, but this is customizable. \subsection{Summarizing test results, getting the data} To create some results, run the tests in this package. <<>>= out <- run_test_dir(system.file("tinytest", package="tinytest") , verbose=0) @ The results can be turned into data using \code{as.data.frame}. \marginpar{\code{as.data.frame}} <<>>= head(as.data.frame(out), 3) @ The last two columns indicate the line numbers where the test was defined. A `summary` of the output gives a table with passes and fails per file. \marginpar{\code{summary}} <<>>= summary(out) @ \subsection{Programming over tests, ignoring test results, exiting early} Test scripts are just R scripts interspersed with tests. The test runners make sure that all test results are caught, unless you tell them not to. For example, since the result of a test is a \code{logical} you can use them as a condition. <>= if ( expect_equal(1 + 1, 2) ){ expect_true( 2 > 0) } @ Here, the second test (\code{expect\_true(2 > 0)}) is only executed if the first test results in \code{TRUE}. In any case the result of the first test will be caught in the test output, when this is run with \code{run\_test\_file} \code{run\_test\_dir}, \code{test\_all}, \code{build\_install\_test} or through \code{R CMD check} using \code{test\_package}. If you want to perform the test, but not record the test result you can do the following \marginpar{\code{ignore}} (note the placement of the brackets). <>= if ( ignore(expect_equal)(1+1, 2) ){ expect_true(2>0) } @ Other cases where this may be useful is to perform tests in a loop, e.g. when there is a systematic set of cases to test. It is possible to exit a test file prematurely. For example when there are a number of tests that are not relevant or possible on some OS, you can do the following. \marginpar{\code{exit\_file}} <<>>= if ( Sys.info()[['sysname']] == "Windows"){ exit_file("Cannot test this on Windows") } @ This will cause \code{run\_test\_file} to stop file execution, print the message, and report the information gathered up to where \code{exit} was called. A function like \code{test\_all} will then continue with the next file, so testing is not aborted completely. \subsection{Running order and side effects} \label{sect:running} It is a generally a good idea to write test files that are independent from each other. This means that the order of running them is unimportant for the test results and test files can be maintained independently. The function \code{run\_test\_file} and by extension \code{run\_test\_dir}, \code{test\_all}, and \code{test\_package} encourage this by resetting \begin{itemize} \item options, set with \code{options()}; \item environment variables, set with \code{Sys.setenv()} \end{itemize} after a test file is executed. To escape this behavior, use \code{base::Sys.setenv()} respectively \code{base::options()}. Alternatively use <>= run_test_dir("/path/to/my/testdir" , remove_side_effects = FALSE) test_all("/path/to/my/testdir" , remove_side_effects = FALSE) # Only in tests/tinytest.R: test_package("PACKAGENAME", remove_side_effects=FALSE) @ Test files are sorted and run based on the current locale. This means that the order of execution is in general not platform-independent. You can control the sorting behavior interactively or by setting \code{options(tt.collate)}. To be precise, adding <>= options(tt.collate="C") @ to \code{/tests/tinytest.R} before running \code{test\_package} will ensure bytewise sorting on most systems. See also \code{help("run\_test\_dir")}. \subsection{Monitoring side effects} \label{sect:side} The term 'side effect' is the technical expression for the situation where a function or expression changes something outside of its scope. Examples include creating, removing, or changing variables in R's global work space, R options, or environment variables of your operating system. We will call such variables or options \emph{external variables}. To test for side-effects once, use the \code{side\_effects} argument to any of the test runners. For example <>= test_package("pkg", side_effects=TRUE) @ There is control over which side-effects to track. For example to prevent tracking changes in the working directory, do the following. <>= test_package("pkg", side_effects=list(pwd=FALSE)) @ If you add \code{report\_side\_effects()} anywhere in a test file, certain external variables are monitored from that point on, and for that file only. It can be switched off again by calling \code{report\_side\_effects(FALSE)} anywhere in the file. The reporting functionality will compare the state of external variables before and after every expression in the test file and report any changes. At the moment, the variables that can be monitored include environment variables and the present working directory. Below is an example of a test file where side effects are recorded. The third line creates an explicit side effect by creating a new environment variable called \code{hihi} with the value \code{"lol"}. \begin{verbatim} # contents of test_se.R report_side_effects() expect_equal(1+1, 2) Sys.setenv(hihi="lol") expect_equal(1+1, 3) Sys.setenv(hihi="lulz ftw") \end{verbatim} Running the test file yields an object of class \code{tinytests} as usual, only now changes in environment variables are reported. <<>>= run_test_file("test_se.R", verbose=1) @ Note that as discussed in Section~\S\ref{sect:running}, \pkg{tinytest} will unset the environment variable \code{hihi} automatically after running the file because it was set directly by the author of the test file using \code{Sys.setenv}. The real value of the reporting functionality is that it also reports on external variables that are touched by other functions than those you call explicitly in the file. Reading and comparing versions of external variables takes some time. Especially when it requires a call to the operating service such as a request for values of environment variables. We therefore recommend this to be used only when you suspect a side effect. Or, for example to execute \code{report\_side\_effects()} conditional on \code{at\_home()}. It is not possible to catch all types of side effects, even in principle, using the \pkg{tinytest} reporting functionality. Examples include: packages that keep a global variable or environment within their namespace to store some state, and packages that rely on compiled code where there are global objects within the shared object. Side effects are to be avoided as a general and strong principle, but sometimes there is little or no choice. In Section~\ref{sect:devil} we give some tips on how to properly handle such situations. \section{Testing packages} Using \pkg{tinytest} for your package is pretty easy. \begin{enumerate} \item Testfiles are placed in \code{/inst/tinytest}. The testfiles all have names starting with \code{test} (for example \code{test\_haha.R}). \item In the file \code{/tests/tinytest.R} you place the code \begin{verbatim} if ( requireNamespace("tinytest", quietly=TRUE) ){ tinytest::test_package("PACKAGENAME") } \end{verbatim} \item In your \code{DESCRIPTION} file, add \pkg{tinytest} to \code{Suggests:}. \end{enumerate} You can automatically create a minimal running test infrastructure with the \code{setup\_tinytest} function. \marginpar{\code{setup\_tinytest}} <>= setup_tinytest("/path/to/your/package") @ In a terminal, you can now do \begin{verbatim} R CMD build /path/to/your/package R CMD check PACKAGENAME_X.Y.Z.tar.gz \end{verbatim} and all tests will run. To run all the tests interactively, make sure that all functions of your new package are loaded. After that, run\marginpar{\code{test\_all}} <>= test_all("/path/to/your/package") @ where the default package directory is the current working directory. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Build--install--test interactively} The most realistic way to unit-test your package is to build it, install it and then run all the tests. The function <>= build_install_test("/path/to/your/package") @ does exactly that. It builds and installs the package in a temporary directory, starts a fresh R session, loads the newly installed package and runs all tests. The return value is a \code{tinytests} object. The package is built without manual or vignettes to speed up the whole process. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Using data stored in files} \label{sect:comparefiles} When your package is tested with \code{test\_package}, \pkg{tinytest} ensures that your working directory is the testing directory (by default \code{tinytest}). This means you can read files that are stored in your folder directly. Suppose that your package directory structure looks like this (default): \begin{itemize} \item[]\code{/inst} \begin{itemize} \item[]\code{/tinytest} \begin{itemize} \item[]\code{/test.R} \item[]\code{/women.csv} \end{itemize} \end{itemize} \end{itemize} Then, to check whether the contents of \code{women.csv} is equal to the built-in \code{women} dataset, the content of \code{test.R} looks as follows. <>= dat <- read.csv("women.csv") expect_equal(dat, women) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Skipping tests on \code{CRAN}} It is not possible to detect whether a test is running on CRAN. This means we are forced to detect that we are running tests in our own environment. In the following example we use the host name to detect if we are running on our own machine and explicitly pass this information to \code{test\_package}. <>= options(prompt=" ", continue=" ") @ <>= # contents of pkgdir/tests/tinytest.R if ( requireNamespace("tinytest", quietly=TRUE) ){ home <- identical( Sys.info()["nodename"], "YOURHOSTNAME" ) tinytest::test_package("PKGNAME", at_home = home) } @ Other ways to detect whether you are running `at home' include \begin{itemize} \item Set a custom environment variable (from your OS) and detect it with \code{Sys.getenv}. <<>>= home <- identical( Sys.getenv("HONEYIMHOME"), "TRUE" ) @ \item Use 4-number package versioning while developing and 3-number versioning for CRAN releases\footnote{As \href{https://stackoverflow.com/questions/36166288/skip-tests-on-cran-but-run-locally}{recommended here} by Dirk Eddelbuettel.}. <>= home <- length(unclass(packageVersion("PKGNAME"))[[1]]) == 4 @ \end{itemize} <>= options(prompt="R> ", continue=" ") @ \subsubsection*{When tests are run interactively} All the interactive test runners have \code{at\_home=TRUE} by default, so while you are developing all tests are run, unless you exclude them explicitly. <<>>= run_test_file("test_hehe.R", verbose=0) run_test_file("test_hehe.R", verbose=0, at_home=FALSE) @ Here is an overview of test runners and their default setting for \code{at\_home}. \begin{center} \begin{tabular}{rll} Function & Default \code{at\_home} & Intended use \\ \hline \code{run\_test\_file} & \code{TRUE} & Interactive by developer\\ \code{run\_test\_dir} & \code{TRUE} & Interactive by developer\\ \code{test\_all} & \code{TRUE} & Interactive by developer\\ \code{build\_install\_test} & \code{TRUE} & Interactive by developer\\ \code{test\_package} & \code{FALSE} & \code{R CMD check}, or after installation by user. \end{tabular} \end{center} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Testing your package after installation} Supposing your package is called \pkg{hehe} and the \pkg{tinytest} infrastructure is used. If the package is installed, the following command runs \pkg{hehe}'s tests. <>= tinytest::test_package("hehe") @ This can come in handy when a user of \pkg{hehe} reports a bug and you want to make sure all tested functionality works on the user's system. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Using extension packages} It is possible for other packages to add custom assertions (\code{expect}-functions). To use such a package: \begin{enumerate} \item Add the extension package to the \code{Suggests:} field in the \code{DESCRIPTION} file. \item Add \code{using(pkg)} to \emph{each} test file that use the extensions (see \code{?using}). \marginpar{\code{using}} \end{enumerate} When multiple extension packages are loaded, and when there are name collisions, the packages loaded later takes precedence over the ones loaded earlier (as usual in R). This includes assertions exported by \pkg{tinytest}. \textbf{Note.} Other than in regular R, it is not possible to disambiguate functions using namespace resolution as in \code{pkg::expect\_something}, because in that case the test result will not be caught by \pkg{tinytest}. The API for building extension packages is described in \code{?register\_tinytest\_extension}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Running tests in parallel} In \pkg{tinytest}, a file should be considered a closed unit: no information created in one test file should be used in another. Under this condition, tests can automatically run in parallel by running different files in different R sessions. Running code in parallel takes some careful consideration around setting up a cluster, running the tests, and closing the cluster of preparing it for the next run. Depending on the test runner used, there are different levels of control and responsibility for the user to prepare the program for parallelization. Below we describe them from less easier to more control. \subsubsection*{\code{build\_install\_test}} This function creates and installs a package in a temporary location. By setting the \code{ncpu} parameter, the number of cores used at the testing phase can be increased. <>= build_install_test("/path/to/your/package", ncpu=2) @ We already mentioned that the order in which files are run is in principle system-dependent and it is a good practice not to rely on it. Under parallel situations, all bets on file order are off. \subsubsection*{\code{test\_package}.} This function assumes that a package is installed. It can gather any information necessary to parallelize a test run. The simplest way to parallelize is to specify the number of CPUs used. <>= test_package("PACKAGENAME", ncpu=2) @ Here, \code{test\_package} will \begin{enumerate} \item Set up a local cluster using \code{parallel::makeCluster}. \item Load the package on each R instance of the cluster. \item Run test files in parallel over the cluster. \item Collect the results and close the cluster. \end{enumerate} In stead of just passing the number of CPUs it is possible to pass a \code{cluster} object. In that case \code{test\_package} will still load the package on each node. However, note that if the package gets updated and reinstalled, it should also be reloaded. It is in general hard to completely unload a package in R (see \code{?detach} and \code{?unloadNamespace} for some details on artifacts that will not be removed). So our advice is to restart a cluster for each test run. \subsubsection*{\code{run\_test\_dir}, \code{test\_all}} These function assumes that all functionality needed to run the tests is loaded. They accept an object of type \code{cluster}. The user is responsible for setting up the nodes. <>= cl <- parallel::makeCluster(4, outfile="") parallel::clusterCall(cl, source, "R/myfunctions.R") run_test_dir("inst/tinytest", cluster=cl) @ where the argument \code{outfile=""} ensures that messages from each node are forwarded to the master node. It is possible to keep the cluster `alive', so modifications can be made to \code{"R/myfunctions.R"} and then run for example the following. <>= parallel::clusterCall(cl, source, "R/myfunctions.R") test_all(cluster=cl) stopCluster(cl) @ For heavy test routines it is thus possible to keep a test cluster up to offload computations. For more complex situations, including packages that use \code{S4} classes, or compiled code, (re)loading takes more effort than sourcing a few R files. In this cases it is often easier to restart a clean cluster for each test round. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{A few tips on packages and unit testing} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Make your package spherical} Larger packages typically consist of functions that are visible to the users (exported functions) and a number of functions that are only used by the exported functions. For example: <<>>= # exported, user-visible function inch2cm <- function(x){ x*conversion_factor("inch") } # not exported function, package-internal conversion_factor <- function(unit){ confac <- c(inch=2.54, pound=1/2.2056) confac[unit] } @ We can think of the exported functions as the \emph{surface} of the package and all the other functions as the \emph{volume}. The surface is what a user sees, the volume is what the developer sees. The surface is how a user interacts with a package. If the surface is small (few functions exported), users are limited in the ways they can interact with your package and that means there is less to test. So as a rule of thumb, it is a good idea to keep the surface small. Since a sphere has the smallest surface-to-volume ratio possible, I refer to this rule as \emph{keep your package spherical}. By the way, the technical term for the surface of a package is API (application program interface). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Test the surface, not the volume} Unexpected behavior (a bug) is often discovered when someone who is not the developer starts using code. Bugfixing implies altering code and it may even require you to refactor large chunks of code that is internal to a package. If you defined extensive tests on non-exported functions, this means you need to rewrite the tests as well. As a rule of thumb, it is a good idea to test only the behaviour at the surface, so as a developer you have more freedom to change the internals. This includes rewriting and renaming internal functions completely. By the way, it is bad practice to change the surface, since that means you are going to break other people's code. Nobody likes to program against an API that changes frequently, and everybody hates to program against an API that changes unexpectedly. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{How many tests do I need?} When you call a function, you can think of its arguments flowing through a certain path from input to output. As an example, let's take a look again at a new, slightly safer unit conversion function. <<>>= pound2kg <- function(x){ stopifnot( is.numeric(x) ) if ( any(x < 0) ){ warning("Found negative input, converting to positive") x <- abs(x) } x/2.2046 } @ If we call \code{lbs2kg} with argument \code{2}, we can write: \begin{verbatim} 2 -> /2.2046 -> output \end{verbatim} If we call \code{lbs2kg} with argument \code{-3} we can write \begin{verbatim} -3 -> abs() -> /2.2046 -> output \end{verbatim} Finally, if we call \code{pound2kg} with \code{"foo"} we can write \begin{verbatim} "foo" -> stop() -> Exception \end{verbatim} So we have three possible paths. In fact, we see that every nonnegative number will follow the first path, every negative number will follow the second path and anything nonnumeric follows the third path. So the following test suite fully tests the behaviour of our function. <>= expect_equal(pound2kg(1), 1/2.2046 ) # test for expected warning, store output expect_warning( out <- pound2kg(-1) ) # test the output expect_equal( out, 1/2.2046) expect_error(pound2kg("foo")) @ The number of paths of a function is called its \emph{cyclomatic complexity}. For larger functions, with multiple arguments, the number of paths typically grows extremely fast, and it quickly becomes impossible to define a test for each and every one of them. If you want to get an impression of how many tests one of your functions in needs in principle, you can have a look at the \pkg{cyclocomp} package of G\'abor Cs\'ardi\cite{csardi2016cyclocomp}. Since full path coverage is out of range in most cases, developers often strive for something simpler, namely \emph{full code coverage}. This simply means that each line of code is run in at least one test. Full code coverage is no guarantee for bugfree code. Besides code coverage it is therefore a good idea to think about the various ways a user might use your code and include tests for that. To measure code coverage, I recommend using the \pkg{covr} package by Jim Hester\cite{hester2018covr}. Since \pkg{covr} is independent of the tools or packages used for testing, it also works fine with \pkg{tinytest}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{It's not a bug, it's a test!} If users of your code are friendly enough to submit a bug report when they find one, it is a good idea to start by writing a small test that reproduces the error and add that to your test suite. That way, whenever you work on your code, you can be sure to be alarmed when a bug reappears. Tests that represent earlier bugs are sometimes called \emph{regression tests}. If a bug reappears during development, software engineers sometimes refer to this as a \emph{regression}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \subsection{Side effects are the Devil} \label{sect:devil} Since side-effects manipulate variables outside of the scope of a function, or even outside of R, they can cause bugs that are hard to reproduce. R offers a mechanism to ensure that a function leaves the outside world as it was, once your code stops running. Suppose you need to change working directory within a function, \code{source} a file and return to the working directory. A naive way to do this is like so. \marginpar{\code{on.exit}} <<>>= bad_function <- function(file){ oldwd <- getwd() setwd(dirname(file)) source(basename(file)) setwd(oldwd) } @ This all works fine, untill \code{file} contains faulty code and throws an error. As result, the execution of \code{bad\_function} will stop and leave the user in a changed working directory. With \code{on.exit} you can define code that will be carried out before the function exits, either normally or with an error. <<>>= <<>>= good_function <- function(file){ oldwd <- getwd() on.exit(setwd(oldwd)) setwd(dirname(file)) source(basename(file)) } @ \newpage \begin{thebibliography}{5} \bibitem{wickham2016testthat} \href{https://cran.r-project.org/package=testthat}{Unit Testing for R} Hadley Wickham (2016). testthat: Get Started with Testing. The R Journal, vol. 3, no. 1, pp. 5--10, 2011 \bibitem{burger2016RUnit} Matthias Burger, Klaus Juenemann and Thomas Koenig (2018). \href{https://CRAN.R-project.org/package=RUnit}{RUnit: R Unit Test Framework} R package version 0.4.32. \bibitem{csardi2016cyclocomp} \href{https://cran.r-project.org/package=cyclocomp}{cyclocomp: cyclomatic complexity of R code} G\'abor Cs\'ardi (2016) R package version 1.1.0 \bibitem{hester2018covr} \href{https://CRAN.R-project.org/package=covr}{covr: Test Coverage for Packages} Jim Hester (2018) R package version 3.2.1 \end{thebibliography} \end{document} tinytest/inst/doc/tinytest_examples.R0000644000176200001440000002774513543151030017575 0ustar liggesusers### R code from vignette source 'tinytest_examples.Rnw' ### Encoding: UTF-8 ################################################### ### code chunk number 1: tinytest_examples.Rnw:75-79 ################################################### options(prompt=" ", continue = " ", width=75) library(tinytest) ################################################### ### code chunk number 2: tinytest_examples.Rnw:94-95 (eval = FALSE) ################################################### ## ## [this is an extra comment, only for this vignette] ################################################### ### code chunk number 3: tinytest_examples.Rnw:118-121 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 4: tinytest_examples.Rnw:123-125 ################################################### expect_equal(1,1) expect_equal(1, c(x=1)) ################################################### ### code chunk number 5: tinytest_examples.Rnw:128-131 ################################################### 0.9-0.7-0.2 expect_equal(0.9-0.7-0.2,0) expect_equal(0.9-0.7-0.2,0, tolerance=0) ################################################### ### code chunk number 6: tinytest_examples.Rnw:133-136 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 7: tinytest_examples.Rnw:145-146 (eval = FALSE) ################################################### ## expect_equal(stringdist("ab", "ba", method="osa"), 1) ################################################### ### code chunk number 8: tinytest_examples.Rnw:151-161 (eval = FALSE) ################################################### ## b <- benchr::benchmark(1 + 1, 2 + 2) ## m <- mean(b) ## ## expect_equal(class(m), c("summaryBenchmark", "data.frame")) ## expect_equal(dim(m), c(2L, 7L)) ## expect_equal(names(m), c("expr", "n.eval", "mean", "trimmed", "lw.ci", "up.ci", "relative")) ## ## expect_equal(class(m$expr), "factor") ## expect_equal(levels(m$expr), c("1 + 1", "2 + 2")) ## expect_true(all(sapply(m[-1], is.numeric))) ################################################### ### code chunk number 9: tinytest_examples.Rnw:170-173 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 10: tinytest_examples.Rnw:175-177 ################################################### expect_equivalent(1,1) expect_equivalent(1, c(x=1)) ################################################### ### code chunk number 11: tinytest_examples.Rnw:179-182 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 12: tinytest_examples.Rnw:191-194 (eval = FALSE) ################################################### ## v <- validator(x > 0) ## d <- data.frame(x=c(1,-1,NA)) ## expect_equivalent(values(confront(d,v)), matrix(c(TRUE,FALSE,NA)) ) ################################################### ### code chunk number 13: tinytest_examples.Rnw:201-204 (eval = FALSE) ################################################### ## refD <- as.Date("2016-01-01")+0:2 ## ## expect_equivalent(refD, anydate(20160101L + 0:2)) ################################################### ### code chunk number 14: tinytest_examples.Rnw:215-218 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 15: tinytest_examples.Rnw:220-228 ################################################### La <- list(x=1); Lb <- list(x=1) expect_identical(La, Lb) a <- new.env() a$x <- 1 b <- new.env() b$x <- 1 expect_identical(a,b) ################################################### ### code chunk number 16: tinytest_examples.Rnw:234-236 ################################################### print(a) print(b) ################################################### ### code chunk number 17: tinytest_examples.Rnw:241-244 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 18: tinytest_examples.Rnw:252-255 (eval = FALSE) ################################################### ## a <- c(k1 = "aap",k2="noot") ## expect_identical(stringdistmatrix(a,useNames="none") ## , stringdistmatrix(a,useNames=FALSE)) ################################################### ### code chunk number 19: tinytest_examples.Rnw:262-270 (eval = FALSE) ################################################### ## list( ## ## [long list of results removed for brevity] ## ) -> results ## fils <- list.files(system.file("extdat", package="wand"), full.names=TRUE) ## tst <- lapply(fils, get_content_type) ## names(tst) <- basename(fils) ## ## for(n in names(tst)) expect_identical(results[[n]], tst[[n]]) ################################################### ### code chunk number 20: tinytest_examples.Rnw:279-282 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 21: tinytest_examples.Rnw:284-286 ################################################### expect_null(iris$hihi) expect_null(iris$Species) ################################################### ### code chunk number 22: tinytest_examples.Rnw:288-291 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 23: tinytest_examples.Rnw:301-304 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 24: tinytest_examples.Rnw:306-308 ################################################### expect_true(1 == 1) expect_false(1 == 2) ################################################### ### code chunk number 25: tinytest_examples.Rnw:310-313 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 26: tinytest_examples.Rnw:319-325 (eval = FALSE) ################################################### ## ## Datetime: factor and ordered (#44) ## refD <- as.Date("2016-09-01") ## expect_true(refD == anydate(as.factor("2016-09-01"))) ## expect_true(refD == anydate(as.ordered("2016-09-01"))) ## expect_true(refD == utcdate(as.factor("2016-09-01"))) ## expect_true(refD == utcdate(as.ordered("2016-09-01"))) ################################################### ### code chunk number 27: tinytest_examples.Rnw:331-332 (eval = FALSE) ################################################### ## expect_equal(anydate(as.factor("2016-09-01")), refD) ################################################### ### code chunk number 28: tinytest_examples.Rnw:339-341 (eval = FALSE) ################################################### ## x <- ULIDgenerate(20) ## expect_true(is.character(x)) ################################################### ### code chunk number 29: tinytest_examples.Rnw:351-354 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 30: tinytest_examples.Rnw:356-360 ################################################### expect_message(message("hihi")) expect_message(message("hihi"), pattern = "hi") expect_message(message("hihi"), pattern= "ha") expect_message(print("hihi")) ################################################### ### code chunk number 31: tinytest_examples.Rnw:362-365 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 32: tinytest_examples.Rnw:374-377 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 33: tinytest_examples.Rnw:379-383 ################################################### expect_warning(warning("hihi")) expect_warning(warning("hihi"), pattern = "hi") expect_warning(warning("hihi"), pattern= "ha") expect_warning(1+1) ################################################### ### code chunk number 34: tinytest_examples.Rnw:385-388 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 35: tinytest_examples.Rnw:398-401 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 36: tinytest_examples.Rnw:403-407 ################################################### expect_error(stop("hihi")) expect_error(stop("hihi"), pattern = "hi") expect_error(stop("hihi"), pattern= "ha") expect_error(print("hoho")) ################################################### ### code chunk number 37: tinytest_examples.Rnw:409-412 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 38: tinytest_examples.Rnw:420-424 (eval = FALSE) ################################################### ## # Check that log and centering cannot be combined ## expect_error( ## centscaleSpectra2D(tiny, center = TRUE, scale = "log"), ## "Cannot take log of centered data") ################################################### ### code chunk number 39: tinytest_examples.Rnw:435-438 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 40: tinytest_examples.Rnw:440-442 ################################################### expect_silent(print(10)) expect_silent(stop("haha")) ################################################### ### code chunk number 41: tinytest_examples.Rnw:444-447 ################################################### options(prompt=" ", continue = " ", width=75) ################################################### ### code chunk number 42: tinytest_examples.Rnw:455-460 (eval = FALSE) ################################################### ## data <- data.frame(A = 1) ## rule <- validator(A > 0) ## cf <- confront(data, rule) ## expect_silent(plot(rule)) ## expect_silent(plot(cf)) ################################################### ### code chunk number 43: tinytest_examples.Rnw:468-472 (eval = FALSE) ################################################### ## run("runs/multiple_loggers.R") ## simple_ok <- expect_true(file.exists("runs/simple_log.csv")) ## expect_silent(read.csv("runs/simple_log.csv")) ## if (simple_ok) unlink("runs/simple_log.csv") ################################################### ### code chunk number 44: tinytest_examples.Rnw:487-488 (eval = FALSE) ################################################### ## ignore(expect_equal)(1+1, 2) ################################################### ### code chunk number 45: tinytest_examples.Rnw:494-508 (eval = FALSE) ################################################### ## mantissa <- gsub(" [0-9]*$", "", x.hex) ## ignore(expect_true)(all( ## sapply( ## head(seq_along(mantissa), -1), ## function(i){ ## all( ## grepl( ## paste0("^", mantissa[i], ".*"), ## tail(mantissa, -i) ## ) ## ) ## } ## ) ## )) tinytest/inst/doc/using_tinytest.pdf0000644000176200001440000063355513543151031017457 0ustar liggesusers%PDF-1.5 % 143 0 obj << /Length 1454 /Filter /FlateDecode >> stream xZsF_>EԗN&i:uK ݽ$q Ic:~{݅xcxoNW/'_ģqĨ`1N4e^L(7z !ܯ9wso_ Q Q 3,`?Ȕ}89;4 V xL4R9aP>ӻ0>s=ӦM(f,n%*ҳI"8/g?Ђш镅YF'Tᤑ1Q!v{ QqVB&iDS<6{Y/WE1K8[  T7F+@jdD>Hf f I:fxHkxǍ{@U}dUlv]VvvSz؞HYtZw TZ(bx!M"&Ȩ`H HovpRAQr +AC$ds!n՘B.ڼ>࿀vv:O8܂9n;y%ZzfMb5ӸIrvc"}ꐪ+aݘr>EAUs"TLݷ%xy6"߻ΐQuJXfQ+::gS㼾 ֚v`^gc mxp}"QpB5*ADDž =ދ $o'KVڬ(9:Odp}m7I 0_:8$i>zy;wiKXӘ}`go FbveJbJ`_ $9 f /єDSxZlj?8%CΝWpA _%t"~و6y_uWgM\;kIrDAwb? 40Icb)#PE ?V6޵AFIjBmzRD)E'U;L̓Φؘe|$HK=[፛3X C7Kc* /yJa""w[JHb dGo3ֲ?"Ґ[ʄ.5),KHTbrF$PڷeuYQ^Ofڲ̬eaİveV ͷ763`oS>e{"eVmZ˙5=*t< c顔C3۳VmfVmg~uJ9ۼ+nŢBu=I@e퓁 ə#*?4<\w?_8;NhU;gO(dڸV=ֳ&Wt5&}/uޭrfPۚdOV2sGx58k[ endstream endobj 156 0 obj << /Length 364 /Filter /FlateDecode >> stream xڍR=o0+F]b>Y˔~E!*}]!jc691kzet 2-xDo!<|eĦxGÈHY6)mtU(f9&2EmcC9Ϡԙu{12ew(:˔PQ)#ZeLgLJQA`+1_ endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 809 /Length 2122 /Filter /FlateDecode >> stream xڽZ]oܺ}_1}@%9,[ Fm]?(YK6-$ ؠ%Ϝ 4)rĊ<ER'qȤ%OFyґ& dh<_--_O]umS_?l7붫SѺ\^WmSW]_7??bv(]oCaHl?njs@W61y4e_kvbƽm0$'۞֛]?D#c}z1VnonMQRU=ӹ,r/+pi7͍lTϨnT0IUnVw3d of@\^ˊ+0޵y=+r>>5~W1Mח2Bez;! ?o;w^w zcnF -+eoHuWM4u_g@-#7Z=<结ߨ׃CM?:]ZnjܻS궛zFM]mo _l-.<1//=O/]]:É83 p^c]A%d ^QH8"uQ).~Fx%e+ ,ȏ0Osv/ż,0[ R+$НEz!WvĨ= 4+MPi t/POS1GWŦPd*JeT%"bjZlHlhTX$ugU TvTDI!XKD>㧒^YYaR!R. eݔO RV2EѺ0(D-aLRDcLJ*Tl0RɪpBMFET%^4gFW^1~V** psL4{|Q$Cm!d]ٺBv1+vG=~ve yWcVcp ء5cT>AveI<"L\2EQP@92 '.MS9~ve$zyWͬT*HfTWx H?I]~V**jbDLP8PDHY8H-f(&i* TºC..YVcB^V`l|asc^;%tB\44PY}z|gᏯNciEJ~e}Yt/"Eq(WK5`!l>R˪xp耍_ҳrUWHjɃO3̈́@Pޘ'XByy϶ӻ-,ÞVېO䶴qe:&ȗ$N''ⴂb> stream xZBP`՜h4֗60 YڋvuK3!^uW;j\3̓3h5”,\f=~7ݳ?^={~iHYalGWˑ.:S-2]G5(%eX%xbdRj%=k|I ^wZ|5"O;p`n5~s}XT,2%G] S䎟+FڹLn'4Rs;3>TM֝T'd UXܹǿɞ,քnZF;O2|nRJXcSJEبuACTC/`o9_C5ˊguچݏe}^l{ztУ__$]#G!89PSFS)NDkQdYWk1{k麟ka>ޓzѬ ކyYֆc's=hm+ʆ[5U}O^S`eW1 ' :>VXՠʨB"^g'eB]B[(P^7P}+ЩU1Y5*2gZrab)!g %gꚿ[~}I՟Vx'^',C2sy'Wyg79F=H> Obio%s28٥ Ig-P <!2- ;qVc*$_`V( 5~ۃDmSJފgaWAr wAKy1r֥ xiGę69FkL>\(dX̻JHMym"u;!dž-}aC۰I۞v#~ǗvRr77hwL\(4$LQ "׫qI▇]8~8qME8##&҄" m\yK&we;9Z3lHK߷7GDάHA|ZՂV= #AFñ=p.2| 6i/] H@½OwхsnSz.b0|Ɋ[>ĥfY0G"W8*EYl>[;wqZ;[x޿X:]s|ݞ5d8E\PTB"&Њճ= endstream endobj 172 0 obj << /Length 2859 /Filter /FlateDecode >> stream xZoBȗqCWsIP-)I1?3/~X>8;2,'GOP_-'~O}"&UZDdWt1y5DL8TA@3sh5NVΠ$Ж{Mѭ''L*|G; Юc7? {ːmd&D86= \>~<){`IU銖WCs`S{fhĔ}Z͏* Vܗ~q+isfh|H*`S VsnS=ǗLBϭ=4m١ t"foxH YŽx~zAt߲q iyc!,ynIR#i<0(-%Xl#w"7x_xUl+\;cUBw"%=DgG2lhIlmQ%-̈́H/V9m7"J:%0ZY -Pr> <PFm;ujfz"]5ONf2k.[v-QkSҊP,qQY[ ![%pJC#*IK'Vԯ9:1YNeN.@WbXMr2JO_vLxȾש+Yr{R*cJH֬w@/$/^%0ͬu󻑐u}Y7P 1]_Њ_%ȹ]هʈ$D%B8s3C웕α4i r:5[mN #EN͜౬ruRA\= j˴;V٭:c|9 cQvh5ƫ"љ5bUvi.%(=>ZAy4mht7Ȥ܅WZ-Õ+ϲHdFܧb#9QwK<c6ہ݂Ҍd~47V.9kfyMR όevyԼCOamieݲ2W? Os?B "K1ݓ}@߁{ ˋ''=MP?OpPM;r%Q#tl\}> EvQUDr` c 3 s~̃YXtcZIY1fo3pZ5_n,Mq4:w{o6QڼAgݷŕDZ?z-EzTpY. ՂYe1|?72ȺR)=ʈS'c+682^&[L%c0I4K}nS3ʁ =Yw> ‡|LWX@Sh }AdLOPpxg ͂d(A+#|oI&v ӻ9^xF=PHGٸ7ؘxDQ`bEHIcѠBiMa aN,(V?Y o 8Gr=Law';ӱUHrr:`21`3fFER$c/E]ZH6t+q99\qT3W̹k!CgmG\[Šqw3]ASx_q^3=2sTXs U~NԞWLwz`Og·ibx3l@==:X"]7qot`F..i D'CtvyFsz;HV"L8VQ)DݖzW6z#y}Υ HqatG)\'YYbCNHhubx{IƇiWF̈H1 -//)&XΆK{ mu!:.40wV[)>mm646;R`:'Kcc#byabf61[*ݞQ:VY{bc,D@E#H`QrXLr_:\;I} 7" #4@r6(GMG-ՏYU(k'͖+a:S b 좮=(1dTϕ-o:RU0nZ`lWP8u _s K~~`;/oOB?!Xdgm?S>wP^]|ֶ~nRa^Ч%e{ߠtly *?%@+k8/}AHj>ba)5nNͶ_DӽYꖿN4mEFjFamW> stream xZ[D~_K".|C,+F̌X` d'!vwO#~[ng.!SVߩpOB>[>xD3",lEM:˒HE2[fE|r6oJ u nph=O?,uF(maᾣdL#-b&"y͍\D9$ܕP!Mz y{?YW 1fF܃K+؎˷&p=vt2jpqA֮į a8UɱQ"\x4&_#Kq L8VaGJ[)ڶ{VJ c_D1PQ3ǎS CmҊE`,) ;Tq|S섖>n|mҠG~F% UƼxhXWmC΂Pqr_P'g$>0pNw8  ܝMzhKVjZ+%gV_`dqCW($2J+Ι PQP&6P`ᑹQlCe x(zy" QY3!D"j^!ؽ W2'-fShOpTz+/Xf>y+|ğh@]D]ˤYy[PnOhۡt>kl㍻)k"\boaX+-}?2:V$*](|@):N Ju;G4eBB#~h'0z'Yd*qff/=u%Kg%ѡɼ9vm7TgSyVNWZioC2ҹL G "j< ~J7~ǿ7i '-l='oLq87;}ORt6y8~OJVl\pHx0b{mdG"l&s50$(rKlQp)2@Ş_LΆyH-Ps#0,~yE#BX BGXS - YpFS gT)-5IU{(`cMu8$geF`Q4q>MK5N_* ,b$kO$3ko@qZ}#QD62"ʎSK .HLvVrKR[y (HXgXvXW[76(S5gܳ"/"o;Վ{(@4 |R5p}_| E1o;.TbY^Gx79Ɔ֧`y2>P|\2ۋq6јlQSH`ᑉGQKs^w3pߓM|_JrJv4(s.IL|mⰜ<ö.ʱ33.TQ p+饧29(gMx\Х[g*ʞ']fK:yVc=@ T%Y \9 ψ; (Rn|/ek_LcM:\E\mpe?k%lGq4{-Sc յ%; JZֱNGh~􆙨D1r ]݇f x`Yoüg;釜_#~gp qѨbnIy*U endstream endobj 186 0 obj << /Length 2270 /Filter /FlateDecode >> stream xZYs6~ϯF,A i34qN>5)YmM۲x{c[`6׏DAw;'_r ? 21OaGR 8U߼ӡ G"j8.Gt7+p %_g00A3M/YaLy/xꜹ{9OI \0&0>{r] 0P-Ĝ.yV ƚ)NCź<'^QڱYR?P|E3ΆPyv,Ъ@Cj{헼B^SI3-n;ND"U >;9g'|D͆ԡiHl%L* CTQ%ϒYJ?b0(9 (Sny^jhI$ݝt8oIޛfv D}w4!|Ep:*ea$I SH/`\1{OWPDW Pkh$IVszX9ܝp2ʸKI+roED܉3{:: 3i}8ƻGўmԯ&ytךɔ v_ҏ㍐AkAF"Se˺V/۩ KWW<7sNq>"yRȏ/f~q)n-'ɤ[5*,0.ГmBe  S)fz~2-lkW$jڭ2Aʼ^e)BZ[/8%9)є2)rwiۦLvY9Lg 4УtĬe[i+1ZG$@G8!:1 `Thxuщ a x|p f˲8FX}fra˕nOP:8C+d"bM:gPzsTÂ]pƙΣ aO<]բ#K#㒹c1f)B`,en]mtjK$>c4N\LZ~=e}bFüL3tK;Kفw>>?@1g":h6 芗|w/2B&D"="xou]ϨW]gG%V#25#<vntÌmqpWwI#>Ƣ$f22ٲ6%b] }?v -)2>!bC"@ ZC1J30h{f[,ȟ+QGB;0jĂ_+ Rx좢"aPVJ Vn77ܑX?]Y}"Oxܰ1Il0̲uTE܈fpv* 9E*-x dI êi̺(V2&ľR5H@$r1ۼ>>Avg?\w~Fƀ)Eܻg~SS0!ָin?P%iq_DPmSaO'~R7kHQ& /rs ȉSZYӧU٘ߞᶮj{yfӣoxŜX 5hC JnJ @8Fhs;j@8'g"JfEցKi_֚@Bq*\9u%=$ư|i.i9/xEfOԑHdaIr_ڧt'n3ͨ|ނeڠw*?mi8^z$ouJ;t=A)DfL)3i|ěO]a331II9n4WfK)xā~SڸKcw`1%Q3m6H켏M],~sCq6|j .$h] OfI =|JB+.dq>izt)vq꫔C9;3g~"dP;> +q 0f&< ]hE Maކ ܁ڿíwWܺ 4^$ 9i%꿏>t30%YG_(M…\q'TZcy=Nt e$Z,AG)Mh".4҉mè36F+m`Ӝ~;8zWԹt=mlЯP#S_TتNV\A,O endstream endobj 190 0 obj << /Length 3059 /Filter /FlateDecode >> stream xio{~~ X ""YlYb7hH!kʔ'.P$ g޼k5fY6E&?x.3uVLWŬuZl9%1&Czϰ~Tے|uYT<{;~-Oq[*i1}k_`umᏺ82Iz2&KDɞWw`{KldcۆG}z`zKtan/t#@ehj <1 &m,wt%0Yk\i}mOܸG(5wK RV]tLBcxH'@EVóeVh+YeFZ:E5۴*/ieQ(S;6ms-ՔaߟUfKRef `q'R2qzhҎ&"[Ϥ/_+UYY@}siaJ7HI-WO}`@ 7_Bĥx+h tT8YcMEm0x > ^K=⟄:>4 Хy"sZLj{ur5k!%gbNn/>m,fr'4ׂGa^  rfY&c qw"8wwtCc 1/f+dzqʢyOމW 58[cw ޛxF7\H0KB .$ip4Z2t5 "a-"݈aOƩUbD ȎݓX*mo;W -ؽ["EsE0L`n8ݨZP/ %t5؃xehuS&OwV`<9p~Tr :CeYz ^v#nɍ^l 1ӓ!C\Qe3o-c 4'&uF_Zlf sL] -~I-O;ޖM@0{.e 3{&wZd`x` Lg$`l+EX$"šVI>mE.n*O)u: k7}AĤzqE/ dm7eh}ƒ3s=w:Ƶd%4"v*% kM:dJ#֕ Or3DS [ z)Ԋ*;ڱHˬzӟ ,Vc]Ɇ W^eNC$B%+}w Gk'h46DdlA-WG.4rtWq0jNn]p9)'0+Yy{5N(&Cj=W($)H1H7 U/xn\w ",<: e!,:LzQ+5Wz\@5CR궧\*]d=l*.s~)Yo(7>v>ppC\uN#X;(y~dv'Kb\1]eVH/M{WyǻEQ>N̻=/ Q4MZ !} j'=ۉs;Uw+Fw…IdPT-{Lp"Wõt[Жhrϳ8ɞMv~b㧤*^ _lV/9a&T, po:[Ȯ֎aK0@3KMT8"Z'溨}"K(2'ڽ/{p{.Z*]]Fkslg2U~BP:D}!Sm"pUOѾڪ h"5NvKh㶋N|-e:y{pM߇eDFt:ػ2opqW LXBn5Q{|- (j: 7&ޠYUT*ZOԛ 7vTGWG)qqM"™@;:|4*' Xnwb!|B$ ܘ#D٬B.no8Z;wWVjꃘEV|\^}#28I. vg!fGVE hہ/jTnuJȬ5UU0˄ֆC&B]Fxk ƮwqKy]tXΆA%+,F4/Q-[ij_Y-\]۴u#+ X[ `D>B܆A.-oH7cbIspib|,C%١y!+F_ђ= BڼH #Pg'|}5y] Q-\(*b+k> stream xZBp?TB,+ 8(Z%NTD6?+;ZMb"%ҊQﷴ8ѮX E]^Fɖ!7dA"rmwG-lB]*:s8V;Je[ʢ4Pv/ LsO`JZQV(iݨ1V-!- ݫn%?5FP¹5$۠ nW=oz]Ykk c5%h0\sVU_{lB$F<<^|yⓂ={"OȎmfEwÒ(Hc*5~r!g) m~O"ٞɝ|3PT|:9wE]Hn4I`b`Q}J0qWmIwU43-yè#.i>-XrPҐTd2UYCF<1#@|ݹч _tK6< z-܏$hidv2YcS@N*|ԅFxuDž o^J{_>py=5:\&(\ b3.ow7;߂ʕ,8UxulIq{-ԟ߹Qgo87nuMɟBia3;WtΝqB;QV Ti-O4sSew 2)d#C e8,SIdkDT޺tG l[J'q^[w'ގ ?˥%ތ ))y[yF_y4 ~\׌gRįKdbW(XBa%#^p8,{t@Pyo8 /m8@^Q\N Q?626if8/E"ZhZxl"lzprFi_!AXG 6nb ތ'. {fݯ.z8pԈxr;!9 Ygu`b|<>xe*pԧI;~<#S0G b/-:ϐ:8XE>ǜq53UDe.z8$QZ8DedB$eÌ<֓7l0V!m(}d 4)0q缉N2=j[u|4dŰ'NQQII>-.t!PWDpG—&џ\fydxD'ƅi#a bUqUQ5g$қ0d[RČ#Cc瘛X$`1,_N"3bnKG+Fʼ;]kj%A2Z,ovlt r;: 6 RU݆=CŢC p:xOS^d)l-Jj@uR:p͵ʉ@9uũ#Bm'!刴36:o2DR2jk2Femj 2k|O+xNs,FC狰 ?a.IA!nkWP?a7 `4#;oFi,ѷO(pOG [=VH9X8a^WJyαՖ?Ҡu%DRj@鵅+;.$ a,xY2h) PHH]IkN}߄a [ *zՎ(#w$H +yƂ44[p7dqؤhLf&Z؟K$18 y_LFfjw2v3` b& CQ$mP2n#1tle 5[W ?Klu6~#-!9HMom\GngC="ېt#gKһz]l;Բۗvf-J2T8^tNBPp>oˤLb=>nmؤN ^OC&+sVPmt)L6!7 a{Jm=\k w3nܑF;Q|JqfϮ7;n1]Olk0̦^/\ ąh"ʊ4.Qdxd- fy\ƻقՀ^ )RO%;Jhw[Re~4ؤÃwv^ry$#OwWŰ{t6kWjǦijFSTΝC6ʻK.S蔱9}#^@s k̓WFm'y]`^3'goi‹Ó9+"jBfoWk^Y2\3 ¯=H8r')OX K%))ZG5ejQ@ZW\mꏆt2H U>>" em0yG$ӵ:m <s-wQ<.ہ,Ĝ 1 ƺeJXpxKFC(˝92*&(=֐syOO0.x@|?QڏxV;8|*uuqdtږP(䍄$0(+q m8ko/ 0; lLd8Դi iȁ\x-0B&/Fm/C)FGT/>GNR0 endstream endobj 203 0 obj << /Length 2513 /Filter /FlateDecode >> stream xZB' xKrF[y4HM;:]r5w^|˝]a\r8AeI6Ivr⏅L5Y'IUYד0lb=ij_?YyLn6Nkh6f:+yLJW5P!0lzKC+]bKYbenݑY#_KƮg:\BihύQyAr6/IwMS~jZ+2[}ëhϹ(*kU7ˇx&'{+~ ~ yG~)mx}HnY&t=oTa*_ dK(d iT6(5hj>"SYٸ$zR;ftc^]E[2QQ|p*sK~-ir6}%lVڲӠ}+_$[p8ʇq%G}<@ !s#ebIgGz{y'd\[U^k.]nG )۔pʼU%MYM*T-P*dZl'Ə!NFU>9}|z"Uʊn+4?:˫."Oב}'(ս!d#&nV ΎmMqxuY/"mr_ ͯo"ϳ%dO/KliV\P8TK'#`</'C+' ݒ-Uj#KpGQilC א M<j7t֌]o=2vgdArN z#&|Vs_od^cyt10;`@^jBH&7w /wa (ygcx(8-iCOJ‰@0Y 9xdkр )(|ϟbL Y0TSl{! xzGWd)3p7{` A)s]? q@ۡ%JN_K~Py >/))ٕB '% p_8LX(5vQNI.~6w4lbLCЖLO&|C}7Ţ侶T]N3R$ȇENMl'I-"[2MΥOCۇpfϪٺCj[w۠{l+:sx?#>aɭMf#nvɗX~##-kNBV4Sa*(-y!wف+[Q#|vfVi !_tzDuk{# w3qpMte$):L^e7.g%õ2)(l}R2GȬ`p`3H ezV"oGa1 (SLʎ{t} :bvlA#oҧ#iv<\zHކpύ #=Jf*eYؖ41X/ShL=w6ٷPX wJUeQ(xBQ7 \7^d˜WHhy_d$F7FmS*uEs.w{;6eW{7ql֨g6J5T o%s3bfE$oழJ{~+@[&d6Qi+u|BJEAs>8\U0Da:';pC+{PψM "1~t XcѶFɾ3($ӝ($v/Qw jxOL%y-etX>:|BȨ*4;w0nd 0A,eN$M{4sV'2˕Dw8Z+홶pل/qH_- wrdϔGt[8n>'U^f|ڇ0XSIXeIY<@J endstream endobj 212 0 obj << /Length 3024 /Filter /FlateDecode >> stream xYoܸݿb>(D]A"6i" HcQw}DQݧb^rRz4o^}y𧣃G/2=RqTŕGiE5*$ʫlt4}p%xzjqKK9 ax\+p=nn~Ż^Ep=t=y.}MۆhWq\q7RCUEiU:Q1ՍQ[UUD'9ش' r1b^k֏-[dxp6^`.^!ݝz`yȼ$hݘ%0hO3@AWx PE= 1HnQ,@̊jAGޖU0Hٞ-铀Pye%V%\EDYvUth$&;FV\CW.]Ȗdzz@Ԧ&-xHqV!jʲQ>E?/5a֑%[MY}:2#p Ak"߹pj3\9G,)^{c؋u0IH4g{(I]Ör_0#ޚL?s^}0>! 贒t~ps!dt9u ;#keob9Z A 7+BJ'nw!w(VzY c0@(l_l(q8Qq0 ]Qu V/_I jL]kG?(qt|f{\%<im;[b@)X#O[ۭ]Y@xRc|g=3Qy  ^nxW'CW8xq}䬪z W\3 ~@A͡bQK"x"!&Y˷=s%sc j?$rd2salNlp[-MM621Ry\ j_hcz&[]*dtΥTq9!h1!nQՂ]ĕvRSشL* UP` ^S+\TeITƉps,n'Bgwm Yn\X@Ľ!U#l+U-0*N>$63!E}L"* QwS5XqU3 bX7BIG͸7uj_`dґvZVQo\L&EZ!Q%p4@ÃP8K\|$U\REXO7 ͮ8JeHͮVYPbnXoXikKEq-,2"DNG︐)VڸJ96as؆+Ck&f R]a#Hf"89cj%'EK"!t'8fÜ'+K MA|G#yJ^s[<xL!z{8W> c(]ƱW 7ڞ)l܆g: :R5mK;2@|-/ Rq=yƒNRaw֙cқq֢bz5 x'fP: q='I¡A!-J(zPRG1t5s5&д-Fl-j6ob@T_9*IA(1* Ə˛ ⪂Ox^2pNơ`ô =yыRʨs?e\q`"gäZ&SUT~Qw H[a} V=y4Jm E7 Q obS.>M6kB)(B  endstream endobj 221 0 obj << /Length 3036 /Filter /FlateDecode >> stream xڭZYs~KUZsrqY#9*Eӕ'@ ^}ȘK===}|lv=foWLeijvy5Ujl1+su>\~M.*Y1:#\q?erI޻no~+(ϮܷA~GWY~*uQ;hn|]q.ryׅ;+[ !Iw$q㺐~w\3`*4+'12EVoaˬvƍo]Yw< aUyZ#/L敡UB,K.6v;<\@Q8Eښɒckk7L lTmc80_Tc cʴh@¨:w,Fn \>AKQJeKncWZքQ=j kJOɼJ eX [&$ {;>HpQEg*NJʤ@nò-Py^'pl\j9INby͎jeڠ^# )F"$sOkރŽdXkhzzXO GgS/ِx]Yԥ~TjrqF+WpE #qD; G僎t-,i2t_\0 B]D7"[Qkh( tr:Z|Rb^Ap:9<$L sֈp}]%ꄲ +@"tUӄ+x {F(nQ^&YßZk պȱNV">b#1HyԛB (NPź)(jʈVဒd} 5 D P3J+b7N>RRk-5wU=l ј* 1.P[,%Red%+;-ߊFx 6?Ǡ-XM%Pώ>[m)/Kw4p Q'Ja(zC;e63iJKȉ9yeZ(}_m/Y[dD) ]vpS.hmP5Y Sk[hEt=Q|%F*X3 8SI^ބ=[nHC@5b FЦ5h"-8א>: |3DpLrH]}[s:P UQ[`B״SDt "b*{^I r_nfDz&>2S.DRKC8 QaAt?Np`o~-Hþ@oE-qT FJhUSo#W7 r0Ji3m@wG+ߺqsC/'eZV>%XNei1ųEUEVC{2ua: y"&Ddc|` /U͋!TM$_oDgש|̚igHnINm00z6cL5k9(a/%JwaNAYBO32Qf.SH@2G:1ddࠟ")IrPw\疍t <`8Mx"q꡶Sz]5L$]\@^6N+VJи ),ǫAL_O"C0DLVI3U ST4&큆-BK*֯]yuTiw[N@3U .nGmh-JOKD781d͘{"AW [a2Un@H)ZN>X kLb:5tbW/K-,p?=؂6tmHnl2N|zZB27EHpiVz`37V:UzxKUۊR#kZaZU68% /_*77qdM)j<&J #荭ϑBH)R^j%fy h|O7쐡&eA1j!\~ 4`bkA9hY#c$y$.S$=3ej*m%M'>x˕Z냛h+:E,'*b|蹤B]8r9a.}?0%K>y0͒cѳؓ`<m #8H1,%]b3x:q7`b n1}_dK<]qA}|LlC+C0-ǻ/'1ةGx,|3&塗wr*2coQ# rǕg xY슒ώ7bLoM͉N "su90Yy&^>KmźEN :H`O!\gnoE&23VB z 1ڤJ+T'J#XLD H26ztNaV jm% q#wsLѰB;ET〡言o*&o | endstream endobj 232 0 obj << /Length 2943 /Filter /FlateDecode >> stream xڵZY~_!l,#7)#Nbg$6 D!)F{nN >o yͮuWwo>:gU0܋t%f"qLh~~'y>ȯ[~-:<Δ愳pvWsUo+kLBA< .KUu ?,jt ՈNGkܕ1 [{!l/qzf( {{`,rlAY<@Bш3Ff>.a:o ??,œVt1!Q5)2CI5?Aj6ܷnd5 ݔy%,,LV̭X]wF/f,c#qĎ qޚo;4nK{tЃrhX!lvۖE]FaoƞO/j-2L2Xp-Tt5-q R/(KzyۆZռa(=7X=Z-qoYcHha&(qK&\sGp.2NX7aCVMed#ɍ!'Vgg~JdL1AD+/Cޥt ߟ^i:ߒӄƱ另#a:zýgChMuާx$QÕR /(bs>ؤ] /Oa%hothWc壇YpE Rf/tPg M͖nMtܕ݋_) >zuIM Kva}{xu$PWqӠ VJnI1 PҦhC H OB"v5q%4U=]M/Xy)/>6iVk 0u4'Mx+^ Нl֨Uq̟\Pz"%l/Sy8K%@]r a` ~,)79*P`8O[^ĢکDn$8Qco0 ;.|҃S1pe;LҴ L328Gf vdf S<t9ډzI^h~sXRe_øYPęK}!śNF($R^}%l4uZZc&obJ45<?f+<#Xi^؝k%'vVT8Ԧ7q,$G UNM@۪WlmrW"De)Tܺѷ2 ߫1!|Pb.H! 6p~b7RG00yìm|(7N=t|klaxXIf>.IF9ڶWN4=;x'n(D~8 0&QW,ّTn-W<+QEyT-BZ-VE>q>mYԍS\сZO_%y.mqLr`@$&ݬ\܄<"nA\lg?.@:W@ q25D3'1?) `lUId޺B>qbt'QSqM =5(֢"*ZWpߔ2AwvP 5È`RmIJ5,uR H8vZ<DaE_rq#zEb=[cN짩cjnDǭZފP>\s/\𓺣ck:8U q Ĺ#JSO>xImƍ%P^ћoIHCxj=މJ%YmMt"vaEJꍅzgR!Ů1F{t{M^iqf5ElB2\^(dUlKa8@]=;d\XDhGuŁgҿ6x$J~ۈ1 >-͟>~oDO2.1ZFLgһ R6+>wh1BOIIEq$nngwkZ=5)[^YijO&%㙫 6fGE60XT ~\k%tv }>zC\Ƶ֪N;w&k>栾~ gnm &WX?: -3l_UI+]2PA㕩+gq{qn.[ \^{fL endstream endobj 238 0 obj << /Length 2974 /Filter /FlateDecode >> stream xZKW(  )R98:znkW+QZIN߃~ ysACl4ݍm*;Sy{5$w?}f]jw|3ZzveVYUYY|盛EPt׸=gzW(zD}33klMVK`yf{nouwk-S9<8{ $jLO~1@IgL5^]Îmִ[)=_u~`n^E3a4o=r /"bL;҅^f-i[ c3_M=9?`9u0k3ZCk#jf7UV սr8y &=l!pY;^ im'efӍ͍W(x;?5EAALc7.-.Gv'o빺rk))H:_NRsrhhlӵY{ wǮк}0i*3_h`@,ZaUR M F(fLwxr^OT ֭#Ч:X Z (BQ@fq茝(ms=*L~# rY>rؔѺg7'Ngh ^?l Q 5P%y&%B'g8 @vpTez0:RED=cmϴceddtHXYJ >\CXSPce*  CHKbwXAc^Jg|U}<+>C"Hm9O6ZDU$-B(acg.VdO:Ќ5Ʊ.+ɪ̰^h]ԧC|CNI=|dԼ 'w֩bP1Ud4+X(Z)}xeCjE迕"yC>p(.u,>ķ;>%RTo/B_ )?uPF|e>"Ë.^C5cqӼ,EPg]H ܸZxČNOw^< 7K$w;"0ZLfyBGg2 Z Iuh+9OG(؍9C >):[ `ƻgv ջQ 'O)9e$}sp3 E s9_Y? @Sg&yf,3[~5@TR/' endstream endobj 245 0 obj << /Length 981 /Filter /FlateDecode >> stream xVߋ6~߿6U-A9؃B}hK&Nrl^ޙȉҖ;(5h4cHn~ЉK2N[&UEdH~M?f*=9uv&˭!.vazks] iWMXgJ;| ~- Z3򞗇5)u;t>bﳟ!H{o5`@m n&'|UE5TvCt@ 1h3JW gHrcP'2ഌH5~ȜI&rctּK68 aDkVچW $%0e#΅@J*@UDeL[J¤4]li,2MGtve\NɐN[%]ڤL`xL20]1y|{80 ~!6›Tp<0u,FUNeˈ|>rڍ"L_#zb&WJX&INx^,rU e: l"Q@zu`XaچY^8?i4j_(r X#jӅ+39]!֍9a2E5BkF'a!r(]J.m=l~GbMT.ұny \rʍv/@X)Q)!ӓkW9L":[cuFA w>L瑁0Lhzűґ^5/cHc-KT5сX0D?B1?]J+pNMHCltGoB3|$+Mkqf)xf9UcpQ%z%dn!0-KԐSͣ]l}C;K:P4~$o崂4߅?|T7Ռ']4j[-3+=3w@9ai?އk¬+!//$ endstream endobj 253 0 obj << /Length 723 /Filter /FlateDecode >> stream xڍUKo0 W(_;vXR0d.zr' A Gr9`")GJl$ id`m"cCC_9 Z06,_2ȒH8X`S> /宬 ||q wH3%E&3$ u&LP)lTNɔ@bfy _Ff<RC'7E ,J%u#IgdxW^C ~HJKDb44ι(>/4 H}4^hq-Ѧ@6 =A*`ֆj\+Z`L%BeʈfDBm?%rU]6"Rѹzg c4SBM`UN[τqT8l枝Q^՛ky͊RTJ~tF?wXEzM"հ-k#5u~s|S'Ou8`6⺮tR`kX wDoQo-2cͥՕµt[o/]GKB? wC)(6ͤpx:~bMAPRAH_wAa=&ҮBe\g\ׇݙۋP`v5\X?]<@Z}~=@~ %ڣ*m2 KT} KuJ_G*uߌY Z@nʦ{>Jc endstream endobj 159 0 obj << /Type /ObjStm /N 100 /First 860 /Length 2118 /Filter /FlateDecode >> stream xڽZKs7W6USڤj7U ӌXhI*}ɘ#R4487l%k&* s+g8)TF>&Ϧ#.W/kOxA%JN4d2{8A.f9(>0斠L";%)+9%HOThs T_09RV 0u[aBPo,ƞt5C\0@@x,ty@ *$#@ O<qGAPx#RJD 4q(A:`򈀲$UTPQ/)~~;?UIx1؂` 0iKwH= #v&@5 (,&DҝǜtIE'я^tu `~$5bl@ (!af̪lpڗawjS'Vz|$VPb$]\{ut`T!oF>ߌ^Ӆ9?7ݥ"xYPc5Y?~9Yk^M~_?y0۷Q&\Iɼ'ѿ'on7 R,s;\̕n^Չ~CTkEVm-1%BٌmRmFhFCa8 |b:ZEcWRODF׋_w~f2K~r G+pYfD 4?W|1[Lown? dX]|D*U/ !(zk[\x^׏ u(|(+Q|EmS9fSVN'Ƃ""i4BG[Rc=;pXOIjEn>a?Zch)!y?R\@O('Ny;+9 m!-&~M,(vf3m,`,Xng%4YePnCl#T@wD;zk,9o\^8oERȟ:3قruMXtYyEYg[Zfῒ$4nڥyq]mf_a6fA9ٟڢ6i?_jqo"ElɵjGf@Jc6o ڲ\@eѣ46#/L%XWN_`Fj}Fsaa}_iR.+FeŻaUov#*(=i2?gl8=Ƿ'=iv"Pv_hͩ|h7iuE)BM 逪i?O]qu7ꁠG9)m'-F]r3]E]F&Ґ ZCW+=lEM7F&Z Y>*j:j:GFK&Z.sW; RC?s⭍iœKYphY?g3y֯ G]͝u?;㢖M٢ {L?#;dz'e~hҝPݕh=4ĴdN$l٠^6HM ͧznaS P&Ҫҏ7) E_h墝=_jN ?.PQTt6 R}qu)`IWF~j̗)--hkl7Z0=ZG/)ъݷ:>6F4Z7 Or!HAS<5JFZK*oP 97J(ڬ=PO4<Œr2d}R)9棨8*‘rrJ\$ڔ@ZVR|肂?>5)$4_˭dUJIYb8hBҕ,z3^õnFM\3 endstream endobj 273 0 obj << /Length1 2474 /Length2 15601 /Length3 0 /Length 17042 /Filter /FlateDecode >> stream xڌsxZjm۶Lll ma }CA#`dj%Dq݀@ _eLDŽ@ аqJ ḿn 'G +.Pv:X 7"qPR`rvg:Zehj7475+yS*T*t3wqvwcrJh@twrp:!+y濏q gfMG@"?2+;t@ܚ >)YsvrXJXAL=wW`?XY63#vho Wo> hY,4gN>uJںZL`dcgprxKbjD*hwF7g \JNh̻ '9kb17'){Yea`c{A T-VZx8_)h-D@o7B_co_kgoTqr0h@׉h:VJ:;Ysl\SWWSPqrXAi<A.PK'W,߈ ,q  `q vT@Sq*x@*YEP8"?Ao ntf#V65+A9O P#N=`+K'N9DKZ?.XqfiNpXazÀ4%j'mPC}lA}'E.P=Q b r?rv5?j3s3`O ΠpllA>ߐ'w?N$GYAz s7UwPPnn'Q n tr,bew3wrg@Gj?VDjAL@g?w+z>@~@s%'spۆ:Q/)A.wx{}&%\ ;< k}HEH$xn/;^n&X  sF%>_p43W,qO}Fd.Iu)'1#~(WW>^y,c0#b(2D-rdqiH 3h!Rn7_d/۳uHxC7 %c )ܪ8iRKlwʆ^B t|KHl-BJe~&sWs\~Ƙ뵙nB~Y)V'P"J|EՉLh{9}Wqb7` 9@DhWHeyySb c%@I) }.vS}E>9OC_G5>ӊ:MAĞ송_:)SD gV-?_aG7 j]\" ˀW~A4ϬF>zX2_2|9A 1f!XcYDZr>c"NX/,ƩCFif…=D9'6i"c+!m&\o:DEGASCLqaaފɉy!LkU$;, +snkb\6͋y#M|Rvhڳ1uT#kx$fާyl80ʄGd`MSOO6 %7)qm]i$#Ꭿ;e3f `}Maf L$'.|M p:Ƴ"JLnf rN.MaRM ](>1"c6)1W$[Ȳ&YyA"f i[vd}J(_m*K!)#uk.}KfTT{J~j*!HLs~38ҴIy3/+xnbVW "(iy\8ICK3aSC2~GVj8^ŊDx|oZN㼡I %,S=OI/g7,C_^E"# !1 _]z{Q˒8^Q5êWzo1(?1BH0 M"T_$+" e4cYތQ/F&Z͛zztQ~ jɞ7%-FU0Bºp>vXs LG5 =@ 8}7pR͑٩bN{ ?W!fi6Џnu4h1BwP  %y/OY|C.:d|Aɇm\"|$Cr"vOt8"fĔٿH5I۳oQplEגrtI+˜҇CL/?eKkL-eͣ͜w9Sbי}6}n~U{bGe1Dk=pg ^|pD"nu$c,53\_6.~EP+uM |fueIi[M~fҒV Fu)[38S :?}St׏}؍reEw!1,_ך\T t}g}s Wgp? O%mHְj2H)W]n`v-Ҏ]uHlg0G ǬJ頗OZn4B~*Nd%6v.$ac0P-!M]‰{k"~Mv/do2~M 0)2A$C;aY4=CԗN^B eQd2[O긟˯WqFz|cTz{OGQ{*xWE`>Ԟ2|ln9BJTu !Z:\O+4ɡV/0,xMDv?Ɲ5qg<洲w_D2}̅jr>3ƙ?CJ4nN4>ܬVVhʿ g̳fsV#jf܏1Jwu:DOu2emn̢r)= ODlc;9++|5-Am 5ï_ J¯/ٚtrJJeݷ?6ך`L+Y `JL%czޑ?/y{ۡeşX`3㗡k*՚;;ȳTiqh~K)j#0nB 3c`D/J}^F!+9"yu%¢x`GP*ߴY`M1^[Px< ?^%kGZ( qfo EE-mņ|v+zwPr^(5BVy}X7;KpA@ (xՖW+c}; w@^(Os^O{17|6omuvڱo8n̅}&OxO5l|gOdbEFNOxOw>00xMmETnNI>v/2 |dC꧌S9VFbP55IppeN}yY3b2 2 B&rz޻'̺8\Ň,dLrO:IQ9'0Lb2ܽu a l#L> 뗤S1ƈ鏸U`}e!du9O6V vDzzI; eӁTlqC V$/_KKGXe#DɐZ^U 0:):!=1Gk_xx 77)XC9bubm n,v=?#&qժ]Y.*U^WF }&)vh笹)Ux;K_Ǔ٠Cqθ~u"6?km]znotwIׯ0E%k^;:,^R 4l y E,|v"PxH)aBy-辖ӯ/{oό8 n6OBqA*AILq?Ӝ{~WM;+ŲչvI#_'0MV"^[}G&;V*|~qIި1geVXt 62BzzRi]oTۃZ͋WMZΤϘѶڮ$35>~@0/N3 Sg=8j"6]̚\V3ڲ26o&T_; d}O,lC)k,o`;u!72-SClTcH*ue3Q[faCW7y'%n;Rf5-vȪ1r: |>eInV~b^LKp3c+Y}IǿZ -9P-̧>n328Vռڷu6@}O考mQS/ u]Đʣ Q(&ϓ| g1aqW~b tVW z)yp_̈qΏ ٠-KјBvƕ+fg0V~Hm'MV?nɝvщ9E$#xA_p@h"](Qh3Rx-9ӾmOʷHKK4[iTaFڨ?,1Z@|M.:/XJPN`W}>sz)<["ǐ37,dq8z /]S\i*L) u|EHtDP3QBRo*z}ߵvc;+fBӯsY .6s˲pVf>+ 7,yBDq\ ڻP@EtMRvxlu(WzϪ"xܨ)\i3?]x[v(cLLw;R.e(AAK_"yhMdhug΄mޓ *iQZ[YCVbh@1E UfdžvMԻu*N%ުcg2"QsJG %2D W!3R&' eqYd߲Lz6x-h\ט3p*MnLIk??~4r9LnJ\V'JjXj}Fs9pߩWt*lm1_,b`ɉ_.#nZةqj~{NG}gz UCojr~R$ROqwhdC:m2-6gtF˕ \WCT :ox|W(;>nj^ aPgtOJ$?{p7chfJ8l]O4|?nB蓍moSog=?v[c5DAf3]Ki}$Fjz_XmRwu^9e Lay3FMZ-QUvܞ#'E7EUY9["M`=; p>՘paΒX̠v>"'bO"_\Uh+"YI%F_q^KHPp }piewqƭYd-S6>GA=,ly7Wӟv}"J]HD=Xč8~Ƙ;c(zEE4Hו!c+aޗ5{<,EgM@տ*˨vC]RENύФ: ȧ liFm?qjI~JR-1ɶkNjx1Zp}9<=Z&a~MH9_a@4U0QLeveBvxEv9[g~r,gKv\Ö"i$H+3ΜY]fR>d%//;}uϦSߔbD@hq wgWR6U*n|An|_g1`;Q*Z~&6\+DlmBZuD Us qfhv;ShͰ;k97uHCaX<}^U pϴYꪅX0DŽTE)9ZwG|xm&ʀ`3V@ECRŠo}sQyݧQOZ޷gw_, Y_݂=Ȗ|XWXFJ/Dc̳u&RӰ m` * I]&pzK*$^#YEyÃjh[DsvѠAk3T.Гu ɢ4[w22u3*.`unz#0Sm~j_l~~9mM#:M"W='>ɍZ޿.6|it+O$o@0jN~`酴$͆^U祇;Cy-|8 ܘPd]S7 `{2.MEi"jCW?K FV2Bbɏ؀FnȈY)9[$o^Ic $Tkc}iN Z|s#Y@//>*.jH$ t[gĊΘ l) QY|ճ笯utL Q1sm_4€b8)VA$DBx- pxRtzE^bG[8 _K%ܬU:X~i}ݑǝt8"[M;rmgq`wx`NG@}!=ʟQyb͗ljVn`5A#"gy~4|ibu'n3R *;]!|g3μeC%zoMz*_}3b ')'})?Ӝe eP=6~v:\DX;`DaSk0%-(&!vSE*C%X U9 6\\pTW =+%.aUH aӐMǵgm5s!X4fLGx&`bVxƒb1̟"WkU2.X#ҚeI*T ܧZ_.f+h)37BݱUx=}WglIGc Mv`:n5\Ilp]QݨY lMϯ+ƤyiD3uGr9#^e[ĨVwbYo^>o n^Wj SFK_cő]kMۛ߱44(?)˴&3RxB*#`Xt+6mtIz^/_pn gIi*-}W'=0rմtj21=z&Z+wo F*_Oz˒t5\ E~cu~Y3J$ή%RTv/ v{4U^5Q@ 4wO6ӴcUHc7LRiָ^^{&Z4_@@)s./ޫ*jڹ'>Rc7] G{@AY!b|ס*6ϝy+q{lJ@m*(s#0É8nY]Z"ښ O uɕvFJ/E aڨ%MlLjn+*8Yji(ٸE vΦ?wKӁeW\k|RF6ʿ=;Djտ٭zJBHe¸)uF LtvKh.b!`JFHy4D.кGodw 7k 118RCC66E3rl[ehNL ;o6-~ xct W^  *cV9\QrOb@1əzWX/ƞ P]?*)=mQRL.)B0m^saԄ;`}_l΋h U>76תNe=}÷8f%e~fmVI4h|QlG u2h$-㥇b\ќA7!1*?s<D7F19#N>'Z=_ u):|hL1gq/Y|4R¨xQ \(v-TAe|JyP_"BgsFiWG):3t}wDY+Fu%!Ef5rRq#/ޙh[S{Uk$3^zGP/Z獇D8 R[J I^YZnu7'`虞+9RCbwk V?My~&o9MO bg{۰X"<v-y$VBʟřg $h]p%tKo M 72͐3s SH(좐( V.&X1׻\Ҁ"r_`I%-," QI4=Ύ;ى:Veq鸳PdUP``Q+EU|́♑ ԯB)# 5c`U{ { գ *G'E`w&LAh>@#rAJJT~>tN4-9S ;E;b*&(QQĈz{6kt'pg{5=]2'Vsjjm|b` DQ*I$]s9e(7pFS|mK%JFgD93|5 >~pJ8cV4'۳@V_0]ը7xQ:Eܽ|~ΫG!FD ҁݲx~V}pLcBj =v(|Ĵ%c"A+)}WդݒCPKODcB^Ko7 7_!'MgV%B4wH1v em>/SHށLR/x59\ȖWEO* 40C8B }~/6]-tq ch;U|xu,DV28LU`3M,M5KEԷbVJep0BU}˓sCoe9B xtw|Bx!6͸-~M |SQKS{g ֎-W9BQUI֠l@Su|y%GȻM BOzU/֪4*Hr2&@7|Ё{`3־[_\P˿IcZ$鴉6IW6*{;$"&aX8'Lm$?_i5a#9:}_u;j!"kW02ޒbkwE&Z](p֊,+W?" M'E CHm)So"A`I@B3}2s߰/9˲gا2}O`ߙ#($ArS:"aw@P,)H#{1Pl94JK\s\ʛ|^ *KPo$V^Lϱ>9Olo|kЀ!VFHNӅ!8o(dGk]4T7(| u)KҩZKtxNGbtwhN+wL;%\rX)Pf$NBZk~rW9K-BՔ; 7(wOGE/#7z kvC.ynfyM]KcfVtU,D xFф- ew [%d*~(mpkXǓ8%A8`k{C9\Wra(R 3F1q%V4An l2Tp'׎[ѵy_Neq HS9O@ _MP{^tLw#fgH#{6J(UI!j>ҠTQ< Hjzہu]1i{j)ށ͇i| 6S~68o2lDWCwpΏ]B)| W+$ǺJ{FD`8XԌXp~K<>S,]]D6/6K5)w!Al5ѨQS|uy[oJz *' !OOIxL(H)<!T}VT"C3BX96 w6#m#~d pO2)A<]<'?CY3->4w:/:6/0==`ll,2 \^-)v2uTq`ytJ,dV GS gUJC cHGZ0:R/M [?lͿQ[m?lu6J>cltS4F+PDO-a$,髾([* 79YWQihkۗx`p#:`;͙ŏ`uԥp+azzGC*0 ] d(c3UxI=/4NI*[ id2KR͍:7@korr=4Waǻb71 p&;2/t &~w#??IGE}$d,3W'eRY|>!QLiD^}ʌU&>4 JJc~řab /иi~.sFD's!/[͖Ml\ ~h_3A47ސa(uND9Ż$jWhSy:uOs!JBtR<5&Xڢ#MM6wp_J79␍8ug í{dߜ`^e7J.m`/aQkI\]*ީxŶWcJ{%^D:%!2,V h>K?\+J찱"ߑ/,MI+мIp*@6XTy̔4䱡c7d!~1x%"Qi}SaO7EƉ|WEi7V835#Jp,"_ĵf>â26|US1SMضpW"D\#w3nq15S! s8qBq׆Z/귆Z p$0|}"ў?h>|4$PGa)Ń]_>xql:tP+p.zO}\}AQ^¦&aGBab5X["p2 endstream endobj 275 0 obj << /Length1 2595 /Length2 13667 /Length3 0 /Length 15133 /Filter /FlateDecode >> stream xڍPXݵqw]i`!xpwKp'ݝ7U[TA{ϡUԙ@&@i 3; @BQ] ƁHMab Z lB hI A9W[;'GW ?C@ %@NV.<32:Y],vƶu?!,]\XYYY@N"Lw+K4jdl5Dj uZ.f@'8;@]VXo&?_r6659{Z[̭lei&/Cc[glWi1U1s6urpqfq#01KٛI.Έꓴr؃̭aio ,B-8@GԒW O_J_bp>^ 9 9 pqrx/BdgYLV@1<ocaf {[16?-yyl~~~/7?aT)WY{sjc_nP7L\ 7ٸL?/$"iW[ۿtl= *_Smߛ4rZYc2[;H+gi+|[kl* g_w ej?KoSJٛ~7 %nn;x̀brANΔ*K7FV߈*XElV߈*qXFV߈ *k(FZ#p-J]7gW5~#pvO7gu"vLzpu&x&NƦ6@+c[-Wh/3ق? /%j]d`S_/q3 QܿAwV_w͹\6]8% >%? x X|c vzpŀAQqs9o.=m.pU\]ONutο;92_6@?  ~~; vgutA<\A8\t{CGzt;GSW']z h8?2 j#rg[]o vTx&3(ά(Ye4d|ƛnC3%M Tse Dh1ƛqJ-[x홧`.݊mλz;@ :yKXV!T.9պˠ>z4W YͱPVѷ⭑oS3z9/+yCˑg }N! A#}#Qy: 347qc}TP!L)O$M.lS75& cX{Mcm T>&n\ 67E*#$Ҁ]ZJ0{t/^k]xB8$|Rڻ4{ߥ%x OH bPlG:c[W}7!E<)$5x{|'S~RW.^ޯHؿ6ܽ}MofC$GnUak),qW V.ahM Z*(kߌ[zoR0wTP߬hZBiG?Υ.CԏVӟ=SmoY7YX]e۸}:W3enF[20p"@Oѿ(ߴk{htI|_Xf-Ј`|F/Zy~gScfc2sNPsutX ðimo"ޒGn(#P77\kqzޒ+ź BC~n +۳9Ph/nc[<DžP@dp@6(MGb]5o/i F#>:M0فCUL[>ȑ:ۜ`AT2C.`Õ]I`|EH6Qፁyy;Ò$)Tct+% h<Y2Z)LW9|ƁT$lٖF/{AgYur&&y9Jn,I=}B@eFn}4Qg3/wtsXQ1v: cg'¡6"ZwY6E1;V=u` lLsIS-N^+ED{&F] -y9+'a(yV^pk %zeUstv,s)i Z\kH5K>/^/>ZG9wnNc$GT|'o=cU^kɾcP;Y L]5'8Չ=ݍ u&ғR7)V?D[3Xp21ұf޼cBoMZ*M).H?o#އs[3=p5kDSv!;tV5m$^q9mU{J9=a16qaw4;MN/Dm|9{KLqeg(ˡ4K; _.(1{ x{,bE'31sŁ+ғ47:-*8h+-^冶0o)ulq+F. U!6GP(L&pT||~KىݪSz&4[v)wF'Ψl瘰^WN1O0֙{璉)6ij=RO tiC[uI`@OR~RyFԎȰdF`yS3_yT ^q^loo Z;I$ %, D"ΰNUX,25ೢag$ 0G.P2$*{Sol-*7%Ƴ[J!}B»<0cQ"qiOd7k3E)FfGKVP¢܍nd3B#ѧ~ZgN%N%wf@韫{]CF\];=dW䶸YIhؓW/1޺6mj\GgNCA XG3Zlϵߢh$M|OP4IvUTѡD?וk @nCݠFB+{BN$| XE,b):$NHM﵇s8 ʁJ9zgG)HK0O6(A:'T[?H%H 11m? M>*gS\$]? ^ép_[yr L$qVnA*yKG|6T= lEtxt'\`x,]yndnSyсt-31?軵Uʵ&p?D:NwBޘ2dӖҊvCU8K*?snwm,fS/byC?:teQ!wa[1r=M8Z8'„)c {)n H 5!^4vQҰc:^8${WOANI}SZOt]nD h_%GZge 1MXL;`< @\H: % !*@4sdNe';0FzHau(>f1l˙~}E00r?!O%ʋDͅ@Y]qP%E:~5^#@a/D*.x%SŁ:g#-*LgZu@V>,70,9dMݲ-IՂ+l֗+"m-s6EWzF^tUVȢMz)R"+ FјX(!t|ZQ"d Qv'/O?EB2H%%0(憵Eb*Yyޅ 1.3{ >}+IRMa@X:zidSZ}WSvg[|ޓ12Pwz Tr@p'߁ >]ΙO82)VuPRJEqOX rݭ^t+pMSB# LYHFV-Y3Ѝ3bRB}$צuڝ`rʑn9ƅͅmCSKUW< j0ov[5yLg^ 5)!aȥlǝԨ+J f8o)Y(($4[CQ [4B6N:uMG$@:j~0s"AI35%葲wކKv}Cp'g<1^h-,a =ĉ{$ڐ<..sJ-sxc]犨?xsM>b=bqB yYQz7(7iBHD-a$8e#Ő@oWG~D^ ur\RQS$!{6iggܰ<б0='q9} "YүcB,+:cI\ʫIl: b/W{J%y-;̪ӏks%_E$Jv0B)/!ت"%{౵֪N*|!~C '(ƑI1˃1]il6.>cKoMcXצE-r݉6|8 LyPfCpQYb: ߪJ4VX.ixZA%Zf~_vPSGI LWOP/dUhRP17\M1ytٕ'-sϯ׳*?8`1-u]~$ƛ">qJ]J:OHso:#=C꺍1 3ZćrW㗐8%-6)j Qm+2F6z)ǷvBWMgz0u#]ҾsD}61FLg,Z~<62\-ijx 1p3"sU~n׈$e펰9u#wv'g=7NAu'9a6*.U ǂ6JTš-ת8x99j| pR57kxofKy~.F坿;9be.˲ᬚ@y! SS?ʆݙ6H#x-Y3orNX_z u9 ks 5Y4k`[U{X[is:nn^_97 yz)'d cSt 䳔ꎟVja\w47Kðc ci?Гc*/58Gz⵱o TdœqZ>T9$tlj-Tx 3 Q :7Z7v:/R_ hrTO en{@ 07qؾ[_>dOgm)sI~4{h [bGB}8+(5_L~.-:>]eFćg-zSs{C42I0&ˏT&. 6h:=. 9Ghs>1^6}5|+J:Z/Q[@٫EtOȱpDGxju9Q~ӜV^}$g9S#nG= WqphH*\WعǢe 3,`]\ 7_k,,\]}n"M& 偉{Xʁ;f^X;iD:fkqQ5 W'W:cR/-M0 %z*p, XgETh!v؛Zi=%Aݜ|mXcƠzL̅<j4pz˜q)aZQ[Q4x"o7Fcelr!GJ5jgalREKXRRUUԙ{:$ҙ `Fh, P6JJeQlB7(:5Z @6L퉪<~;}5#_{*j%"w,|GmMЏ]'_M#:-m Yf(D:#I\ۼ?7)&5x"pt"[WhJ?;ǡ`D1,'$33ϐVQ$<8EC!W别?>z*ޕ^:F0֠crոhHؙI]Y|9ҬE`ϹeMUސUyWW{7$jLTUA"fz:33,2FUx<3dMMv!s=bED (&dW$#oky_$7Jĩ4'.Տ* "گkZgC8ѹ4OnޯP,,ji~p=Wm{pj8.0# ~Gܧv^Oq<[J%Nz0_mG ^( ?sZYD $!𶩩v漙zh gU{(h&(u:: vr76kOO鯐O Ŵ~np0 c)q5 )SNC9\&ʁA(>9w:å}Sݩ@_Vi8Y{G:}u[KY'?w[ݘ?:$nYIQ?c+?lsoa̓ ?W5km ]\5슙P7<ɥA#9peapٌdi?s~5aK*zZle :k7T;}y 3 k@o=ܺw1n ZZ-4-+J#27r߳W8$K!-ل<%9)|ھ##BF@%!G EH@q.͌ΎxUReL -%(;0њNc~IghjAcbݚQ2ENuW"]>:[+ Z>W>m 66Tg@RfoNlDf2Vg-Fʞ-6yK.l3:ZB*w54u<^>擡-9]85.0oj 96ۖXRGqFiKȡyξ ]V QmwwH e;gҝ^!Ăpb\"]ot[]3Vx@vѩռS^$˞gke }w V*[: >@ 2C\ :q R&\ÍИAO1 eD'ǎn1w㻰5mzÆ^@R{vb|ڸI]*0@+F*sMd$MXp'/IK%60eMLVKϻxqj=rﳔ Z|y%Y),2ɕ8ٞ5;xǞ错͵ DccwRN8אvtFƪ_UUY^S)ʰ&I Cq ibW /az. lG1Kptwd R Oggh*z&-E.~,h}P Bڸ_Gm*gOc;~!J'iݺQ6ٰ e07j:ͨiMZs;C"\w>Z=F5 P I}1L,֤H}ZH55//qjoIdc*IaH"ޛEvĸ"Xg]| %Eɞ1AKh%z>p7eV vC'COK-5c`-ç>6ɜ+]LBj-GQ$YV۰Ml 5=.;Q\Rق qmf~;`9lV6.!Kg~U<xe7R˃1'b/k>$jPz!D>.j$ Ḷ2IsysڠA&+kQf;v5J#RmK]j;?Sx%7'*F[ 3(X"HXkYM"x7NJL`)и:䧭,J.yz/NlZTMO'`.nE8qp3+vy{q5%)ZƄr%}^\z_,NcZܚ/%+Ưy[ ӝ.YBz̠(Dz Ϛnz'XOh+1`;H5bxĢ/j 3tP݉Li]85&|WSVBN=H#vVL+|Am~?TgGߔQגf&@ngTFۅD@ "z5[ rwW/)~)f@A@:9k֩  (ǒo~Y=hBV ' e1ppm&s 3rx7'V_=Iݨ\}z`>LZJHg!5]<(jw1P"Cܙғmi,NXpLSqb:( 6r[%k |\p{B,cvw]$xđ!U.$A09ؑcIpJ9M<0ޫڪ^53QXN[-dNB{oI{QKQ,GMլKSC#{nΩ.Xmʐ;ޱθQQL^#vKbE> QZ&э7<\؟[078B5z„<*dfM?jky6l|#@j7BA4 8t4X\Nl^_-U h6qȲצ$L.KF[ (6;­Ob U]l$w5?٤dK_^ݴzraw"OϖwCmZ*(!aT/"wWѿ d\y-FZ1bR߀L`OQ^Cɱ3jP|ݑ"lw+ջ8m8]NOU݌y׻9s ದ)Wv# jٵ7'&xJP?rcP$z E'4Rg+ ]0I֎$?|~cOcCKy9 \ۍ F;gWωK]7!W 8ЃOX{Wn.OK=c{LPb@nEe6iP|jt'3J|2K BT>p`PKwSkToӽđ^ wB쫏ȯh1Kdrm!`#Kɯe :vCI6jKvl=rgAf#c%k OYL h2&Ϋ!b{.a`U5Ca{ D ^cH^9Ȣ*FR)oFQ-:0|"Vƥ PAk)|i}g2M5AgwF`>iq2(\u 8REſN[Ť;ɯž]5q4B# "1p;g*M@E&eȪ /Ԭtmj7wߟf@sq[ؘ\jbXTh]Nn+B" a1Ne, $s_؆Sb5E9%8bEZfn;tتZGn13hޏߗs}D,-|f?(#Ӗay|}>]RVX8Y/p4 ?>;J͹ ʇ)#K8$3"Y@0r?L|~ţі0NTd ale#r>8C߄(>ճĴ}6gd 0Hl;Q^%&}@ %٭b=ڡS Yq"K->+l.+YIжUa:w,&C\%/70QGqWv 'nWox(<Ld]H(/[Ho,MGa?>\u}]$Zw,ðE}w:$O2džqH^Ci/F x$jvj&z@M* ѹ(. Y1^֋p6JWBvgZF=% ͤw_bM9 8;^_w"/其ƈa΅"qZ?VS<co< |G !S_[jg8/G/-nV|Vy4D4<&\$+M/. %hS>K> 4O/֗g%*ɢJm7~<~`| ؑčJ,&u9C oӹ,93^|u)? [t"]:\9vM掴pr+=3bdKk;NgmSrƇBX2I"o5R1zP;f Jɧ&1VHnl~&N֍ n&mӠq59Ў'd`&gC9%~;6MCk>qPBvձiҥĢT H+{G3R@xtذku5)2_đI!3LC"EE2Z8z=:Fa}cm=dXw ''P:\ |h[ҫx|^ ӤΑWndƠG3?ر [U}=>'9vNυԉM;>Xy{H ,ǔ~? hlrG_~\u´yխ;Q˟Y zd3ӂ©TLu ! ! Z)TJYikjK*%.棄<1%O@"#g 7;>D Kw &Z %H2S[_zkg!ŽolMMMggӅe~vc9E38:7IpZ=BEA{E7!v֟$QqMd9ZKqO4=dsz}"t5ML2ZOߛ<.p6yw*XX7WzM=ﭙ+"d.F0QCi况<Ƙe}6' dffڙ|t^sX\$^+ r zΚ DAo{)5^@ :xuꕞ~&:;jCȟus-{oZex.SŇ`@Q?*`FY ì7{gpEq*q]$v"!/Jj "TC.9yX`MU%QKV # ḻc^VAGB`2~ &f4D97#:N+uR L`]o%N?eב׵|QHwd~Y >d [hZ-MV~71/ZQm{qOguĻ/21IWʁQҔ8d*uTO*t9bO 섯15M˩O$E1ĶxT{Vqs: V~u_R0$6XPKJn2"$ ~G䖱JE#8OEHH|:@#ק(|mq>@7GRLnnFe\E9$Am1Տ2rJ(X[@DD:1=?9=DL<;E?p42ct ۥ: l*zq6LI̵CkJ,tu>T+a / ݙe:,FęBzfA*\MbFPE 5DizBZMJۯS˚+ OK-c[f|VŠ?cPsWWj;̈p>+棷 wzDJ@;0JN}yۚ|lP{.G_ %[~ <>w5Ɍ+ endstream endobj 277 0 obj << /Length1 1768 /Length2 9125 /Length3 0 /Length 10238 /Filter /FlateDecode >> stream xڍT7N"! `Ht*J4J#"R|9s}1)80(O( P12@1[Nf # 04`%S!Q:0(@ ( B@ߊ0$@q4aP0M 8 Qq~p:p%$x0(xB=P@# Ni$SR@חex }_%tAJ'`@F0'/02:Tt6@ SYO^_ /_ ?A0Ou8A=Um~:R#`({G):`*>D# jf E"~ P}pݠ0_h utU  PK%"-s#"@ P\~0A jx 8?8Q?{/"8B{3J;J vXQ'd0GG,iW0?@ OHD !&<}4M5N0ğ٢w>_/]jpsn:!:< qK5HP_Շ?7W_V B-FB?>)7i(X[|@prpCPAQߐ*P5p?5KB""@A>:c?DP=8TL K'āH `@i~#qo$p=xBeNAA__P DvD Q@.PߩaiToáP'CvQw+v 9H6])m,vD'E g]q>x)9VS gϕGN<޿ ҷxWI3F<.Nzl@Olgeɑp*BV͗Ė.PzF”NWň5gb]E[$-35LƯ1b! mq?-s2 zjE#ɩ)4S%hA+)'EUtbU­ -Mk#,r+șz2Zܭ4)ϔOr/XC!~hmiT&S/S$9Eނ)g_@k_Bޤm=>z^&6ےtܣp9/W*w-NLW[Y._~6ow>WUŠ%Td4G5L^F?kImE;ULg1ED}DfЃ0>O݇kah#cM-:׾{ZI/}[+`]nJQ AG@/9hċŔ8ֳ#B3CvY}mKfqʓk7s5 `n/;̵P5u>$ D;05[䈎d?Z#[<v!r0W }H %v-%g{@}RvREqWY@Ad{-,m3Cgrd4)P;9!PI73}uHz9ۈeǡ~a׊ !a{/SQE򊖋]a拫 :E%]K0oL RJ)|3ǿô T}jhkS_:fD@P=hjS`GSI?_6+ qډ_(Qfz R)O.V]b Ƚ.?|3if7Rb\#gkvɘǩo4_Wl woR\Yi;-eMf^':ŶΠ}FL$#|UƎ H((^I\rK%TV-@[[DXU3;_B5kS_eT-u=8}Ѵ fdTIg~lK\Rx'VSIjC}ucT<7OVZ*fs;G0k (<יּ-3se&̐f!I,5zr?׻kVOeDmo]˧#X1fn:wMڭcբ> olCG& 8(Ad,˟-Ŀ4jZIҢ3mʜs.LWr/O >y!)s<05C%ϗTrcyTFFy6DGR$ÌNg2%[ZᚮtEqc+Fx1ɿH0t o`zZ tSij01 i*ZG)6_|T[TIsY_Rul&(7ߍ7.iR})]L,!9ZuK-k~޵ ±` uhĉT13k_櫄['\WU_  _3;i + g)^%\UQٝ4-U_ԣdbvsJW`2}p/$fWS OES>.hOk(&{n1?EoeonRT:bsڴ` DwLdpxMNH$D/Yy߰N,d@k7FFКy*)L7v5 K$!ST}F3+ܦWTK5SjB/2mмwjӧbڳ 9vv7/H[NLWxt@|6DpxY={ X ȑ~-,JksZr?cˁH/]˼R^Q=>kѦnɆ]kk=."xCͳ~c aCb};N{nvl"ŒBa:W.~e^2oi(` Mˏ\f#tiıiQPrUq^DK3\ʩl%pLMeLipͻg ܅: DGǡ'cXKRKGFdx(,b/ᙯ?y@wc]/ځ iUkܓ aHzt¦QIXyCOqD!PQsEO3IYxJKkAvfiu/TE1"$osuOr?akzZRWiEtOer3 |;'5Φ3Ww9 J,el_}FɆ=H2È4tuqbϾ3:5cNbU?4d؍FgX0pWg4 ^{"ŵl#YX }655{j_Sd 'T.=*%s~ 4M\鍴Ԙ;vO_\.W*'} VjDՂElQ|Һ6~J2)i+#qjqyLzU #1X)8 [%Bo>w`BYrSWfǚ]VΝ -u8%}|A=e[R멚-X+VV1S# 60H PAVcAшlsK/R7ΫFf61G>dhx7%$,8˩űS/=#؎0>x=f=3׀OB$H9 M|Jׅo%22yWwc,Ce9s(6.>9 y־yHb&:9e<seXt{z_(A|Q/U!o&5.'U -]Z!rUY(TFR x ϳ }޾o. MT}M9yܕ{Eѥ͜^(fsvAZ+QARwAա} 6 ?e!>!Mun{/,n0 ?_dM/Ӓ& ix7B S7ַf9Hlm=ޠHEg,c輑g2dM&fvlEu:O-zڦCXͱޒuW~oC>[IKXA(q|Q=cxPeb*|APlOj AYմUfeC?XI<{I(`r|?9`闹b"vc =9z0m렫&,eݳ:Dz DK蛰2o ()=9[,UBx٫7L=ڤЫ~~Z*ExmK"tq*^9%fj( >( ҅oH[K?m4>Q>Ce;~![V)XöD'\rE@'fi[APEFWbOliUeK4U={/غX)PԔCW2No0``M 3mwFm1v4Ko 8 {pS,ҶU>Hx14vӮSdo#9zϷ 1yJ};Nіǘ {l+Yxgd#';5aǨOt}:- Wez)<?{ :]zXϻfO\.l½d_9O<ٶPa ܯ2RR<7OƼwyrzR`KTau/q짹+.1OIedxIFi 4R+ K/-HNs=i񕏝sA6_KŌ?X#$6 HIv1p0W#4丈Obc˘lކ,@ ڴ!wzsI&^FQl"xv?j9BTb׈6h@&B'%iq>1oJ=[g)`6BχƑb.m` pbcùi1gJuFxKʻݙPFjtZ|Kki­ot8)|_IC ļt[,>NTL1Ja{?sJ-H$W|75% +M؜6r]fﯪwW;BO9V+-Vp&/:ڻ8xkZʸ@G"\=kl&œX F^ qL9orX*fZ)ȵt99ɞ"7đ^Kb>Jiꡩ[wCoGWf12aj,AGK,#PNj~Ndb6+/Z2t k-/Ց118FH^LOt-ZЏЅx %KnMhS4M5钲жM'MAQ i!-OS>-{۔7C+$+ WjW^{+i`nn.y]GXY§Q"- j4'ݩ/AD!j%<[q E#X1[ʚQkCKހzs MHU.wMBRoSD]=}= ~8O'<%ix%DŽ́[#@~[{'QFͫRR)h"a2xg=QvG^= K4{'y(`\a}YR`g˙c}J|]G 6Xز؆1GA R}NAF2[4|ZS| g:ZՒs|Fz]ezJ*|By T:5'WYF),gWR~R$uMS]%0dO%74N؄q?\hذ>m你vבZr~V`s%P.L!niaٚ (D@^~`W a|i{<7ܹUbj+Ҡ"HXܭсZٍ1 :m?lAŮܴVr}<3|eoZe!dlJn)*jjV9})h17g/eS"\~'#ZnV_ fN2?,xB]7 g Q)6}ؠ{6B؉$G=#}~qB>#zֺ*l᱇"`q3.xhvy.ȆD{JVXhGB[nݍh+ :rΘ֮)Yew;6uh%M E>&ʁ"-GlR~SM9iJi ̜ma7DG'ÚOu¥MqzYL e=|ݨ¡D8CntP'2po'gXJL>qsFj 2p D,P T2~]M(6ۓ;f "Ζءc2+C_Jf͊HS;FOq<ԍ}v1϶(Ix畆o{5Gk=NlReowb;=OY^ɩ?6qv?;:H߳ ~_\.($`g$PL~!Os{զ⤻e>ћu&dTx\Z=7%Xc7L,  B̍wk:;ܾN4g񔮪K&ND#aRoAP@S=CjU9,Q~ҁɥ^aPZNÅ}Twe:nx|׸G/`^fOݩ:{Njs*h la-N dψQpdMrTgɁeE4*c޾ZrhbU,6, B YI1 (.E+ے]61\™ ۻ;=MBIF=K.ֶp~SO~yѰ%%'Ywh.~}m"7:jCc#RhM.6iW2Ac@XH ]&_egv ߘՔV*j"q6xIvJJ̲n7}at}(⢚pE~LȸbpY%wO=ADC) ,HZ) tnh)Ϥ~X.aQY15C9 ]KlmY2mȌ]cQ]f&wxkIE^msxA>qA JU{q5p^lGuH X%_:;Rb=/l4яG|jӷce8 &iŘ빋)qyE1 KI5)zf^Í .##$j \mF!LN*!P~aLJ~bхI0<;ފi0O>:w >t XaO_}>w%{n,:9{SnSj[T}&j 9;zRjB2vi` 0JԖ]oYUL/yuyޠdPJ%pPCv@/a3׺o\(C&1i01sM3lY)Ɇ&# S@é` *#%}D.Ҕjc9ݝNݔYn|WxV7?N\gb'_GM/_yֳ< IN>캏:ժ ??<к˓T"5Hu~±"KF/PξHȐeFGMUJeYw`5#3dyuJA7ǣxnw~&KϪom|@ZJ;YPf,͠l%xig]K3Lڹ=\7<%Xy@PI>Ѱ`^ nUwm 0ih.r!7m& (q}#_D*e ٗn~O]ĸ܅1L:fog8"XFCc msy.¤jL}':@,qzS.W>t_35UMQhy#2Eל@e֩T_ԩ-ӞJdx-uG7|7Yspo<7}LBߡܣ[cqDYcÿ10 ޏa4aŰ*7 ꩾjz\J` F/Nz`cn>8K͎挌}[UI#y2[ֱc!J"*fݕy/ԕmӍv0ӜD]V9n AW6265pzFa̠*Ƞ_e^|iӍ<*eFX!Kݎ>Y}=I6i'[X()ic@ա'3 endstream endobj 279 0 obj << /Length1 1475 /Length2 7054 /Length3 0 /Length 8047 /Filter /FlateDecode >> stream xڍtTk6 tKH ) 1ҝҍt C " ҍ4RJt#ćssk}zz}1i J8 k遄@/ȏƦC;A="Q0\ y$)7D  @Bb a1 ED `w @CQxl/$}_'$**; E `8@:߬;h/ŋ@Iqr<`h{.ECmZhƋзp!l`$pcpApMܬSQh@ ptFJC g0 œ-%u^'"Px;!. Po:? sAxQ0_=Js͊py3FOBn݋u#<>![W6n.|pTEO΍ o "@AZ@ ei||P`w(tol`4j ߜ? 0zg~0o#S1{gq!<><"~A @T@ |F ߡ*p[@jo鯊ǟ w.MčpunBn^D+H鷟;Üd }3Q7j@m`nUAoAn`Pmb^4'@~-_8QvAoK*!_c/(#`/<>yz1@߄n"xTDgnDސaC|!Mf_Cܐț1¿ ( B@ğ:Wl\>E$L1Ym;XWf$dHT7}Qh35ؗ }=)yj]Bi(Uzi(3TPrfv >Go01{3eYi(`Z3qz_>VRZK_vw E?v0N5OxD߹N9<Щr89}l,E$xV+g\ AVۼ?L.D6dq]cj kښMʭV`b8MЦAЯr1udO?DQ~̌B@{ژtRO%k*#q#^tGZC?pA+/q_(fAqg}9 n?1B>tY MS5HV>*+|Rڛ[O4)y^ێtJ~ !$EF)w[P> D^v=FK8UE3_~Φ;Btk0W Vu'&՝$$)y3ѺGq4өk>6MޒW0V:aVS' NhjΪ?T6OwG'0s.My:0FBN-t+?~lr\)%R0:xHΰ<=y$N,}ε/~Yx4z?d&~_,+k?fÛeg~I'WcjғtE:)Rtz͛$b.?V.%CIS LSSF׬T=_>XTtt2ҙ;⚻\ YH~sF^]KL?1֣0* ?/_3ŵϵIK[k#w׎ fykCZ\M[*~b=% G3d iBA>b] 4>BGgrKTtא 5 ה̶"X9N6ws s5^:ŖO,yf.!g$6\#5]jDj^T?WL_hxaNz3w0hw4sicB[wvp /;_7֘ ,u(4d>x_E0j;*)n9C/m1 i˅DE-J)`~7]3Vuͪvڈ?Q8 Q֍pJ;k?v؋}0{ӣ1,,XaSɲQslYԼWSLC] 3$egn:)'ڊniTakQiҝQoHP}/$tTngv<ASaq;a~OvMH*w"Jaf"O>")_pɪ;Z()tEL1DhԅM{gL|myGM4V#ЧV`[!kODkOm"iI3f"rM0 w)p!S,>04KeM4/*|G<)L~1EHBL-I_'ib&݈SD '̜pUs-ğ zG(py?.S}>PŒ5:y4+Z%-> AD#3]ToQ^z8n8J/OV~GQөע ug'm:Fs wq8=ق6>vɨouMrh,[&H2C|YU9Ҥx *.WRT.%Մ=?ݟ9CA|!˖?ĝZ%!eXEy&50´X'{U0D6դ/DtfYj5Ȟ)OC߃v/C͏l+gq«o(P֗Yώ51ܸZ/Gya0{G+H{UGmyAjXΟ_{._M>rg*\őx@`ja[F(۾d2کIr @OHkZsA; :`Geݗz'4"jdJ!+Dz%oz~l̸oy]Dj(6ޓ ~Cd*L&_.3G¸cClyy_Fn[Nnەf-q"VԽJMMc6QX3 oD'&xd 4uHǠCގKoҘ,NRs15Ÿk]xsi %-;+6D`㌕QGq+wsr8M@0-cSlzIY3֨`=YسyG1x./+,–w[+5%$v_mi*2&,ane9ӗv) #DOB/CeO6[d]EBmU1d_djs$tCSȧ:3{L#L|+ofFp7x >ktԺ 7lt#"&qg*?AKoyrӱX,*G%eI4aX"HfMa轉vay"Ep]fPÝٞAU[ !ٻ«ևKarE%֟m tMo NS8*4Hޞ6a=;m{j5j_e2ܭe_K&~iS2j9*3- #i1ϸ~Ю5SH<~Z74$wn͚K7@3j6Tc @Z :w9beaʡM7+,\Wbi}G=íHܤ{f*/Co "$=`ТfGѥ/t n4W)#)ci;~tI.Zh&+(5 (P*[3ֿg R"!WX?p> ;5=Kd[^ᘒ!_? zћ䇴mA؉u쳩Dz4gX![,u,wqoGQj\eޟW0'5YaO+]EH~)t!=>wcҿKʦ,HhCPNC>%1wcziY|R0-cmt>vz<bA[-J{y67>ʘ60 VR /Wy,{htߛrS/,"]Su:[kk'W׼u@ JnP"B&X;0*ũxRN6=+D.YHx1|P~9",+g6fA#!U8 (,RUvh>S4[ ]XqFCWoP<|q(Υ54<%WCaG|=xOfDIuIXs9t>xy)MTݸIax4]>'Ieb [N]m܆Wֶ\Nڱc]*h3-)njzQӤ!(zu%;0΅@M̿Rޟ۷ʼn;Nw6"y>5$O7 ֖m{tbSN&J,y|zIG{b]Nf_Lz]Eeur4YgVKGlfL~jqY`b(y-5s^*niGF/ΈlY-"t@s%lqq%?_|T=ד ӷ%>y^e"Cӝ(nd=_^f6(4ǘZ_Q/n!Z4[wu!~*V ǽ' !,a>7O_.0T\DK _tqZ[:5ݺU~Ѱ^#>4Δpd(]Q% %hjB>3doetb\ ~>6DXLhiI6d8@?9CW\.Z~EPΚOXL슦3|a`>b05# 3.gS.ˋUpw˜Cf_WE.?lܿ; [RQFQ3_[|Fj*<5/3 `š8CEL?Yp/{lV; FrOw ~BPr|S/:Ҥb8N *ЍR(<4Ix\2uGzL=msH=ઍzЯoQz |q{ö7oL[Wvz&_Fʕ2u:[X U _eFi5͚6EϤ9ޯ[fk^ǺZIL#o_$$Yw!5gZwX_m!Ƕ4JقATsGrgs63ykhmCj&"QPHϡ`.W5`:=iEQooyr'L:iwlsϦLcESRfs軗 c+&MżݳV/e(n;}jUIDjVU cA^z`J$]| "kbQn!iKzE1SHTp;xSw:`inI:4:h\զ}>}xZY]K[-"B}Y>>Dt'gy.Ap.'%f݊@摧Ɩ-t1P| G!m5Wd rRݷҋ32M\FUD M.|+ϲuz| endstream endobj 281 0 obj << /Length1 1455 /Length2 6559 /Length3 0 /Length 7535 /Filter /FlateDecode >> stream xڍwTT_>tHsi Nf`KJEiD ABRJ?g}>gb30T jHFPDHXP56  qq1.?f".3( G"d᠌160O<\1@DRFDJFX#%=@WB"h".e \.Oמ(BQp{0c`P.1+ qh!$QOc` EyB!φ=+wgBD\ m7F:`((pipC.5u}7(ⷳo"B"3+lotu#|Gt0t`O0lwr0h/wàp-~eUD E`D?S}p# ?xLpwK6G(),@= 3|A plw^`O(Ay@I=:Dg4C~GKK?] D|Af&ww%%AQ a@DXR S߱$ mOŞg8C^ -r+a a3&.Hh+åh=0C.poVE_G8[-? (",_h;_^KI%UH(؇/'r9/ !s\8 QD?O O(( \B!=Za~lB]N/Q]늀BD&aNUa?*W>-4?L茔pO>\IWqWL=gc۪53cFKsIiW0[#dp|/pr!3%%OiTZf]π+ -'ڂsgaEG|ai 'wCd77דՙ*5OdكkԾZl42XJE o'J'Q0m+ hbi e,XM x''FѽquPeEVl%6NF=o3bF5=*6 0vR萟*^S5:gK;(O^oA֐~9>]3ĩJ%=ZKEi1qE%h)of|\i0]8qef%o-s5SiHo2' wȲ41R Z -RՇ?b)5gh%@?!, _d6۳/?ǒW:0|&@.zbT0A:_JɁOa٦җaESC;TӾ-ӿ;#5}K"S+HpmJi+JK[sqI\wBmꄡhDBM˖Y>bLѯf}48q6;f ? S2mPL7_OOE"褊V>&h?7y^w T<3z]-gwB j!~ 'wuPI>8 c:F>l.t?{^07P?3 )f>IPO$JvIy.įJPY|ߧЊ!z57f) q\^;P,lœ;RNQc!$n{yNFg۝9MsxY+ӷj͋RM9~oyf~¸SydqOj5J:=@&'/`Zli 7ۼq%zLnݜ.M`bF@D03^xB%Dy4}kAOֆ~>q\u\͚%B`shB*\)sI݈ͣϰ0v:!eu^i -!Budkt]L?X֞jIMg@> #rI `%1Ȑ_Y?kcvkϹuf !̖idHꑡR<4iqjk:D\9 OvSILL+CִK7yǪκxyV4&^adtLZdvvSOJYx|Zmz\vU: +^Ĥ͊'y.,-v߱$(8F+"CwEީd+!\Yi%vhWOaU'mo̤h^GLs"V"KJ_QIk4ᦝ}J TB?Yқ&kh@zhpfTsnɬ@Vmv&q["B6njs)~ipX ZwHr+l9F_46ZC)$w?ƒpѨMV%V@98!;xYy)i[ƹ/q2Gqq %quN5gM6Cl:ĺώ]\pw{Lҧ[W@4Vml|+ in[F^t \xp؆ vUWEo bO /J*ǾY#T]$,c$ZGpQ>ds^FcM,5fU\ETQrzQwH!ջE;7xtMRyYN+Ϣ~ܶMF!W:ë$^lK9~כ?A(.pX?R$wA̐k&7:b+{pNXFZr0@# |q;J]KkdYT75H&HK LI KQ8 {GVISA f T5L 'mO5-=9[6ɷmDjC yrWqRt6As;S0£#j-klTT5W>ׁ$6,\ۃ'5csz m1lE[k.ڟt>:&/GPiߨܮ^M$9rWwεA%͆Fx+-S(?}I P8;gʓPpC\@ɄR i1> zmN~O`Y=r;?xn4e '{"jĩ'ƟvԲbtNYhQ} ɖu^1zi,-sVA#}6N%߼?i˷#C9(;TKgo;U>gI8W]6H\6ıvmљQ_s߽T*8jHBQf|5 =Ӻ.~Eu_HSYLۓɧ[ߟ)WeD &MP];G;f^9e81uP IiƱ  *iQ2|AlD(6X߮GauԳ7OW/[e_MOr"/Qh'wљ#VRNg~Ų4 H<~^wMI&ż΁^0IơOTb FY'0"nIܣpp+C{Jrg}xq)'KW%pZYU.>jv KSqW6N ϼu%9|5dn1mVo6לRVϭ]pVM6^wT YD004"dIdyfGӬ F{7*C|кpH>P;6hʌ ^5?*1.OiP/MGŹM4w)t2mgS(De)cXcMVS *E3(V52x4N6O 9*5ZD95#oQj%-N\]fG6d'i}j/‹ 5;NmSǺOռ/t zO>5-3O֞JnVԑT†܃Zip5۔3FCކ Hv~PXў!1bJ|j\P>~OZWI+Ìa<6~!"f9.DY`y!5ޱ0<=cߓ!%HJzҬG{[Vm8Q7ZXϲHr#H|ea3+-=zztP~vשpp|-jfa S?Bn\ O?w{АVg^H."mw*Hoe֩U#Cg.c-? ~œ3~tV7S!Po[a'MetTQ԰4 >UDž9|4W,e_/S-GS%Ӱ潲J7 #+SVN19auEC8r F]-ʽx+7QTQUz^)5Fxm&NQ.FCA?m,ãɹȺ.q+aƇzyg\_%{~/r%JFv"ڕpR0d1lE-u~0D;Qo:i#!`&12*N˔YrMфsT{3~V,~|ib]@i.gm0 a)1S`H294q41`<ײtZCmʯz)i\)L 1AM,b}j<܂SˏN3d (P^7- #)e=7+-6Od1~2WSS08{MqпbxaZIS?.3KA}C@UҵZ.\2|tC|6 I9՟V{aE=z[ľP<آitW>~L5 $y+f~K촦 )ιbrߐ}']xjo&\@_vGZ ;[WK$cLUT5g->)[c^!vܞяO:{ޣ6[1OGP|4mb~Cu(y9eT?N2훫t}'!>aM=ؕfLr\E"U7GL%aYbЎ +ۏCN,H,]‰JeפYte0}Y93V' Z_}{wNd!݄[Uq8&~i${$-B `^uh. 4wkGr5LFf2xE,=M*(v"" *yD>_}I +^ rMB'{2DLd/+vDA+ոY1؞REUQCXTy=@=wɧ<Α@f1iP.z`FUeQ}Å={\O8SCX h(o۠=Ү+ь^;>ʎKN-Med Fm6j=VB!3Y`knڕ]\P.+E.Jy| }}'OU+C0r ^H?+vStn>?BS% Oe~zr^\HYĵuvK(~IPr>4,GPZ|w3mU qRǣ wat7:`k.o*T:FL1D]mr>Fjf.<.pTr~ endstream endobj 283 0 obj << /Length1 1648 /Length2 7941 /Length3 0 /Length 8986 /Filter /FlateDecode >> stream xڍT.L# %:$fHAiPb`FRNV;C;so{׬53?vfM>itp$Y5q u.vńpg 񇁬3ₖA\vjH౫`1 ct nA b hBW U-zLop}RGY %S?SE Rp!;Ͽ ФuuA}kjh0j] CvgD@b_r_Wfpk"Q_ G>--@)[G_w"aNLPD8;C< KF#E7~ @ X" mTT% E ?HuЯ`@0  @tNP щ`t"? :(, 890:D{ѥ =9 BqpJe l6Rޝoeon)"ި3\ąc򕷵* 'sXM_Tq zݳA)J.K2{|9=燭VpFjH,5b{R'8O8 &.CڄWPk[=PpBƜB#մe̶k,4W]e+(DLC4ғޙOBU+DB)ho{=fK좥L|!#r"[$}U_/M_#ڪjR]O]swXrTob*Լg2zEc8cwoBHX uvoһfżÿ£)K|X|sm_ƪp7EzpUVF"ATut*X#9 -*&y^ A1!%uʘZEQs D~)#!E>##21KT}F$( {GWo*Kn<`-D t֦mkQ!<8X0C%Ea ZqX}²0SyomOs6~5QDZc9a=)OiN0 ( x:[ׂ`$L!m94=eW8M6u ^>'&&˻]38Qut9f7G2Exwn}6.o/gH=M\;;z,}'髕3?`5FS6%|x?d9I>Q@QVHgdï3MLw[N:'d{H ;>&j-o[%#8gӹFm()tY ܆Ոsǩo`'׽Yw%y"P\D V<-4%irk~[t4z[:`b6\1sŚacAl;5a26pFdbPN!vnuoeJ=BuFgcVoc_lL5,rFPa{`Q>gl:,z/Ȧ%㪲lo1\ڒ>oF?Μu CZ)js#nB(OEi^5@&0˛XEoWVW>/ol9\-ef,ԥ tOXuaWKQ5ùjGSv[tQvs&d*SzDBrifGg۳٦peDOeS'EQ -k50)͗X||\F'Upw1ըhcYcژ_5It.~"RrZǭoG݅w+vZ J},8, (T8IyF$M0渹N 7pHhjRRHӰ1:Fñ+]k0_ )'Ϡ5uCYӉA4#v{ H*ɒcF]Fݘ$^' *(a1Ppe_W4ex(1OQb o~8YSP8F:MĴ4 hFc{s2|nW>oi3slg $s엖 f7t8%E=^@ϩ9g;O.Oۢ*eK'F"G[(DC. ܃M@ uv/5=#Ϙyri@j(k#Jym>+'*asUpڶGX%'1/\?$HkrZ2d?oHƦe3{ݴnxXjT误KϷuh(CHiQ0No~цQDai\#R DzwZv}d3-. m8{A2rh\]׋3 !0jeeI8qNzeڽ3:}7hGUxqL뱗r?^;}I,Q~w׳-.'αj:C}q}JY FɃB "cBlI*3B/ITkc:q5KM3-\ffLrٝR=tG~SGh#3@aD*@-[g .p<qKGJ}dB&ElͰCkD+-maHK++o pT8n Kۋ U2h1q5o|+)q|(NӖ"i()Z`I>Ҳ.H _SgBnSy8NJ_k²L>{ SW.Qϴxi s`KVØBY["poXR:+ tn%9H~EJE4 /dfʞqԃXI)"F]0l)tb{ :?):x/d Ro6B}y buL_YNo6CtW45j=1OjiRq`41x~Bm1SmMy>Q[/hξl5?]&qYjR^xD).rsv3á L&ic,wU}v廙/ujtZ|ɐT(zF ah#0QSF>l䰿.O~ܘv~'Pޭkv@8Cĺ8dz=،S-ǐZ?']vqdUj4}MԫYUoRKO00mwlS^7P0q5>2:$p" $\/͹+9P>otHz "ՙXPޔ2 sⱠ :|8==7]h)}23T.Rɸ쑳 S[ps oQM7*(5RA(rɰ`1c/R Bf/PTH\3QbNFr.{{+7cT^#k4+Uß\@wt5&9-~ 'ΪnEa^u^GEU'ⵠ8%`xF/:ݪv 3ViM: v%xe5daTK~%MQ}4b}RuxQabeFdXM/L>S_[%wWadc7L{F 9l^?JR1vQܕ 5>d84TcOZ SJ|7M2Hr4T {797 (.r~.' ? )$@bqINWA>bjz6WCb ~ ޘ&%Tӄql3g%1E0=bocԠW+S.X)I ۢVн!EG]Ϻo?-zo J~/N/}= l)e"3["*|udyi=3}UԢY eUѾm'aaΕ|vtz N-rf)/0t&;(&n5flZtPVꨈhj7C](Zc#zMdJo67xQk֭E30״ؾ/_2G~)2@VNˍRi8w( LwʒyHӅP>jxV[ qWnvg9|/rUlޠ2؞Ę6[%.FрLjS{ 䢳(FT,9/!ٻUUM&;&g BiDUfa)_{[>~B1RП}lJ>̈,D:8֟pjo].],Tcoܥdַ0u N+ Jŵ?k^x^aQo= p$Z` z337<@`p,SeHls|ȁ${Z=44>73D _{>^++pP<^8xq>Q|˸MFg|6SI. )6bz$`Yk(t tz'bO\ݓZ:xDՎ[G2;MqbDyD)pAiMn8{LmR::;g۫TTM{b"o79#K؊u*vVDT2.۸ЁyTTT0y|x#S@f"j#KATAG)YwhTݠ'\zLk4+ han8Mrxu G8[k-nY{!I:*գfTPEeuqFw䍯gXGE I#Dtes_A:;FTaX˯-]} Cz&G{qiQ Q!lG643'_o3M#w/(+}S6 h܄P+g=yQ\-"Nʠ*[ciSMe9 !C3,oO\QST8ƎsNMB>%5CX+RxkowΚ4.N_`Iy'9}{?o7W/ӔY4d/ gV EUtJ;>YeۮH,c!!f-ŧQx"܆gQZtnϧɑFeFww9@Fj&#͜ղ*m?2{{*?љۈ-. 9%W(iSG1n<&is endstream endobj 285 0 obj << /Length1 2238 /Length2 13740 /Length3 0 /Length 15057 /Filter /FlateDecode >> stream xڍuT܃Cwh\[pk %]a&y{8_٭[u*)LldbʫhY,,lL,,j gDJ 3G'؎QG3#W< b@.^+  1#W)@ 3sB{8,,_OW -;@dbd7r4}= 69{OnnnLFNL`G Zbdjf lٿ1!R,ANTnFfW z>@UZhof? d?ldb7YA6fE 9&gwg_F6NW#W#$F5B'G* hq;SQ_L^;[ہ~9RL]@.fbz!Y98XXXY8f3wKQ7[ KZ=`ZO"08,@v)ptY^`osf mE3+KJ K[j; `d`Yxx\,dO&Jۙ<$ک&IϢ7u4^{v45Yقl>Ζftg7}p_&Ǖzqkx?nϿvu(P .̓Mj Þi2q6܊W\-XJXu*8e/\z66z0JXο@&xͣ@i&8C(Gw!ZRyS.&QaJp+ttՖfʽ%j[XP~N=H2$ɳXS3^a44mHgI0B\TU[S(SH}\mD}(IkVN*B?h_Gq8eWy:tÜɏWWBy[h;"J; 7³0nTvvCq8=6A_f?kƔ:v|RΔ8['E9]:eG߽ 컕T G#!S鵀yhruFms_?l2-(\ZU1OS19 LN_څmUccf@,3M5 Uˋ0lL!q#[zz-`TG\m|awjy4}OH`cgFᗓG~@4yNjaBQ?;}byUn> :E ^{.WnOpj!?UDכY;$WpL8dwfwR7u8c !Uȸ6KvDWeGqoD?*iH%G)ih+&P"Hl=<C?Di@?)M(Տ32[&yvF"V n7>VD.z,$Ԑ#) ?XpChA9vG1w0]Q1LE<%3loXV?Sbi%%-FgCyT߷|g QʆܟT|I')Aĉ`}}_bȀtAͱl[j^=!.bLYUTos}-KGAces%ʗF^ 7P^8=|x& I}!c-d3&2 ػd6h>.YkY^~~0Xɦb-&^T*\_ⅵ@L"u9|l%cҼ,-hv;UE޽9x*w۩жh;҇OOD!AfO%glx4)Y 6d*hczlRFc!EX NF!8od$ 25GG nW7c("?wc\ES"O&⃺K: Eh*?'ˠ9Xm<=Wc\Ha'y̺.' )zXP`DDLP9.p}Ϳz&3Ww;.!>WֻF=loa8RMmWFN-]M䨅q)Y~,bnXbm\m0 WQ!5*0Cv65:[!V:j)Zw҂W '?ޕS K~<={}ۜRSsD&xGJL\c0&0TTY5+uG2LvvTtopg|t,ܞDf:+5g/8uj2}$VAi8v$ҥ"0, e|><ؘCx,.+D%7!K~с Yb"_Ͻxm=Pj-d 35i-(Rk 2wfziBN܃<# g{xН5 %4S''kPǔjئb^Xk7bcfK=QO 5i58q%;ޚѝUJ-tؕ>C4ɇ.կ ]Q~cݤ""mv>@ .X;l"1'%G [ ք](aU.~\ZJ}'wQRGN iS4}qFz_|],x >yؘsB t4 /}#1kUߚ1Z4h0P}#0=Q6|5XiӻVEOl&i꨹U%؈U]3uٗ ϋdb Qv . 9뻯! QѩwS7LF3DVYpx}{}na^$+Je,)oٶL QXz}(VnT%-kQQmn ޅʋsS@)bsՒPa76<_,B9szsyc6!nTLl;yzzCn "CGO^IқaBC*ݧ^9t4\I]k^+ek &NE+w{{ExG*/u6)Ik$Rnbf v6_-dqOIڽxCg1]'ӲV)uj1ΘZ[!I+fˀFjkSl] y5P&|iYK'37 剹"3e2蘦^gO|7:K$a $'[[6Fk)HD3_tAV{33"%*!l J[9ӝZLAqkc2vt́/]~w= BHc& ʈ%&J $:c?5ANȠwB^SYyGɇQ}6-?iaqi-9/HB-h2pPBi^Z7r6V.E;ɶ&ՋDH]7OȮ[i?\=mѼmH<7 CCi񚺢zx^yQg|,:"t erl/e<6BjwftK-8 RXe54JOӤS#L[hsgLb-%8ɓnsSgVa+ܬWe;>Sh7nszj^NVe+ B˵b<;-nnw/8> ;cAn,ee7 ?r|ttCYeR֚Y oݴs /s)#ēeZw!lZ|GCm{Bwks5phhp.cq3wBХ^u ^^ʵ[85)C $U|a/,lpxw|)ui%ȳy4gwwe~USa4=JX[Eƣړ^,mN[9$yiz16vBbWaepm,B%::~*θmNCځ& LSo0agtfΖ54$^%5b@"Xo6׳xo9' jܶ㋜3/'.Fem&{P 3v˕ͬNk/'ʯѯm \݅ǧ49uP{FN//؏_< 1l8'B4=$a҈[`//\/O䴱'.TA{[\ByʼBa,jkSLC6g\2n kqKn.mIBكH=CS1N\Ua-%)E ?1~ER$`W. fǽڒTDiHح0C9tJ[٘&֜ ޗ5ME*׻;3_ѭmM}C^E&O콁 ޳A)8t>+6D\VMI"4j+|L\qn˚]H(Sx"Rj8:ӷ,=70M~DcerwΒ;[ #r#Lg<7_UMZSU^M -?GD+Vߢq7{aFiJu\g4ޗ!n aKSmfL99FZ6ODL= >BlN+ɧBX4?"ޟ |(3 |@|U3>_g<1L8Wly\xKQ%K7a [*E k\w;Lav̔Р'k:L&TY 8sbCn푊8N >4kQlz2buVI#.U1qI;aet7"%̉_+e\҇HP.# RH^"h=5HZ~g>S: qvz;/cQ?rgE9Bc x 垀:]dE!onqSu&F:+NwU~`}Nʚ t~O=Ѡ+鸨cP/]S)0zGx &P yj"4ZSFM:nnꀿqwNͯFLA)i9uUϠOpi$~QR6qAc7c(r12嶒/r`v_cƼ.j[Z$/_uDQM΀wmzCc$z8/;|078iZn%<(Ď&3:bK;Z^>NvBVuCښZ:xGmbWDziA~DZ?'oↇ{h,Oz  կ|cYEK/2ʷ2{ԯvA,$~<]M iyՀK [0:\9 2UdYi>3ps["M =,a: {tBA v CoN̾ O+e,zAwJ 7ΡxbեBko1RҚ=gDŘuUSWg8T 6? G9=w:SSKqihrnKj ik #J˪^ .+lY65i$3aW _2}}CM ʦ!qR/qC6k8ȽM>}йs/o VOl98N1&xlƪX6O.HlSm>_>l^GoJ5X5Vp,fsP9LV c{$2@>߷FN 1 m%bl#F1>|+/Dn)HA4ǧLofc3T2Y@3zdש2KC~Hq&z)(&-,ܺ$-e yq[?ICErA~n:}!W5h%[# *rD$;QZ:ˆDu)*PIU0i58-`~oC *W#By~@GR e:x1pp˃TdO ʦZ-Go7LP \2 \S2)6bՅ\"d,fꍜIکw_jj Y%[7`5m,ff]OֽKge=W#󧴕`Xe {N QoZ7ܫ8*4|U#yHHA11Nb-Ozy![R'"r*y=Wa#"{|*N\NF;aH &1d/"" _l):ۂ8~m-!/[ l!@\VŷTݛg l'}ZKu.~~H.~#Rsdƒ =VұgWs,WP=g͝("bH @,'zhiڵwvdziiq{8Ma Ow?2u>LG\c<_J52CL,#b{BnEm}|=],RwXg*xn ;(HCRvLISp0>,*o6MX>qm!sTTh1_n@ZvZMxjk$v >z@*;)f=zzߪbϘ} <_'vz44cOQ8xZl mɞ tQ!uey v6靽&> 7qHJ~N2!&B3a $Tp1] ,Oʗ=Wjb9= VyW=:]- (ڠ<'}f15VmLop9`Q@]>v jME NClq(֋^G}{sKbc֡[▢̚s.D_j~ 9$ Q>mPdN^$ $[p7z2)Hഢ!qeeZM,Ƥ3AwȺNA,p>їԴkĵ 8{f:H4%f-,DzSOiLkz!)Gꅩ4`HL5 n!?hԫyThe{QUm|w% y$&$| zk%  2-C/k80s}.7%>+o|?2N?e!eO r@U4jyi.Q%;b+{sJ@shYBXcl.2>ڼhXD(b/֋= [zA{qXEvj5e89Ǘ )`pdXZD8ѺS>+LYk2s~2eՕ Qa(T(>3T5-{9 tL4rmHEY^WLO'4dE&%;sl7/=纅#yutJii SWvvq5}mc&lqKˢʻC /p:чƷŞOS&P߇nuGxmm }khdX9k#t6v+!X]q aI@l~oORF*@Ṍ*elz!ZhT;0|*@Y`KS$e@1zd^U 2 嫒M,3ʗucf.8"v0+w+F۰4b6ɐ-鑵/^kXh@=8rE//*?3CǝO*Gs5j:6gs~Th#B[;ڲh \ޱ%Wl)a뵣JS;DH &TtqI@*We?aĸvsh}D>&"SA,ܵ,]Zc&Ρ"]~4SËO3 q,B8#sS*t.0#zG|˅N2צI(,V:Cy-;}'B$[M&Hpe0gG_ǽI#QO+jR罤!S\>0xPUG(.jb'i hNV^KlTWPxp~Po{$-d|bj s1}Cѡx˦z"U"W: {tQ-Zê * Ts3f%al\78ydy=W";5K\uI =m:]_ԨO< pN=I!z U>p sw?AP#Y~(Oh c9۵kLy];#jDHj2Kt_=5mHXfP+9 ҂x48-0-`ڊJЏcy'Xd0S;x-!PIWC(>Y슙?d(X. ?Va;VzG'4Mpk/KQFKbv38I'owQ>e^KO9!Fy '(؁JWOpx:CUcB!t!@4I<Bhߎb6^2x+ܠE :lfYԚ6YHSLtaWH!A<}lYKߦr*--rMJW3GKf$乻xgn?\ȄHz?@`NsbD9+gy}YF虜8ʅ%VF>QT˓ˢ/y_.lrQ/Yx*^PSD׃ԆSLT"I5 ̫@K.F;{Zsxؓ$Fb֑OZp2 t'[H¤GXZ9"nԯiR_T a{1e 8«qt.$HkDu%t~ V" ꡩcWuE2GQeꋿA gz"*01tDUs".^a|lyy25%35&@. 5xWva&VST{[_+ps#Oc#\0U{#FR*.h 9<ў7KOmMVp~ A#O| g)f-@E-~]9P 6rf~F*a-!9^K9A*7&v >ɬAA(t W-Z@tkÐk.Z S&|sy}qD=yIE:~Iȵr8)i%G 9t8_"MLk\IF &%4g)/oi,"t^v_xv1%\|eƱkI 4cI8xJCglqcL$u!LjLP4#tLichu_8.<b,=tًDA*J@bO-x~~vچ'} %*]jz @B hkJJ}Rf(k5Z6[l+Cm K]ָ,p|nDHz$">{&X6`r#$]fB'ib`ˀk2m]X=0DJ)]+] rFvٽJl$D\2@wd}Dū~1]b0N&6l}-Ex jhwމŬ8U-1$abNvU^@蕯n>[Ԋ-c1길ދ=ӨF|wc6 nLF &5綶d ϺOC{62gyJ1+Դ'\H ^bc&C;. 7 0J b2`@=`'`gFˇ#.2̷ ~Q`~+ni(ḊXhn05HP͵ =ƒ] <A<rSqK4̟GC~ev-|0mKb@W#TmnaP^Lǘ9"E7LyƷBO>ÓG4ņ\ 6ᴠ ]U:¿n Ry$m'\{0ᩅOy28CS!^3Ii[Zi$f<,. lG7rA[=׌ӝOf6)^w8n$yi_wŶFz֍$B®n GP@K(e`3L(6 agCFg48F*?ChYޟȵupл'd62+  ]tg)Xgy# *rWX:>=OjMb$}h:&/{!z-ݮa =~tsTZm|&OI]"Ɵk$6V ( 2҉;pWEIx$Eˮn2Os( endstream endobj 287 0 obj << /Length1 1709 /Length2 8658 /Length3 0 /Length 9735 /Filter /FlateDecode >> stream xڍP.\ݡ@ ^S w/Rŭ8[qwwsLgz׳/&nno:qʚ@rkAmV P?L$03R Pppr89@(/C{GA+P%<!VΰD `p ;B@Pdj дAIA͍ΉR%+ l;]MTLeq[iofqMy%Npxl 9B= PK PQbwvwfB:M]M!f0j7ȈLa-Ӡ$0sKفN듂8A|mnPC w#.P;?F0d`g/|AVShy8RrúrwX@,t/'SW0:''r-!PDc!7@9߯3j̡ #wx ظxN ??W߁L!< wWͮЀ%y X*0L!!q?S/?'$bk?S;?&08VA5`sjMa+!њo!I6W8frg / ?:ؚl`-Pu::zF C/N^"3j sX;+EW wu@Nr8,<? /aUaac8++u82\2\`y@X^ϿZ#`w0}v$l]t]!Nƶ1,tr6k-=f+N3=mNXfSغUEuz5u%̶%Y%Ds)gٺIT(#p-5q:g e7U)?_pn0='e\Z4 OVIQnAs+]߻EqL'-\\g }f^*,=ُ͟J8@x:<eBޗ}gAQzSclp (aP1*d-O68Sóm!zPŬi 5/<4aYԋu_Ѧhj)26l3=OH1y g\} ZƁIpO|Y7{ƼCR<ލg#6~>^%%`VNb}VpCz4cC[[a8J*x%h_x"2 <.$g@N;DkFۡb"c <609N;!%i'M-`2=kYiuM|!3Y$q_rl|%V+M"a 8-+vst /ovP9i$D;p*:&I71&~T1\7:ID!D%\(*z[  vB[A.έƃц lakAImA [UOehU!?*mҙPԪޢ2pYKY2>5˭9 .XOۢ`7H[+4Tx{ydH*9 X_W\?NGޟLBOfb,FHՍ0&,8IM&ߢ_}0K nsjEnؤU{KPHҫ2&0;@vvLTwLB]J6[D$FxzRDaBUL:w;d}Dk/r8Կ<-"'" "R-fcJhDZ! >n["u?icݳJs^wirTS_Wj>Z\_g`I kW|9~?uo?j$[5%9 7F_2CQg2*}Vi_9΅oL_%ĮGΤK|QWp9 @xn_ښֱGNbn)~%~mce"#i;X %ضՕRfuko858ur>@71KoO4Y-) 问 MwM &"V뷟ik[r1j%Gc C0HKdH!ZaXe=۳(_a:I '(n!~[݄(1LFSCS>2k~^TxT1W@! rW@焾#rݪIRo̬l]M|5m*oa`ΩNF};,)ɥM$p7i!d{vCn'St$hz+  f|zw*#L9{BvsdUhMUj -lgxojՉR1 F67fK({$!hC Yǚ%J<EZTx)zzUwbVnYs C>Es˓rfEn>42ٲfe#k6J\, 뗭̨O]_#d'-[ޯSvYE`lBMcA/^L#ݖ+j04!ýUXi};A Tca]`:4k+Ūy>%Hqwڇ1vds~9 ,Ɩ]Qsy-At krmokК Zۭ/SPU'~ &bˮ3}%րe,*~8񍢶Bm=cgEA J:Y%u A;$ڶhх+E8$@'] I`ulBWNFɭ5Yfjl(ݠTOF3&TƸ^i|"{G#iԛL_5uE3{TTB_}n,mNv=,tLIߗ5 h0 mŮgH Ҟ;1rα&+,[~WI"&:<M.K%@B]e7cGi.lh&DT0&]|+#$zD/_׉IuQM|4S_[ %df8GI95n*"BqמuO2eq,ǨT}ԏG]Oqw^`=j"7A0-{?y=+;!Y|i# z31y¯PU.܎= 9BV0]\v"pp/]K9sj;.ũܠP#}[~]kfx[LY>@ V%ZV])[=:Er_HxK.R؜|@ڷj2rul&֣-j-=OSDxEÎU~uϮϟ5BjI؃UOXl x$K-Fr&)Kvb<>)Pwc:)ӻ=n& Ym`xGFS[9 @*rysVΗyZ38ҢNS~Zp 6b,O>\MoΌ% 8I5ZXpUr\ R N" H릝y19+Xt7_i#$$tUgs')R$̓8_a><%cPLL`^RpHci1_-5:^)DL$֖4QN^$ *9 Nb&XTU~vs1`5ABG^( 䄌u@ӘV +[PG2HvЂʊ4˪d532=ɩgeܲ[ ~̬Tȷ!cIbjʜ|Tx eioUywl#,ekg֊aoZy-"7VQp)7ty׆[(F,c{vås¹O5h 2]io{h>ӛDf HkBADDU瓹̎a"Z݇? u8 ٮ7,#MYqz"ZK4TX.gFHq$ Ox}%?C]T8 ?\:y z< 9EdT!*[Lqx !ӻ"j&˗fVg>41vCkz$p4'%mPڥ5Q@P]j=L.\Zb+!> sxyű[swv8~ϝ 7 q->u[+$y$&Y_q;uR J(!; O7\Ci<`Mt]@N~)2;p/@kR6h_K⧳H-S4/yi!!1/&ßw(Q\(ߊڦۨ|9UӞz,P-꫟SyC3Gׯ*l鐍 t/K4ȗw::r_J'K-ɔ(d21^ PY 9+ėnyI[T>bs2=bi*<:->'v|iy"B$պzC>vDގ ޏ3\EꯒDhA};z#SIUgȩ ]:^t3xP%Tktajе\\7}y@gD\.[+Q 幓%8jtĿ{Q gJHUdamX/R0QJUY,~S)CqfagE7ғ C}=L:ӿ9'h45\S˿ډ*꺹Yz9+ϴW'o+p"-S[ŎkB9u3u)yP]5\ n JumiȜ  =ǡF"$bI,Iβ|ZwGpgN[ u촇+ĺ$ŋS_ x @%Uu[2$!Zl.ؔm$&8e}jLTKA v~#~:^WA4jP[K䫕DqfS#Gopt;(g5gWS>XA꣛N/u_R"Oi%4<5C\P<3E;LUɱm#Ǹ_^oRE}@g[GiLܔ#sS̙BFf}6VQ7A 9y'/dPKuYR~LQ nIJ+aqa2`/HZOAm_&sǐہXh(vZ{|\fHn7E>7C=7iJn\Sm?1lt<=٨ vqn3@3C_tVFp8FvXmfa,GT܆Yx{GT0(etOI]ͷ鈆~]YTOlt9k8QV8]J83ͶC(`d\JHw.ZTƣ) P_Z|~hQq4ݧzrS֟x^mftrc1|,ǂ$[cCQM.e44NR|Hd=<`l8"UzCGt/X%5j(XT(cѕSH'+C,~~DD &8Ng U9/ jWÁL[N<-^ǻ& Wq֝QKUhH-mE<5ћ]^/R#4I\cr"2Z}w1>"?;9.XHo^93I+)qAakq2Sv%VVX(#Ir|1(S=:ḵ2zV?abda2zwyi{$i _ӴS]ee ]Wz0%qm 7c{DQ'71CH8#pcժ7ӲV?.MKD_ÉRF`m fЍWW{ks% ͆@>> stream xڍxTT6 ] H HKtH 3 )]J4JJ! ~9=Yk~ykϬg(`"( P mh@A PAr@ܑP\?J[ڦlBp$Iĥ@0(7.PpC utB} @ w(жE9A\0! | &%$%hD;(' q~ бu5 ! 0D8l!CHt@Wjht ?`?~_ _`0;0@WUKmaH: C~n PUآ'k>$B "_3 JfG!  u#^pWPï1=܄ 0hm@()&& @ `'_| nE致PtsE`;; qF;`D0{o#R7k9?aI⢀ѳj?=_3\DC,%ws?~[W(/( h-j #]m=([hF CPovÚ?v_zA= ˇAAkuU`/ lm}g^@hUC $G!;ᯃBv @_-2߼@!op~s~vQ%ywe#:Ѽ7J9I /Km򱢍}3ރQGrQ*GO9BcϢHm,-RLgŻD6_}yy݁|( p3:v1S[dxi]c>Ɵ+}Y\ZzLPZ*kPr?uR΃7H NE3D6"e,S}I" :Ju 7z!Dgd򺖏7 6%G;kC`.4EFBrPjRlLHLD-e]q܏4;:ٔp=@K6&^m)O,+ZS>y#iaB]CXtjda}o۹6.ϰ(դŠ` -JK}}ZmM]߉@6ݬt~?VTPrͅKWiqJaE* o b894n4h w-؟6Ő]u3.Q2 =cJILW콏cf({+%'M Åfz*GZͷi~;ޓtT;\mt⋳KS&,)0՟41 @UWEˆ.,E]aӈP>1}s#w9*РekhIfd*)cuv >^?iگX,Љ|(<&dshhmY4?D&S7ʳ˺`{N2`ϪvN.]tQξ 9fI"xd7izu@3>(g\ ̲r)WMI#TźU>"7TdM<*w綊*?w247bp|dxu(}O8Ij2&[KCӓi#Eٷ)cm& \9cge(ҁrKfؔܡ=.: oUo)ԥo-vs/LY q1\Z1bGIa{e?PWR$,xP=I|ܓz+YowVꌉf5uɊS9dS *#>ϳl(I|Yýl2u%F&`Jo/C͏P6;6Wc]V0h[}HUc^6(FVGս_kRHm~׎uԎ_X`{#G yC e ^.  q([xa;i&DIA{)Y$k(+.ĵ˳U%ÎJtI'Iso[nI~6RZA=iI͈)A0?sxZp6gT.~J:т1A@GHHX"WKq)-dEI"8;ڵ[Keϫ Wb*(%՞ [Mbk+7P@ g̃xxdLQA,ɑW<_0cك4 ]**c+ @;jVYGIص-3 }y%}aJB{ntEݗLwU>$UV~]x]Ki}0(ROݩlrHR&Yb+ ښTTHq^bFBijcGC,+o'50GL?s@Py?c؛/vކeE;OODm ?vzg+ (h=DZՑTpMmVR m;;Xxq6p{ $Zfu85\-5c}:(? !16iu+t}bT^CLN}QOL5Ye$ F/? !q ӜVLI\W`y 5+7WF{_>;`߯3_Η & j_ćuy?x;XҍպDmWNZ*N, fo[gz _y [vpb8w̹e%xv \ b.0>N׻֚/,_3$R?SI,~s|갩XW9Pe5ਫ {y0H9}:44v:c7}vQ wvu*9=Yg8ɄJW:_Szƃ]s{c͒E^S;|]p[[.q'gcgF:/#=$d[Xͅ^S>ܪjU_豵jFDUMw@)igP{,ziGmJVe3nb}ԟ ұ NULEn/z#XΖ{]. < B Uz{$Sg z5Sy'(?VIJqLt=!Y,MBCg %9g;ոڞWJBW_ _-G-{1Miu\Ic_WXt@w+d TE9GV:(v`.x[jP+wN0fc$vYˮMRJT4Gf9)$'A=3%v`@N>9(NY8@CuSJ :kh; #qVr(T+.bzRTX֎9dV&qYE -"rD,R(. Y=$x.~}_qksP$dyEtO1#pnXnۙ*=zrڱg=Rɵ*w)1zҊV e)";yoԅz[߬h̷Ņ| d`b I/SY'7`g3ٞeLQ'vN>>l=cWyXNm݃2]I!Ĩ9OЈGBv&ԃ@џ|.M%e/zOEP?[ #J꿜$XPOi8cƛSP!D]WnRr'??EV#joD;n~(6Nʿ1G(Oi){$>TR_mӍU{:^3{D֩ɬG.!Qҡ$sb'n,H6g%And5c-O"BFM˗C%*gٷ,}}ƒٱ!Eig {6ܽ~28>GQI՛>_z6^~Э.K?h'^cNh9‘/p/`_k [F9?5`}ܝVӯRiy3&4\ vՓzg?RtOZk6nf-eJ7fhL?Ry7^MFW HZ{0ex)xgK͠bym9pa $YgY[ƧM(G50|3@"/'rAJMG:T1aW4*;xIr;J*~O/&'d@QSH;M[^HǫEt N0Z877F#+NjNYvh_A>&#w5Jli|mRL9('cvaj(%\ &0, )^"&c: ؚ{ʤ & KR "n<{K}r' 78ǡTN8 FaUԒ`hj=KRbw]ҍM8fITԥKOAt>VKu{|ŏ:7xS/fFgteɲ+"M+:y.`$ endstream endobj 291 0 obj << /Length1 2599 /Length2 18476 /Length3 0 /Length 19980 /Filter /FlateDecode >> stream xڌP A!! 79ܓszf{)P `ffcdffEPvW@ tqvt $7q*8:d,lN^.^ff+33 ]x&FB ߯j3 _{@ hb439Yݼ'5/'+ = txK(.ne Y\A.@&#Pr:X?􀿛`ad'޿Y;lbfhdm` $ݼ& M\A&&v& R7HL@]+~Y\;?qkL2ݝ4݁2ۀDd@73337+ zY1&Pvd-!xn.@+!̭@Kk?Ab0]z̠c0huL "tRT ``ecpq8y8E,)`O.7a'o,EG\ yr7㿣H/= &v[  MpZ,jeL@ `iO#]%nfVjf003hlA+h$RAf濗`b $V/ h́^ 1 UptA}&ߢ . `xL .f`XLIA&?Į ?ĮT `R@]kA v?ĮA ?;(WX1324u11-%[Ǚ?b[#?dA n3G;|"o #gwn1UijGw9L,D-Ҳ$VNV@Yd "'"ߋGj!fvp7}YtU39I K * :9-S]LN@л/Sοd֎΂8'; OlBG7$WY@% R^xAڙZݟ\Aw9 _*_׏ W3Gtn{k)@AA 4sw_ /w 4CX^p4 k&ɰ;)0KFLSr+<ҋ#A}#B{R5Q(^uf ai{pXnA]xO3K"ٝE{~)]jN9 1(Lߓ¸1b\xbL!İn~~YPgu%}Oy1>C+z"[RiC`1lc|oWO^eGjܿ)X >sW؉e=W~ýw.t ޾{[P7:7rǧSz>zXkM/1` l2Jk|lNf:%Q` Q|iyǮ_f C7"|~7-Im4U$aDlcUEȕS0R&W{O&,j^%<&y>aǨ,̒X@8 jN7I`sSA f4n`AAއDpl|BI^[O?aRP[9Z4E*UfR`>^UK Y˕|WUi~C()'K\6F6;ۣ=5\CP^!X%Fn<)A[gFWߌ͂ dLʘ4qú^gȬB,}#N+ibU# 3n (ւpׇ|^K㭟fէv̧I`Gr cs)<))D1}^$=Jg]_pg3}#^Nܞ1'*^~.rRag6Ngp;AKڮ6}d4e%S}uzTi+2!y`q9@9)<#o0iHe)R?O80 S{s*229&~7pP[ boFSgvX&>G3,47/~GKxPqDJ 8tnw,ay <ߏ|Ȍn<b!t(:q)Xʷ4vv ųUmO A-v96]:5ٽOt'Y$:_8*'L(x*92\wF2cVpzf0]h~B6l(9x߾UܩfAVs]n   'SkQC~{*CepIWϋHֱB ͂,٧D , pl[|,5>D14&NO$DB۞EAϔTVU8RcbUEb7֎ Z= ftJ냾Ϲz 1)+_Vp(6SS'v*xk$JnHӐ^-g@<϶D㷃R(OTƛV0H2v?wL$\}5X] N ]\P9EnP @밴ݖ蚩(a:QzVG0;P5?b%CeaH_}%iE2݌R'*B}0H^+Mh&5`id_#h!sςZk4c7Pxb.8\!8 .>-qn$k8t];5L6L[ C}aj<% 0YS9vPm<3x?׆o;Mw[kla'bMo.|\pz4tx$r椪yV7q;'B QSb2-x+/݇E,L%=q Emm|ay,,-)񃾿GQ6͟#HA(x¥qbv8[FYu:}ӗ"寰%okHi0t(JFI~f>{MA"&@]|S}AIdXEvPcx4^$dH1 zWG&>-Nv'\Fh@pM'蔏 zHy%櫺yzN "_c:S/ihEthAVSA$3f[̘JtT]٠E|:'R(!]v1ejg1A |QD?'ҥ 78 EmJ" N+sdÅZ@463O9'9VSͮ6 y'{ [}`T~c2O]wJҟA~zGmtl13D'P_20|mA8# -ت]ɭ2IBY;9Sϋ~kRS?Z`X#7'a0sYHx7[RvѰȈ6(Zf-âԺz>_r` +G$Hp0N8aPxkzucU J}d#Xؕ#At`d#5sk:g473yϺ U1 %em.2l,Myz3 ?MCR89w] bVxѯ&Ldls:V5E4 pNT_{Z$+M;op?cz77F:ʻ  Xt7VS$ioB)hQ0]bg+5})~ \mX 0v L]}ywLD!@qo_=V&JGbsl5a;4}aFmv{|֏X֫.{qxV`uGw}M+Ⰵ'29W!$BQ5z*%WҶSɦ+jceV 7?Z0MSvgLRGr,ƜLV[B;ҁ q jD~Kxj)`KcsȂ #Xyi͐5#q[Mu=˟I/y ghTvI]FJ2-\SLqP> .p-:Ɖ1d7f.gղJwr}|q[M glMjz&|wdf+ICUg BJNrD N)%`{҇=52OSV37>o tb hZ}ZxkM GGmػgELr>+J06Ϸ|A7bZ SϮzHK]P;pt\!Y OK!hZZ~Dx$R_hVtInZ%N6+-8 Q >t G:*~bOy"9jK%*n*<*>pl@*_&}ي[ҼI )*IN6XV,wu|T7%EΨS{8ÀhFq&FtvꤾD֜ߌ<\4VFY焔YnX2{}S9`;T .$sBב yI;WU'URM,C<6tTLPb7{KϬ8AD1,T+͜v*Q]_A:*DeQA_Zb﷚M.خ1omev(7;Ồ05N쇨 `YO x%Z L?zIddHdNVƜeNQv[K+FYeZTE]sZ};SXw6#J1!<;܍!0og+4d:TspI%m[h䝳Me: /tٍy"C3._J9)x]\j1~I=P~uymA]n)e__P|c$cgSQ'\|,9SȢ(uˆ?zKOǃ}Lm:pM@=dgh" a<-z G 0!'lul'_c&4ذ u"";Sf HhVq!M&Ef^PV燍!J|!l] *γ$m*}kWO*"4xGnڛ?bAg8sě?&>usᚊk)q?1QAO]v@wǗc]r[~'O~W0:*1KX+ז" hg;5$)yH05 f]2+lG $d~HsafS< J&TG%3`q=)aѩ~%pBI}DTF2!^ÎܯA lqGQ%.r?c |k\24,5Kk*פ6"Bݚo V*;|׌I5=ޣHOzsOs$ E,8~(0b'ɜ|0'Đ9L 'L X~h4mE+Zr0PRqO7Ul o&dd`ˡ︎$.EndƩTRD$}ZnEڎ&!ȴ%cHE&c'bCVVkȒG1xj&~?> 0Νy0fzv(00%ۏ*'uKeD7/q#0^M ww+U gv%)ᦚ:e9h96{[^%ūv: _S==.8,hîX/[Gm6DT&qTP)r{*DRIz|B% l-;/ J4)Al\gtW39:?/%5uݴvu%+`.\r|*j>44UR ?f; 7i \nU Lc77ГA,#V2!;P E)Z ! s=+dž)]-1|/22.=F i!G¤GFK%|4lUD(Lc^Ei?ebrWs= /oԨc>!z/G P3y2vUf C[}f<XTj#q`fţgTȲb\Pe}yEFaO-52#ϕHSYgy+~"1&B Ga6>#  _pc.CܺakoaoD@ 8̺q*<[/Z^ˌIҩEoA'}cuK]* ;*r1PpbPX ?~,+nc 8J*HYQY=<񬩿kҰ̞,EWM]RO,{b3;D+LT+ݸ5'd7hJIzvPpwT` rȊ>&sSֱ _aEQWYf8eMCVo=o-ԅJ@I ɏQ'x;K%ԏ'Adl`LѦjnyOpM`[IH|S"`Nq vj򼁸Ӂ 1[iWWŎ]?ۼTF%^ 2fd 蠩At~#2D/Ak{h3¥GH):[K~+Z8`y20VS"*71ks`|gT\!Ż4MYvp[ɉQYor9aXw;|ڤi|K d 4tPhUY#ōT]#K?L$&ܝA DbF0e YH`Q0 h5Q"Q3Dqg8Y`XM!q)x,LKߏ}lp tz"0 0/WNrDٜ>]D7k:d!Bp*~ŧDVSc'`f.*U!EmBL.jQ owPi֬-UB} ^"Eu { T]v7 +Ɯomh>i$v;z~wh9G$j &+4Kѥ)9jG-=KPE,Xj4#qc5{&~>'h$mⳘ&0Q˼%AË}| ,nLE(b9]aQ漘~XOxBY(W)\q ;qJC2pP@YdӀ,H3eEP9kC/8!1)|:k>ڲ0)qZ.w]PY⺤#T_ӒJP>Z0#'kHr0{罁rcf 8*a5ZeKdS+wʭ>Z&ˀJEMS­4D )Gj~Y֟ufn 5m1&CH~.GC<'W FaʹND`)zqk uN5ݡشҨsBybrKLm03":~ scJTm0i\hb3PmFC K04pdh#bV}]8V?LIޒ5XDߝ!V*Aa©lTJtӴؕҏ}&9N +7:}=c |CzoVn܆pʐ2@sE  Ⱦsi/LF6 (:uڟqMl;j` Ƃ R2JU,x" ֘q4r@fF$z"yRC s "E(Ī'0t7 ԴPݖ$Q2`ɌG۲z_vCzUɛĄ7zqdƒ4Or H+mȳ::zr7-P_v7DHəy`Mÿ2Z:Nӊ>jB9oUkzrPv09RRtԱ$ƮLz*zO{ե!VG?e?'qIF@+B2t#4ob%{댭3T'lS@(Ny6ƀҳ8ͷCc27ˎ)5y"ن؂"Tm@#4PPAx?޿_vrUXpf͠U\]ycd8rkbi(O*_>-kegX>|ϴ)C{ƝZe[.v֜5 <}Dc`HxP4Џ9F4J̒`) F"8{WrOjDFq1k:x5r<h}C+Qa5Bq_C#+*\hd3xG/,k19PgKKDы;e'H*z$aB|yJb߫Í0δ[ЦkkGؑ}8P_)cxN{[9ɝU}ŤE>~H@ilp,97|!?C^YݯSlK dH3#' {e Mh+࣡6F;xQBɡ_HpJIW.o?z\'v ȹ6TգWW ǫrլ0sP->=l>XO0ZOi 1ra{[rei4/V*ABm|U .5 os>29 ]]WOg'WZaFjd``|$ jZ6\wmkO5}f7j)7X 4ɒ/BcFaE`Wؾ9gn>G5W#]sT߫B-ᙙSN` Ub 2'8(dafv-ﺵDt1{ɏIj5 Q祽覊:8j[ޅ; s4Il1d-4Դy eBaΠ\ H}:AokK @N 1+ڞg τ_]w$Y:gEKٞ!wQou}1"5 HZ"쾲fg^#ڝ'9fQ.񬅭_ڷDyzCyRG|i06Y2BQ_kycUAy8/|=%qf= -gV9tѸ>UͭogGˡ>NеB67X["P\bR)#*1߶3r-t k4eۄ@G*_f^In[䦝7P 2mKu͟'z&oN0.j<%8h>`M RC9%m_#;o1i9­jSy5( av輰 d#Hk tD)gQ(|̊Kc;+Jo` KMo^/Vi&ٴ'jiY>wE>ZȦr\PLH|iEM "l{"X]X2jvBk^: 3o hܰV^D)zxK^WB E]".D%C\h ٠y.iÝff_u/6KJ 3@‡oOE98VןWe-8?u|: d3n6-3nlk]O(=..Z{qJ ֯-jaU%q9|3F×IV@[6Z{/^lI>၌ kȜhMEˎt9. iY0bI42'wϵI q//)>U#o装v.~m{q$),kOXLno% vyj;QA'fH{9(h1S5My_L308(\4ѼQ^$JDڛn0>-*g= l82ZpuMgiz*]%$j mgS}Q  6yqD:} :TdL[j"ԱCPӚoދ5=F_%>݆rB@sQcL5R.oeɛ ܒIO3}e&$@q4//֔K38 om5=}}8AkX9#wπZċF|{g^| ԼˁND4;zACQ$d^ ǁg%\:+{K+Դ /M?,(:nbP[v`J1~۲8ubAO}LآMr-:MBKgᨋVgu7Dn ,BwģȃQ0ϰ(Qsϭ10 ^Vk`rckudQyqu4J$G,Mn0 *ʚ|g |m1l nWEt7&[E"wfu9pQ ZVg~XzGC0#"Ss|m` &Lm [JH!^:8yB5 sB)$Z ]@f'h1w܄&Oiڷ]ZĨgE 7&]QE)'.;6i[v/]jtg9ҥnNP(Sx6W\;}E* ^|E:KV,GxKYv|8i=M k!d{TD%5?-w@PiiHEE_C<WS4:C:u~H_Rv4Xi /ߞUHҊMB{ګ 0U d|ۑމ%&&gŞ bŁ苑'ypZlW/ !XjZQMQ@fQtd ?dV׳ˑԠsxJXy?ĨdDCt5h4NQسZ1V3 Brra$"&;upº)E <^]V%.D/?*_&3a҂خ0) ݅gXd8EB~]7BEIL\^mVZByefM-57dpYPsPSKƬ''d)T%I1Za$3Fdz*ϓ?"ȽerV\hÎq/ lC>_4&cWDQ1NW*Iz֪KKlt"'7xLHJ,!_P|?)*}߾=~[üʈ ԃ D5"s u}&gIE3!i$ s R #Y  6͢QF7aK՞a!/K:Qa'"tB4w>HӅ}'obwӏx݋(suJTK(g1(>;4?*IxR"Aos܈ciMe%=7 ^XLX[vjp b'!>WsR+扱vX%_~!ط0rY7pJ5"YVw'ط)H"09g`[v97MDG) “t73?`98cޥհSđP{Jd\];sҖɕ%X*didiVd 'p}_[ Blx g:ٽӮY,W *ћ?몕ď+[$Tzp)iwCzE}(+6mc VHu?YGIg~elOH5╈Fz.}sX/ -PYEi25nkew*2қ)#d׸qP(#'*3z&jE):0+`IK 3)BV>MʡZ֑]lYGbsz!wdط>'7~e@վO!.rʼnkCcs('pL[ڴ+N+pS3'>U K\F0ÀfpI ԮZL8ɴsF8`JuT, gO"d&M0(QT^4 +av\Ȧm%twds>o(4\Au(WFs/- щ{)z+-iPA}Oo+%^ioCnB-r')hM2JhtEL3cd״ .8҄%YgC_5?]a~o>` >g3kiCrp)ݍZ+!T7tyt0' dbpr7h;nLx 8R"DP73rJũ݀z/kt wߢg=[*׈7фc%ijH`?r6p,CoZ3}meϏMlbxu]IQ?zq<4tYr^oP}tϮrxȗ)n?DFW3,*K?6[2Ѵ{h'ꔓIߴd!8Fh[?;s5|JHz{-kU|ꫠq`D*yZDRrtKQP#Mu]|tC1B/GLa!f':CM[o@˫Z|sb&j3y)BWI=8=C~174p]emeo_pF] lrOW8Y*mxxT#Zc|EkӾl+z7nRcZ7 ﷹJ+ivDWl'U N{;93)2oђ,w}3ӐvkT.#ې{qsw`>.lu9hE6=0hR/ R`6Q/k*g7{ wf/]x˙2`|:yi^4Q܈<Hzz)AV8SU/?K> f|я?+Bz,`(AEI"آ_+;5 z5CL3^I 'pktFJѫݼPe՝qܿ1i7OQS&ݸ,bh6&۰Ib]U7o̰s[~*>d}4ixY(Uoj`7N$Mi]1Dkxή{ns42i,Cׅu"ߘ;⼐Vƽlfkـ0L04wc=L V2\䏩_8Tr:16 ayikA,\H[c^> *L>{iqo !e#;'Z)rCj.x |xݡi)]GNI.c@ HmC羲Wsn/,Bh,t@}c\i)X(sjVc:G&w}Otj{6D6UCa۹\c,F#ʶOcL)Kt}}cbz4U^"ܝ2~e<$ݞ:1 A.Oq,kCro0],JSفRgɓY@m¼uw.hxlGEtyȄtH2oZfB4LA(m- jc9uQCUf:Ew##viyzn(@P6Pe EHHG\B6=Uln tlc2"p )L~*;`T:& 5ۯ0y_6rowGE p~vhf6 )Jv `ug@FlXjl@'y/MO*&*4/zwQ 2l y:/f֟_5'nZ-G|{ryvOb_#|BDäN][Sndv^ \O2yo;.EyHQUmh l*XhSC#תl٦J_HԕoZ!`d ixʫ CEz+\Qğɀ޺u{E1 ܀Z]E3ehT3g3XY#HEa}4T9Ghȇɞi=֔Ƌhv͓뫒UFŌа^MKfSèw,hg9nOв=߇/o) -v6gⲾjᔧPRM'p5ex5!V咼y0+EeW|TPM mBN9`x!ҷQ`;gaj7JiO@Ax_Sk69v*[P}bۻ.l# ؄3LϞoo94~r\lq:`p{m̷_:Z mϸ[WH' 0D05Թuf@j^X?u|>\6cB VK4lPlv}eP B_꽁KiRVT#3ZLV4}᳕id1~P?R9PM'Ea<>` JHۖ&Yt6]y{4Ee_և> 28TFw)Cj@=LBE!_2-=!|Uå$7/!w :츳7_o}TN7tZ/,ʀZ`><K^Vyd,Oy{ix&{̃h tO-ϻ,4(Ց^Ϯw a"/7jEEe3lFU?1wR)^dܱ1V̝mֲA.83ap5vw~ )x~"ho!vSF)lL2j:6<ܽ8ڠHN')(=Rf\ XEqKKf&'#tw8K۸e~I !kLqR6sjo}9r,t`߭wqzleRd3 BP`[nC52BL>NoJM24 ynCjuzk ]||\+:hymCR~|ƛw,~Yނ҉\dnx+nYh5l_|i&+=Y*p"f )[/P1 9[sunxYn:TUbS|kX{MJnv~7=b0UzERT; ZI6!ʼpx`*t>7AtiL Psw˖oSs]l FAyz"DC.D ܖR)zk0sW:bHD$[#4yLyЁ\7wNx RXWmޝbbc~MS1R%?3V ݐ`s{$7O-+sW4-$˫3RϮEy࿊BzUyHJmq\8ەa7Pt pWrI:JݺBt8k7`Cexg>BVk=J}45 ?Ԉ& BpC~ӘJgr\ bF ozFv녁a^%W}$^hԘTqp.n a1rvUD6 5V0#[]/)p-Zh]w0<֥ $ѡҘ'60x-Ag9^.\YQ/I@h.BTxGvne-4(P&U ^|w{Q&T`MHrɒǬCIkNuaTMﱅ{%) hSߺ"RGx4<>Vҳ x}Dwo\9$#)i~NpgXS#a l3'RDZ.,rӲ͚#3,XMkAN&rܿZ%b6FZpGM sR- fn;48ycwv3Iy8F ;uBldeez?N~"ѧ@5YU_;s]oG:OTxHMynh1B>\9?^0h endstream endobj 293 0 obj << /Length1 1410 /Length2 1837 /Length3 0 /Length 2751 /Filter /FlateDecode >> stream xڍT 8T{/IE(?c0ތ3g89bƵܢMEn,M5-B%FAuy3}߻]kBga L(D2 8zQL6%T"_G0/#f pS=0rP,hK d ƣ'fDCa>A!QϗW`x:sa1QD\#_ B`Aw% l$L." D` <\xr5"AE!/<(O,0b`t>0(DrB:̄ D4 .DX` (K drόc"f \ހo8!1>p;ecvFY >A:ƒ!Ib"4FP[KCGX!4]oHXd)c,H~1x"u;$%`1'!l#$q0pR¿[ H"H%|a?P2N? K_~PN7\]<&Wtp Lfj,AUon(eIL|_y o4#!A>8.?iD.Bg yW8}CıC1rW]xV3wWNeӃwL!=ʔg-#ag⒡%ﱡٮ'ܓ+26 餄WdݯtKN.Nlf 9t[dc$DV+z$ e+^(tFc6~ ҁ"Ga >QUaO12zTbÞ$P;bY~O:+)^SGyCk{cg4A|YK yXÜz豲r⏒c]™CSI 6.oIpI(տ6$o[Aڧ/]XJs؈cV+zA{ik[ lhUaZ7wV^[{n~btͷTqqMwݖC[M)fV#(;d~f_֬ӯM[0=)0PCROo]L%udv=sWB 2-L4 ^2Z.]C0xL0]}AJc bOb~=~>gb5#tӟ4lʞMye+B,TɾW_錱g;Gw/>@ikx4uxyn}ۊS=,B" Xit?u%<ڤxl)kí1}β;&y9wd \ߜ A}Y_И(q1얊¼sԊu-þuzC-GgS IkŸ֩)@1ƌ mssWXڟG.4{\OԝzZR3IlPߵ7 /cWF̎E۾@#fc?ȦBu6MStp}SEN]YV}ܔ$m*v < WkNȉ+߬yw}O}%M{n&ij߼*7&mz}]Ϣÿ:W +Xژe3]i;ޓ8U'\dg\9!jv^xMM`>Y´KBW-m;WNfϫ\m5u'{* ;:ڬ]P- 5/-C^VWuyjdҵ:W(^ FyA*?0c[R;bGsr?E%2#f\&q1fô@GtRﺚ"9n4WmȒT^on{cJw&$82FR,`Ò뙲I*FakJ]*kRdjgu.C-xnϩFz>Qtלs.n3>H{{=P#shgqmh%4&g.m1S8;-HkcOl nfxnB=Ӫ8Ld endstream endobj 295 0 obj << /Length1 1642 /Length2 3920 /Length3 0 /Length 4944 /Filter /FlateDecode >> stream xڍt 4{ܷ-1,I)d23{.)13fƞ%iȒP5nQ,eW5P^9s朙|XA]QxI*QmlTDQQ۠Io1H"@Dq{ (8.Ӈvx`TY _x:'<`ǡ q=7v BHP55]w@"p`'y88GQ$ʿBHizH`<Kܵe_4FQ2 4 X_Ƀ4<ލ 'FpD"yc3el@=*;o@hܮ3c8 1(LG8!Cd8wV upz#"hoQ CbQ8S>BNڬ 8NHoX ]G"0PކUBwހ T Ed@"*@P(D#H+.F~#=(:x~fge~uNW9EԀ~J__iHesHCq' ADu{'Fd쪥vEc( !o_'kB}3c#hC!$.1~/CY7B E7t>PwFܹ/2'N# e0It]'v;]SB0jTn{"va`@(=n'DC%e:32&=AM 趻'N^H/ 5p@pv/B~,2_@UU)N+-}`,F\~9DP,#zOMW?i~||ԥzf /ZqEʕiȴq\%:,b–jZHFbZ5S:zZ.u78X6^@cg+YQkyy<(PAcUaT8ǻ ^.+j,b#SOdtd 씘nl(1کDu'8AyR|cB[B0mjڗ0oRraNgBqq%Vhmc)C& l3LR0)1Vtwth7$n;?,4؉Y}vzR8:8pO+- tQ?65fzEr8o=Iav[kSn9f {f^dfܴ~b6Hya)֧?Y͑R]1|AP_ -.<(鳶rd<+\sjg/Eg7+s..5\ 9f=u{/V&l^ffj`+z2Q^Y-f_wNUgK=2NcqYK2q ݴwe;7O6voL%eW*[Rj. {ׯ@tWQ,Vcb7/3DuQf۸P 0^)[%>-X{V|K X.:Oӻ*vƼ{lz9lb7:[:̭6=QYw܇/% bDRo9zԢU_2;1++W̹sEGH4V Fo[hlL*f.n3@],;08zpjHװJp4 ܦe[f{Aϛ.A"愾}m&( vR1ՕR\$OZXr#eOr~5,%Sί6uhz F "uNC,t+!| ԉkFHQl]>0-.%aW b ,Kb$o>)/461l%E-. (OM`W:VD#QGoMH9, 6?'{=]|$Z!G!|j!=IWk ;_EH/H ꋐkyh+4]6Nߣ[/9IIP7o%2Fı׀MS9=:u%[ͳ"F٥]CyjT{hj3"O4k5̚ i ]s4lY˒'8hZ͆UӑF֜nC%3}/P!RSoBr=O2Z}9qZgZ;_v2(HU=~m1\>N65/d¦sU1̰' 4Cbh¹!r.﫼B6Z݊uMHJU>}5Y쮒{6F`s'$K J?5XM1?g5*Ue9/++dc(E(զB._<_=q/vwһhgf8K ~. ?`55v6! އ 2ޚwe1N):t;qv\ԢJ+kVT]sb* |u*T(y۩_2`cfgNsIώFe4CZeH Cv@]F ?}&*39D&rIC--g ǃ%D;FyMQ'{|`+0|/?|Xe3Ǹ4gۼϰdQۨts@K4eRףUdKz*Xc{+MO\M/J >ecX *J<(*yQ"9jOzLgQ7+3JO' evPLOlUaFjmv=6zM;8VlR6d122 {QElT(4J})2zc2p5rG \Q}ǟOXkXi*=:Rn!!,q84$.z*&eŕE݃G5I]W 2$%28=SwD0HR^SBykmc/깒b=/gIz . dwX"toV\*y#b=X$(2X${Wm=@`iS 9NxxcbVּ\ bI%瀇u|DNNNt;?~z>28" Pf8ueZH(TL$˺:);P·?V7v9(!Y6$辧rK*ܣOBnDj JKzDqI%}ϑIŮe_hnh|Zݾx6 ңTO٘l oLfɦc)Jftʹ$/Ι:n!xkCG^ <.J;_+&ή~/zu֓yGn|` wU S3uƺ3[y;?OfEGo2.A hޢOrղT6}fr4[ So'So~% s^g/ɶ9V9y^b81}|}De\x_kBuIƇ-C7\~ר",v^]ZISZ_\HfUg%2ԄSM޷(ͫԖq}̉r.gCHaOMu ?l^oeg ? |rqdG|y!v䑥`61p3_Ea~E+b]#+E4b>@ຟ1~R5}GiNIsK#37 Ssx 6+̎8%%30ne |^o|s=:'19j)54=8jsGZߣcjd|LSW5 Z*Z endstream endobj 297 0 obj << /Length1 721 /Length2 6909 /Length3 0 /Length 7498 /Filter /FlateDecode >> stream xmwuTk5R5t 30 2tJ ]t4 HJIy}}׻ַ~>g?g=k8X5'TD@DPX`a/",##PCAAh,  " !!=Q0W4ۉo`A`0 G>9>}G( ]g P3Zih5 P>`8 s"<g$ '8!_x EsF!=*u5!S5iiGeN("U? sBPH/ߴH?0Ax@}pACQ}$B Ŀ, C s@0o X oϽC!0s$?G􍍁f|-euCL  ? >lZ῾#e"Dbqq hBA 3˿ߋAfNrn!E|㣎f|"s#G6^WS|_0I(Jy85nᲘ%jڨ6Ϝ(ݭ*Us,k'_y5?u̴M{G>tFrAZX5TIfuYx*h6h'gg~ʧd(MK~ 2@4KZ*,bfIvjA:7"I쮿eW3}ݔ0`o~ϔiRm.*2ua-ɗ!FYicD'jz>+dDBKx|'V6_x_w'ȽiB&Jw'M* {b#"߼p7)T)M¹hkXw6=Y,* ׷]ٌq or>+'~\"&3P"><_{3z `<,G/oM >+f4h,h3Ʈ V=6dEMo1dnhe>/ȍrf SN`f]ȃ)%IFڪڕEi,n]t!T>sffVx]ͭ](pxu8^\Efa }0iOO nMl: 9]%iL #ǥdOxԓ4Vu|K* eOtn>ʿ1ډ6fWqiڄ︯OBٛn0?tZUc7$GdXP*=kDɠyBe/r-r8wlt9*[ /{#NI53~rݡ0&xͮ >،}*6qDg%ҿG@j3KC 'eԩ 6짹3 '0wτ-}0|KH)'QAɸ nGCK=vrȐ޷?6j `#i9Iݝ“0u ^iV)g=qAp-`j*ǔAoS5ѝۆ>F:!jkTOTwq7OS7KD]a =Hh"xS#%o~+#+R:иa T<.l3_|V{{4.9jV Q^C)}RWG͖ P$a6]mM_42TUjj͆m~KNT]16RR q->hlsFcs~ ~OAɳ<z*}oLsGKa[@h;U1o9Uxqeb~gf/^$@:W=CZ J";K 8 EAgzE.M/1!ݑmН=<2+gեrPɛQh4c|& Ͼ'|aׇeޤ/ZEԌYk>!wn?Zʡ9l e/2@g;?z2$铵ЦO4~C.iJؔrIkRDP4*PWw+TO8!CՓ$S&O,o]ULUh2v͐N9Ռs&вĭMhc&WwڌRlu'~p晻 1g2p˒>(+4v$ pie`"!\3okWɥUT|NS?j K&?Rf ߠIeS[b[}{\w_SG'!Q31~XWΪwqjV cOtg[}i*`Aw9nd!.b :pr3oX!S1Qyez1H1;ۗ3>NN+ᭆld 6Ufi YB3VMZⷀga%ڵwL^O88 xP̷w-7;kKj},cv&ub:qD{qӦ95"  \YH${#)s`AXKn6Kݝ;c804rdYA74MAѡQ]$AJ'ݸ!􄕝M[KXeI͉tE"Tr}~is :u<1x=CmVyn25:A7|%55@x=dǍH>`ϱvBA}csoTur>KmY0s0G\ K-o9evVb*>䢻pKrZAf,LF ݄IՖ4;S)!Q޼񣮍@X=ah>c`"](umX^A"1Y2%L@ z߯wMK'ԎP&+b QLK /pb1Kk^1aaO145gZS瞍Q:Lc7slT6 Ҁ,1k3;KY6PvŷJY,L] D^\}K*̍bWQp [GCYgm9U2sd% FO;P/w wo"6{^Bgʨ$e%XP<֦mx4;5 ɱJռHg?:S0k.O=Œ7&I} +1{]o}yHwwK: wlyzMtg؏jx6[݆)Qƾ5-JzVansf8Gfϥaos/Q=e}ւc1T1˨ ߏ1`hWg@FLuyn %T]|,J9? -fZY0$atӫMG7<MNX2 +t0jАUU@5%)r`%6.tY29=E/wlaE ӤY&(Zuj>Y"l_я 1b}Tϓ)Ks,И nUoDnJTl~H 7z2UaӬm'a^kn~Yz?#4n.E/zMGR^Od,JJZΊ؉C-ا H5wk?\sutVrlm ;gפj 8߅}@9 (]jG2Ucًq|*1YݾfdE5läkFZ{1mDɝWjs3Ud4f5rv_JJi ď/<7ewt$|x >n{Ł#٥ 2?Z_iy\q^(P'6Х{+a8sY|:0Lx@ p}l^4)dh>`6A<3]oVŊ}%+ӟ=y[0 ." 3M-IY)^߫G{|+q"IbYLpp @Z-^: %4d L߉mcדm*}r<KwZ*_{f=uF\e&G'WfE ;R(nkK=$J0}]BuU~ ἅuֵiU;r .COvIM=*GE+ xOW-n"~_{z ?7 :Oԍ>~ZMMف9H~+yo* ƒ0n;)o.B춬u^# 8P˶8':wDO*3~6U'gs)>hN.{4|~Nc0FVhՎh&NB MٻȚl.cg+U1C,44#'`Lk)u*T/MFeIu:i8HQV$ 'ށOI@eBEwK2G?Z}N!V5W{ٟrf(Cm%ɧ Q v o%5akeO(kR![{Ma`s4s~L鲲>YQmyq3F6˒>v?eoJ]kfdU5  `7&b]rBYOm_Kv_Y}~7fŖ'‘Y S69v2~hu"^nRSm]7ٔ|޵ *Օ?ڱyg&mb|u_&> ӣfDt6rW\{t9Iܐt̺u_Uo nbVsnG թ9 C0]_ !<=ۼ a:q1aa7 T{Ү(kF3 2J,B*Kn> 3䑆Z-ZSGFJS endstream endobj 299 0 obj << /Length1 737 /Length2 13137 /Length3 0 /Length 13718 /Filter /FlateDecode >> stream xmysgn6~i۶mL۶m۶mO۶m[ӸssukuLBeچK4De&r<&'&]c3<.{0]0gBYRNJd?m"NH?E8,'fM?J%3iya7[֠J5& %m, kjPBC)d-]?w_-aw1aZ-qG fϝ"qظRSc6uf)A $Ya{pmsa3𕱲(^|m\C~-rݛ&e D.Om@ҼO[>2WGHQp%/=F$P_Yc GIf}xl}Q;=\[!z?ߒUrCwZ뗻St14<~;*S,> \'H:;ND1OL1ЎRvJOon69IP>h!M0@śd #SPb|1)b#0qP?݌JZz{&2ʐ"-rjg#&tQAf!Kh@ȚjbOpgNh;?°y~Z{vJ1nIȡzrKte]'@_Am~0{Ƅ̪++; ೎ْFh g߻g\ue4q0-l2H^QjSڊ@d0Z4@ce$Nh'T #OvqG*hFD%OU)f?u%U,@="l_=UtUP-tU)[땴=.3wB@:t:ufgpBpC.5 Y”D!ݓ Vǃy7iaԥZd~S}rBȼDh@|J)vn$Z˙h![,NvQ~&!dQqWNr1nBf?eDAqB EFj|^mٶvԥy^јA\R"CprgDfg&ZuqYqn 5r&n2rCͫVyL)U:{}|Kô8?^ħ DQ*(61dzo!q_=tXR'|bL -;dQhd0SʪiZO6.F]>sc&wBCciZI<iLu E|I IFy QidCKy25?צ:7|KR 5Mx{~HFɶs༧S9zz^y⣽Ժҽ8ela6ʃZnJl'/ 1'@IKǩ@LL-IWx6!uOp"ǝF䊯A3ʓt%9Y3 N.Ѳ+?;F<%1};XAGZa2' O"s1v~oN9Mѯ+S &.^|AkBn "K'%1d{a3(s͑cqhb,'Xl@2k?Xz %ΰ\c|4ś'/4ZDL40*DFyifoÒƌ9~%<`1)|mˀa֒N~JhѽHY#B -xǔXeT@\3^ {K/ X{b(yK8Iۗ5k@"6U gJVS]fA%(R$cR$!ѐULB#i۝|-6 [xL}y;uzK8Rlt{T_5ethv557K.Rɳ@P6#'QpJxhJ G_LjEZQv ;gsGF'U߸*UTƁPcHy1>zNH(׬oڵzn)u&QG@ T>\`[as{|BJq/bBK'ƥ*`_j/blvY2%}35K[joR% l CmNe2&^PkEQc!#5%(S?# #@$EV;gE`\lYԥCmhR~`K!ĄE b){Y4(k^C\ѡ b&t߱m"i )Zz ^k옐W+㪥iHE7& o~Gg?)D) %ȊgF ɘ zlTц&8Fj1qfVzhoiPgdQ3TI z1H3kȏs*o),PxBbwv_8`@!1mR4g)U6w VfIvnՓOxFeab^zޚv".&}#_Q|0<2*1!z-k#nG睨`b6CAqz9qfiJb5>R`_$clS#%F%u.S~v_GS,=4GA=a8/Yɵ#e9a.OXNpJ|Y9hnV=&2=ejcE?*KE9l7ȟ qJ:#ĦDrmF6%wwDkY5&oZb ]3PYmXztQT oZPn [cE0;pY`0[d ?aXۮ"jtI'TL@I nFP(˹^Y=^CªӶivGE >ur5;M#)ʽ=SZ3[<RhyctoXl X-!-ᥢzіcIB; 8cNnNk>rT +SfX@{6ͭTtC,OhP=$=߷_OI̩A\*=YurHFV`84{lI{)^:`ږ,:ʳ>x=\v bMpPGJtjLK`I='[vU>2W:5Lip^1d/UJN Ab_H\hCm> i^)K!O5 TkFKjr'i?}&}}2-*ztQNq V$<6+{gnA>꼫XY2藜qa* L[bj@b$O_diEZl7lꔎ?|;e _]ggJ*_po񒝯1^esVh:FQzzR8z) *waCm/ʗpBګ)'qt2>y-HJ%uEHX|픫Om%5ܗ| 5sdkMz wĥr`2l҈,jK @.Ïu4Og'Y*jnmvɫӶJp  @ϼիU0Uvof] dlFrEm&_1Yٔ`Wyks\(:TItt|O7}tu@ \p=+w2ݯ xp]pDAxs?x7]tr>"FfF՞\$ 'M4jѕz&G4KZ ҖqY;Eb1W٪{fqKVn7`O?KӢUwjFft _e[f?Doxxv1b.0{oICmpt@D஧Bc U ~8FT-Ϣ DJ{w0g]~JՊjR,O JH Ė]&:6'y=χ,AcHjA_b1hFiFLO6 /p^E1ZLrڐwR&obt„ʮի_)Ha۸hT=5X6٩{h90DH vgrI()^AOXǔSWRUt:vHK^u߻DGiL\6Gy3x'K7N4֝ pf2l* `x0k^J8)E >U#:C+6NXƞoZAGTD{qH LPnjȄw9Ÿ57$|h |%S/c}ΞW[%OǝX6#.-w{FRdHISۆ ~׭Qx&RD+p[Sd3_ˣٽ\(~Z5Uq"\ ԙuM_u?r[$IH뤮Z K1+M|;n!` ~Y\K󹐷}SXq㵞ݷʍtF}ȋāmOsnO7 T[inGq,yk}} %jG2b>.yoA}X nE7ۨP0UN%f%3+O8|N RgM?D "<"F7 lH~`P"is7ʭ:IZ3{>C eȇ{Ch/fs]]QDtX_D)`Z?Rb`j}EYNVdVf[f4Uy㼓\nVDx5zb\F HETY7%B-=63Ǚ[ay<ڿE&b1`U10᥺+J@*CeUᏢPK:"|\)tj'ӑ º ~rҵ5o~ xw2{Z_q aj%υ)uAf+rDNjx7ӫQD9bWB=\4i}PnOf];-^P/?[y5 B,53fq3!d|64D˲-`QOiJ2|WsI'N)mq΁:|#2f>&g6ʦv(ߢ\[h! 2_i5[yy Ne뫽y/b8P #m,+ g!uhw&azz~rej_mODK.V OxL_qāBozCqpOT' E r&kB<$# ƣQ+;#QLV_cx6Aq^ @RR ߱lUWD/uctsnPt#kkzTgX) ryZȠI{OU@,C k]c+UJD>B9edEG$ puTmB;vđ8OGZ/vG¡{KԖ렶GRA-6i-xՈY{oqre3H5zbOT~ ] !Gf=€ИBG*eօٴ+ OGl{ly󎇝2BP/)W 0iEb5-Ŏ:yϝe2O25/54KWp9M&!7 jP~p ow6~T Au6y#17x:SU^P HOO/EO4/i[_2flHB_b $2P{S^(p[3FG8nkz~<Ϋ^pnbru1HE;`60&zO 3#`I83]ͫѻXa1]=OFerF.zb{dNierWzX2$ Tjwgτ3"M w̃KSޠ!=ST[MʐދY+Ӑ կ[ MQs!>ѯ,kÉ .fWh28av-Ԡ.[QO* ;oCW7iF eOy .G15jG1fbڸ5"KW^{{N-) zj! !o}7ƥƽ;$>`7ۅ5/Gy.ʡbtwX(^dAÌp1,:d/2MQk(bXOCtȥs2XLDT.V1sM~(9'`mb u3aU߹$asZF_lqXtTkx0ӱrr&.hkh1ؓ@-+r-#%y_UN| \mZNrr6nA-֧Pa_ɧՄ;gkVY<*zZ~Mk G6 #Nz㲅'2gU~Rf?`W34Ak,$f0v8=kpv *rH?֑(XaV>(254fQuZn=Wu]"*1_]mͱ:dR: G_MЅ/X{ _u-niC"Ѱ}[Ҥ>€iy裆Ke: ZaN:Q*Qy_+OufNrI,[@<v;יNudC #zo#Pq|D6[giޖ]':&c9 2o0B01at3&O3(t*Y~ @z9:R'}83ݩYj\f]3ƁS%r RŻO!F9 FׯF+hՏOHlr"!JSO$>p~(?)ȽbXIy^ DdS[AI;0Y{ yY06פ>Cd􀅴lJluE(m&qO,)ҷ}`bՂ_K[b!4m?cיb}yS2hBEȧlv1eE0+7xI?-V[8^1³:(RjjW9rjdBMAUw!]ylBzQ_IlYtP~.%6/UQ?yw!BlCˁEqQRIDCQ+DBFu*Ontߛf Kٵ#2tC穚m7qJCdse2CZE^L, rtm,k'&&pē"4 }"1Ӫ?u"G >  ʓ.w3rQH.s۽x$M 6M'!UYC湃I`dm.fC ЛSd8#ϥيNd;PD_ s E"Ḅ\P+gxq'V.&B\V^GL{D̈́$H;fJ JZo.vgFubeY5cF7sBp Sx4]2I"If9 Pɻ ppX:K ]?Y/OfE P8eC/G: ԩ;*!zKF5EZLğlErl.ߚ(5 q"% {:҅R@uqN3T[f۵$Ө!ۅ{ľSʃ9DQqs'~WƀH&j}.Ie9JU+<E/Xh]y-b1-:,HeG A7("},Dw$3s(L~ ?ЊkP~Uf)6~MPiH #mL<,sRQS`msTTUAM=Bhb⌰EBN=EQXtH6m*LZ`Hɯ;p& i/`UF,,itiV* y*մMCdxy`"iwze[٠ŵR r$~^di|, ܥ`~k2]f)O48#ֿ*OhgShMAiQP% Kn9d\ V:ZvJT͞G;亅IV {. {J)gP_&sFT1%%xa+bM 7# VBdUWyO3 %hO[Mu\ (dMˊy׏9Aٯ6 Jn7ZvV.,1(>Q~w1-]h!Ok:cQwIvҧ4@&a3YS-&9o]Xgrf;{"YnvAxQw˹ԍȨ=xXG|L.r\<ם?Da^VeɟfA{Q)˻ki<vz<2|V:'++UǢyj3*ئ3]f9qtK>w U.@&G9&'C~Æg)\3DGBj/e9]a1X~PxwPkJ)[Ҕ[h.o"$[l^~VBvgY0kX);Ga zN19Z8JQjTw^ZTڥ`#YͰ5<8H0]1, sg}8 Gq|4rzbc Aף3l-g81t- SIq]^.ΈXϣCGKKhҭG͹5\vo C`vt s-+5^$3 LˑQq:*1klyZf5!LwbgUڀ:$V-tyT"Іf%w)9{LxDAbƣjʷ֐7z=LI/ex7Gf de@YP)*O ( _Z},i3+خDlM`҆(Bݪ;.Acu&ryd_ƊEuPJ{ '&S۩xG(sй.>wm$ߣm吗W? (v>IP5I-Klb%d)r)RhJE7Hʑ}X?<~ ,k8TTgpOk/Vnt=HMs-09|7} s^$n58T$QL.?v_ d2MΪ DІTM9J';"ޯMW#hu/pU߷6QU%R6ǁ^8h]:ѸpiDQ:VNr_XH(V?",0}݆pK$wQ6>:iW`'TuNl+ݻh((b'Ӽ a\E\0O(HXܐ E.Gڢ,>:ˊ) CMԤcrJɅ+XSOycҐ5/D8 d{끧2kvHɤwaaoldݹj7Hv+LӋoshS%d<dbUd5ݷ#\ G#F0zC\z^u,jYe(qQJqѽ@mEM(ߎvR17lԲ;K; pu]gR ڲ,?Ղmc!#9\QnIY rĒ~, zK4'g,p+k d^w4A ljpьw<i[ endstream endobj 301 0 obj << /Length1 725 /Length2 16161 /Length3 0 /Length 16663 /Filter /FlateDecode >> stream xlc.]-\ze۶m۶mۮe۶˶97g2XcȘ3V&)#-#@YLE@ CF&djbio'bbP75(:L 0da{O'Ks 1տ5CK[K'Mٕɕ$eSS) ,))'Sڙ:\l,2ƦvΦT3{'09?̜m*br*Qaza @Ft;gM]\_l W2_N`&.#SsK;&igf`\r&fDښ(m\]L&NvqdB m-m<O?dڙCLb& .jF?pϩxʿףQ"[N&k[??XW5tqh3D=iXlFfVF35vur2s71ƪ1wUzkXh|8**#L6尅5΃ N;\ɇbxSUR*s; z7`jضr`.A ,yyc *:v֗ĩt)P~Lhj-Bn7@ nɰ-*µ 5%0Evwݪㆷ!2Wt G!oywe syTwyY|#^fu(\f)twEa`l6W\d'9&Q+-O1ۣo΋>ym )e@l]ځmڝAK%U2=1['",ݚκpv8R [2g5 y &\5_Ү#K\TEzW<2ҷJ5< UxKʠzS!O,>8c;Oz^W/MrBFN*A81u_oݭ2̽췸ڪDP0 !e 3-GK^eGqsGx^䀍^R\D K$}u󾃬?FDsuVw(BVŏbqz6+?1w~*eM^n@wתJ.ޖD:cqtzgz -U<8#)-{íAi\y-!wY}ɖX7nkK Fvg(KI N94ġBFhvvyRC8EWW2?c}aagQxb]c~E990RFD4>:+=(s qwtUm[<8"\cX`FyCrPܪsmgSiTB'vk?q';-4^ܑ&l dr1CwDwPڋ.hutJ9Ro,eE Em\9͕Z%W OIo=2=Qg9'>cn G `1L7~&96zv3CCHl ȊFg-N"}РQDU*eԢB~Jmp!%+NIiAnWO%iwI0[9^<91N/ʏ,[<,gScjEj=Z9]= Ͳcsg呇Vz 9ۋoضUK(j0p0%$9uyV |ė֙2P)M:bswmc=N̩@^t{#2FF,8$Y;(>.A>I#ūN9_L}T(qGMhѧYu۷k^م|:u,RNoXXgQdt8|cAt${ A]c -(*n&@rwaP[O+o2\7:^uaBߘR2ͭt ܪ 5ߚ#S?j7L$IK3;SAsaɃ!fES%p3iid6aKu0U˙Yg*.MR?g&O'2sʻ!A]icԸ!Ʊ${r:\i_@torڏ&cf"쑫~5']>oF(G #C+_o&װ-9n ]LͫJ^]:$4{+]^$ +ug!guCK6I3(hցAzk~jp{G*TvJ@olR'תyN&x41q@L8 4\ڠ}C$`agY$ p{lr>֫-ҩbPL;&,^Up$cu K0JMȓig4ÚoR W?hY/[Ь&UOxOkh!=P7GeûQt.>ԕgd!P\ -@?' OP_v@HH:eY,P+{P?aM|}P+jo e[ BW3f!83Ecs^ʊ,RMr?%ˠiQw'X7zwMStBufNH6G[.(fVAng*~afɦ !ƨ;EuKoUH BCp,eZoy DODeAcCCf&T= @L>`';ͩ^7n45߹&.gt@[O ق&(DSDIP*:LB}eJܕdƯ*Hehq՚[pPe(=hejP'/ [XR@0'd}>,-BΉ{p3_tc.L[=ڣx!q :U >mx&܂EC)tk2U[-zaZ(k2nT 4^w%3K3̉{4!kjJ"nۦp2qo`k/?zH.T"*=2c4q&x2SOCb^Bq$t&ʃAZ̻N_,V/ty4~>2L+/{sRJ&/MK%/۳GBfKq)*XϪkGK8][LY/W~M>T^1gޟ!ø s$Ï22g"v|˔H 瘡܂YB$\ZXAs× pec(D g"Rmg۵J3 8+{KԒ~ O^FǓ::%*{bJw܂!.)O2~k{14f܋qy\'Zj*N:jnNelZ&VdC)tRޚh{fNLjܷ/B&a68={UXY q@F\ys\qa]sޞWihvP?9r@8K#=s?U3a3uA4<+dډB>'c8XTOPŀ14"c캱o kG@,K/t[*, W b͏KkvL-%DHqRe[]&sQr> thO&)U޸Fnsm4#GT.Ljkܑ/w%&"]#:F~$ o1 Uٓ_'`- AJl}~V|x.8슴vh/@Lq{E\V|HA[tsMf%0e65VxW P ^]g3!3źt r;NNjNFV[`Q Z,o1n0b>a?PtRձ%H坫}] ϫH.(9&o@K Sj<_$q_g!sI8nⅣRcf2+DT @*O"ѿFo!p6ST^”J:Ϙ4M88 ~M9|<1A F'h&r\S#K #޸jz^cY9ҝ,|=OB^0T!eq_"S4]ίSNdk8 !EBth㯎 ۪?0Gד-1t`,x្d;<$?65l\k<ۂ.c,L¿_?˱eӼSk/Rzs@Ҥ*H{u^2Е=m\Noµ--$R}ǒYxNHdRrlЃ]uaе!8&MQ,[ߜ3/}3)M65H"RvE$71IΟ6;7u][H} z!Mփ;H]_WQ@+OrjPU 1Re\Èe]qTдϟ*8WkaoM|DsDE8,{SPq=+:ÅmĚ~ö'ttMh,@_~ud[p *Ga3wP887;S޿FR`> LF헣正e!=.e_ yVRdxoqV}7P4P^vmt!ƥsMQL.6rYb[9^=xǪmeAqJP@CcXI`VqMv1΁;KZ52a$U[9G׆qN`F^䎥Th?:;n<9Ļ a& j$!d2jԇaZ,G)EL c kpIb(&{2":$<Ņ/ `r&_Q-l|tu{hf۪=.|pԶ*|U.# 0u臜bI>9G@'2;xˢd2z|*QabSUgM^Ò{Tp]1@AުiuXpٟ'?M-lwK!+gB1?LcJ,hƙ+B#^^.Iv]LO֟|Wa]}_H 7㖲5܏XV^P^ C{xt'ܳYb] m-Zrn7c]{Dj`O/X/~[-m'.s Gl]z(SriЮA̚Ź21\,fg~ⶤxb~6N*PY0'uU%|O QpϘ`=3h'Gj9ރ#6&H^Rݘ]t> æb`6  'nYL^55ӈQ:]ҍ֢L=r2,mf\ҷOړ,Ncyb"CHnpԛpqnaoUrsK+,-R Z-gohG=Bv!-ߔ/FZ>yo:ird,mO]Q&ri?1I bRI\Iłx5Ʒ)n.6j}%&4s6Bf'~UoCyLtR9lՠQ 12^˸,߈g SbJcv/)w 7pmA÷f&A.Ye#.'0&MBа,ƑkIne_Bˠy%W^q7 |L%*{meu RERxIfLsû块e[VxޗTOtk RtuY ATBj18O^S"9L__[)jYbM}V˹`W}X-f{aϺ\jͶbْnjϬTӚ|6o|cO%x!|ǹR$[tH*_~@e*"`;I KT>B`5IwlRz7dRDM8ږ17]fA!AĄ#NEH C#F/f`t ^>?ɓ\N"v x."r]U6vG;ԘmbaMY0(Nks9iE;^I(y)[ % q줦 e\yT]{xҊz]ن=_yB~܄e%Wj#$;"ߋs-jӽ@lLbl挵8h e?{_I |s^x/4rf;vEO_|_P]MH'3ZT@0K3';KyBNWtwC<;HXih/A)yc: gBT_&/#jxJMEw/F(h Rf#yYIrZvV^*+PivLǣIx y= ,r[Co3M#&F-}T*KM^45QjRЌE<;O'r[FpO{؄qfIHPDV&ErwQ<s#3cBuz9=s-7D~Q!V%m%s=N]4h52zxOĔ)S jK_8rFqZ_t[-%F݉dy˝>1 лUƷav$zjoĺn$"1h}95 #R]<32"%c#׵P~>4+k^-WY(gjNB%^oZ+?'鳯AB@t`cz.4;,>TT=x|;nl g$lY/1e{=xr_İ%9<}&%{lre1<7i4ʎUďs]Y.6\zD8̄ yn:'!͖EGѻX5:El.'KJ1j"Kc.a[uMk,G Yb^b7Gm8Ub f 9Ԏ|; w<~$ [V%ȑ~hnQ.A $yݱjeMkM?/xۻH~8кH,V808~>:A]R)78WNWBh4r7X }AM?:Ug-3vb@zv5XDPT'|K{kZIlGr&v1K⅞%!pVq3(xT[gu~G! <̨ys6uF2$ ȗk!3fpjUE_vTPԊ>~AW> ā;돉c[ǹr>1%lc:k dN@B8NpT@eq'x%sfw-G#P'q!ZfA  :d9w)K_s!-++,2{s3 Ԇ8lm=+}B>{ZoV`DKA#L9&%[V/5muC@-&]%%bgc1Yfc?ي+,)3(e7}.ʳqQN{kr}j.6GլҏGݟuDŽr!'S ٯqx,q͂=)ioyA<اxۂC]aU+˖}HJ&Ø\4u_w߿\v0uiwZ0zm85u\l2mَiđ58ȩ9R{ySTm+Z^9Ow򴥉2f+һb]obͦ>%] 2R5X3%z󙮴0)^\M]@S3=,Cro3tá٘ߐA3t<ȁh")gxB0~Or:,R*bD{srF͵ڍ&[I ,P\HWե֝]x/G} Zm|j r"'rQbⲄTA̜hq1OeYr^5Vط#Gd.tk׸tw">,Z,9'#d, cddGVOYJ˅Ey٣ptK 5m3}C-#Mi)EK³{ L,PӶI =D- ``Xx6>!LF]YQ23<`l ga:e`}3+o"}/FtR6vZ 8WGY:S6-07,%Ke2au?,V؞:i\K{Np&awN}sG$][8*8#yif\ji>WN/_g?ҁ3<aio?XMİDrc)@ zl}Ob؎ [؂SцͷN)=%h$]m=a,M]DK*E:! [yMKԸFd$F\ 1 0aYu6߁"W+zs &ۃUᴍ&5zٯKcuq+AuͩdDJ#A<:6'ZW 8705gnHN>4x[ yN-_d Gk&Q.|[K$l${"*5!qSNKOeKk׭1>cll!2 d398)-e-9x[Yz5(@_ɜL} 7Q`syl-wJw 6"/hGA/@Òάo=4Wt c?~;}ت뤍=3EAlq%~ ˡ2hA:S=$9d\`>\IUf}X(ŵA13eA0%Kcu5]Q}\{ث6ș1 WkXKjm__ޡ$fkD?m 7e. >`.}U8Fai!apww8h’ާK sRyXlu%fr~!.U-qIr] ro񻮊 #MX,1^  ʺg45WcFQ-JXܐ7z Fᚢ ƁzlV=x҄X/[!Skrw~N]8UDCcg\kr"z)[Ml{M]%iTxFL@r괛j5 W֫{y c[=g#m %;ۥWsF-T(t\Ae/A<s$QO IGQQ'H+Ri8aM]>):wvVE#GKڎ&&dH@V{"qù@Cw ;N"1= Dm֮{kavzY ~JDlCiK* ?ـ" }%Yto=$ ^o]7U9|2oZƒ >˚_X))ˠ h0$P}:/7w-!i/IbTV!)?@DLlrنb@G<CSU v(FbQ tmPGE^'?/fރy+?^+Q*zw]4h-~t+9ݮ[ zpn3j"5Y(S,kvmu9#X ä9À \#HYd5HDbԿԣhL`y"*iH34e)<Δ Zn(}?E;7_U{w]>[-Μ~c~)Lz>3> 6?/P}pMv\ hu,'%Be_$nJ' 'mer 5:FH@fOIhYHy)lM\-$LCi0:=s`+4ӈCz%v΀oJLMn:rpkP,}~͸eeWPv5c{D&[7硼fs刀~q~c}}*y-7-jv8⢜LyOvUKF+h>wyShQPeP}m?ҟ\AIAv[B$=#Cfׅ\gH{=:&Fӄ?X[_L8RU."`kF#'Da&[|U 4ץkdM}AM 4+"%[j;c;5 jQXlS(nfwZցgw aYL6ZU̢Upܱ/Ęc}b&Dqy{ 粖?m7?ඹe^ҿ9D(.j竼T9o6-,}H2SL((eMU+qQ6TGp4CPEp MA!YAEW#:PMg ] :OCnV:W=L ~9DnSt4hVU/& pE?˝i4#[K j=4> endobj 261 0 obj << /Type /ObjStm /N 86 /First 785 /Length 3748 /Filter /FlateDecode >> stream x[Ys7~)UJle[@Q#i6#ίn3 ghQC"l4hȊ00" qi8cSg5|…BKHq[`^aLH PZx'(e"1NQSD+!h1Lʑ`(`ebdZ~Hƈ5`/Ev1-<) WA,80x0;9"h`x%$bCV~>o`w{oP;|aÚǙef!MzDUL3 cF[q%P @"%4y)29/!0f.` \5#"  d6$b!c270y#`{p \k!h%}B>Y1`!% ϥnqi%jW;@b: 3 0t(7-q^y4c0cM#D8X7ƈY !OL'`&X:kIR;;D{2Ɲƒ(@r 2Yr4gr`s)Pu$ЅF=x\\) F;pQ`cgI(c&FV@AslWҌT!ĸc:obVYwk;p_o(p) 43&W|#r$ q-pFMD.#X+ѯs>xzADŽ{j=E&6 'Yq4گ̙`\mO 2'@ΙWsanq X mMdžG;U;]؇^a| 9 1CnY`W$bQ"FdBZ0o'd$IO@>2\03 c [C.D4f%-sxpKMlm zv.5 \=+iDWY9ݛ QU\~!#)mcAdyTTݣ#O'9}E_z2Yғd{Q͊/Q"bxy9A-=+lq|YLbI/bNK;YZK:r^]xE&bYvU,)]EU蒮 W7MVŲ(h^i\M˂~o;U,#OJLXI9ˍΣ`C > W`&du,bP,geOL?*0;u~ߦ%@)ҳ:Y¼S1Ū*f-FFcߎP]Bbޣj1ja|PPcPB8pNs4@!p@bwwFHl?$ڥ~'HM ,}rׇ__~r-hl6wư^@Hc,__=%k/ZaMXZ!nT<~|ip᧡"my7ViwwU2Es^3}[n6߮Ymw*>x |wokܡv|%9e̸Img@]5⓷>{lrtm,҄,řp2W%nh{S}sxG)]#v{$)쬀j6-V7s)/xE'UD8!'W~i0) DU. /\iu =mt]B^fOϮzUZsVoϼzǺny3bwXWp޷7lw7SWF)36~5`nտ{޶L-~#EϲE=oWAљi7Ge[htI]onq7;^k d~c\UwihۄM"߰hcZ$nl7XAvxߗbF/ScWJ/ çӦOW/Q5]%GB)t!B&JO-~$@-2 a=\WrCHJ IeD*IOG2|w]_k0L~Y;%ŷx#F7b1,k ԝLɊbԦkzĝggm件'ɷ@ncW9޶omx=bwbn j>ktfbt F5*+&R5@*% "ӎ~m\~]e{=nV=pnɆň>kD0bEwn;ƶvrF[ut5h6.{ \ o̱=Xk}%b[ȵ 6Bs 52[bਚ,Q=;[,9<>o{ϫ2b? :#H$PF:zk/]]\5Z$  .%ur ] /Length 753 /Filter /FlateDecode >> stream xOTQyð3ðȾ/""+ ;(IlvVjb|s~s}B'QQP91Z&'H[2d@ FD6D6B ZӶ6iavA6C6D fiA9^GBvdVОY0ifHOWл饵Z&V+iny Ad s`,E Xk`lMnp{B`hMvڀ|K} Mp@ ^@e,qH]*B uǺt;O() z50e]EeJRKMuRlaIBYea@3fz>I44]qq11GLDqqç,<2r,q,q eW`}[xKCǑƑƑƑw)!!Tb9"P_(EXx[זZGUYHUl"*iSU/RfoTU}{Ң*ZUU[蓪UUkGUiDZz@/`,%Ie ,ޚ[?\ endstream endobj startxref 209788 %%EOF tinytest/inst/doc/tinytest_examples.pdf0000644000176200001440000042543413543151030020142 0ustar liggesusers%PDF-1.5 % 66 0 obj << /Length 1786 /Filter /FlateDecode >> stream xڵYI6Wȡ20bHQ-E[Ҡmyilٵqmdg6A7m[Hh3nxeȔ*5y.T*/ʨԕ:GۉZ1It04Ʃ4[oঢ়ūLGyJD2UE\i-4~m&iWZw<wۿ@s7I*(Ike7af ˸8fhk?5c^6*jCtWA9 %i!Ԣ%蘾QAָ?H+[Et@]} dfQxd~$j)x(|%j]IݶS([d^3{҈g")97W2+ *,߸mdu c,Ј N|Z{4Wz|b9ȳ]_WqBbɞz,.yU|ߗE_vṣ AZ %j%RTV#P 1YPG~Yp*7f8d$M3vT0l X3܅RC>kEwtN&}llgcS+V}z(եe &}t=/i(18D#9wxF* J3@S$kO.,{|SF8LCUJ|=l\+Vro:-&V7r]?("]q];Lud$1xYDܦev =xj^VC3SRY㿼!}R eu8Qλ(9TJ9 oYvs-@ 3:.O$j8ydCgdHr,C0qn50˂#ؘRi[|N's4ӍĖƀˆ~Z4w, "~XKX~!Er7쪙nf^ilo/yԥ-[㼿GYJdcOa@J6eiBt b-]R{x~p%r0ߩ㏃Cei<ٹ V*͊vhmI[/~q$`-'noFqDE|.h踋 Md*Od-"r]Ƿ+bI yVN[q2h9xT[.F8W[sS7<Rqe]U 5QDR8RrM_|sk3)ȶ#ĸ%;P0E(wE_+{HRY:xQƣe{n*51p9nϱy.u\̹MK>(YN"mwcp@Yfz:09kG>:y[lڇm]NģP]6şљ҅R}nFf:h(MH3>ΉP dG9D;G#Ŏ`}ਣ Q uDג+N S?Rza|7 J "yx+Lf.w3?l?Wj' Ɖ;(H*]ڇ.LjC8R&=2`T* !2rX1%=SpNg dVI~}/W~ r endstream endobj 80 0 obj << /Length 1995 /Filter /FlateDecode >> stream xn6=_a`JRb-bMa,oi8nm)㹐dIx#<<<7#F=) EqxTBӃǡXN/F*Q)?΢|t6G秿=?e0Di+'?kg PX GH ?\#TiB(O Sr1kj 4B+9ppGOI3! Z`YY@6+̡*MT4On]} kJ ``||q *? Q~v"i)S@KWK֓TPpH? A~'"Rgz"6 ėEDɛp^Y Mg z;A`GkVl`H$,TJCȀ{ܬNj 2R434;1]/1K6,V-ذIx*Te&lL+2!苘YM$=G*@] K\Z@Tk=׀nFew=H0чl‡TjͶTNr- OjW4¢ԗ;{2vĈ0dJ?Ra qe;H?Oz?k0 ]Ш#?I9<~xÓJ\>toT1+W=~3&\o:ę=+iO/x0Qo3ș$9ieg_?EN]ϒ 71ي(QLDFsnV+b<;6f4,tbUxglz;}ߧ36s2OxHgDlG'{pbeM1L|mWmưVim94ZO#|Uafv+>bsr4an|{rH,|WGlN3H*2[K~.X嚾9fc  <~ҽTse 3M-pL>@P,L RPɽjg:ӻnRhx eU^b3]%E$zɆV, U2fKAʨ[SDm*ŢjJs٥֐xQ "BԪhk|"kl~A 4B;tSѮxnQ*pE%6.֤{poi؊قdLGJN6uLbm;dVvvf4YU34©C}5K_ޣDuur Ka[ tM˫e91)̂AP;ѨioYgC0wv<5Namd0۸`Uox!YTmx I4jGdTB3xD۠2]ۜ>Nhm66̷uÕOp|Kaqfz:@|Iaopsڔ#/26nų|]ߺn'tS7w)d>|^Z_ *0΂ȦO737$gߞ=ұl@;^K덫>z_?@=U[V8=Rƛ(ŵ$6y#M,mqNƧ>|茷@ &X9dk(ܚD!9tS<ܕ[#&gRnM0ukL[h;҄(s)[M.峉_;vll{cc{nO?ηf?EF!ʚ!uy Lj7H endstream endobj 86 0 obj << /Length 1412 /Filter /FlateDecode >> stream xX[o6~ϯ$"k m 6d0`EoZm؏߹d;-6cO{I:{s> MFiEŅ囓'T8|^0-o~04%zSWA > ] ?qjC{z$"*ru0m7ȩ(% Åf+#- twSP-n&]-\yFʙyrǟ'.|p&\1 gf=FE6"|$~ M `#.PZE?x#1&?F,=FՁKjzEC E\Y8FCH? 1 '忋:0FOIpҸ60ڏ^C{UϘ g9+ 3]V9bdV?uCeA B0_K{IN~wC!u{84Pkho+QբHVsԍRٮ~.R9lQ`ny+W=إ~Q~*H9Ƙn+L&J6cD/vu`fIYGHq3:dr|GªiM S#tQ0,,ڻs+AsB[$<=R8.EpD`,(vS崴f}ݚ|'sޒH~ yY+aL|ŗ8.AV\S@5Ȇ4z5^Y}x/Ys8BFTjxBAԏ3]-1 ]9 \GH6w2.0QjXص.w|7' Ei+ڸ*[nmQK`cs: k?ζENݭǸlK'Rj w_?8?Jqkv,D֎#CLX$l/,oX?d.:pTxYn,@Ϡih}N%Iِ}mwvr=V&R('6ի[|3uy7fa1̄!8I#d/B0+߈u5 endstream endobj 91 0 obj << /Length 2244 /Filter /FlateDecode >> stream xnF=_!8JK. ТI")}JXHε˥DَkxXkٹgvh~ ^~Dey piK=5 Lp~38Wg@GJPCƥJñ H5Aswrն/ЭFt)nyaG1hpjkjeƈa`'a\1q9f| +>ymBO)]y[=^%WFj@qm9HQP'*5%xDH7uGDwk$I L|x#hnlɿm8kD!d6FQc|Q[z#|XA< ҹ ؍Bojʾ{~ /k$7_Ko " P6I6oa#a'7 ;wj[FhО@v:!K݅,'}t(n R@0%sYZ>l7$ #aW ĩ/9 p fWS>y^Wq)Wɸ04'Z 8܇I3# p 9cA Lmwe;3|xжOFY"m#HA'ov)vH:KO#rUdnc4YO(ղƒ:6VKʥ4[nʘs %ވq޾1Ƥ84siuؐlyu'⡏83 5=ynsS :啹Ł_WnsZ-~-fDSQs݉ţ;_KX|ha_ζdcZVm`Oyxݺ‰uC\`{LdR97f#!o{dXs2ԒDE٭8rs&#R'9e,JLC ehqj{ paҀWjP rIԽ*n6]FOޅڽzjT^:v*I$ąb=&*Nf.pWLtC"R-*Z"iѧ5Lpqty@ lN[(TTTuAA&m᪮fB FE@?K,q%D ǜXnU`eM"`,͓;:wuy`si R@I=hfOa]cg,}Ɔ߅@zhv{'ѽ(]DV64ף#ET 1ǔ8WWִ2eѕD@r4kVzbTZYl>?ʕѷiW WgH2:JƸ>PfϙU#?9{dsvpO/ܿ j wWb^&*tW£w i}:RuD*aA{\(r1`m}=^.s6%fcR~ا'sK'ܷgи+eqXډ"hSKAd#pk/ 6' a o]u+)yϼԶcrL gm5יJHŨз/F9(S#sky"tkMW5gn:ʻw8Zln049~c>:k&zNPt"jޝ\=9XSuچ3[NGoqE8!-NU" L%,.~]5$[k4 ^D? YҜ endstream endobj 95 0 obj << /Length 642 /Filter /FlateDecode >> stream xU_k1 ϧ`>91 K#=ued% +I]'پQ>Ȳϒ,8YN|9S%pd'w`8͔֒Ts" g$!Ռ\R]Uc+AŬ`׋ԙwװ@WloaEwñ1DpVR<eTႹ n,WJ nO*ȭ\:h.a]YP |t33Ft}ē d8 3%c2N\M<<#(¦`2_#gY.y @bH߸# ;;"IcU?K)F*o."C҄Vuy:CGaN80e0XL{ {82Ʊ>?0O n#z>H==fQn)z4:Q]JW;]&}NK]ze,f^b_4ej E ̗pv=cw/WiJYFu6[U=;Aw8}n> stream xXoD¢IiJ6JS r$Ku 4@k3$:^йKrIpb/8#kZ%\niclG?}p`.U@F2l/X><'BJl QãjiUpphm$'DRc tXBgy f*^{Nᣎ˹ X ϯ,y:ACd7r2HbjkolZI%4Zc;hx@Jɳfy9>W!@N^w侀 [Igf":5f޶eL3`fD$F2h,u} uQk) tr4J~R8ݗ >U_SH{^ L+z>@XC/-@lMDzjEqQ9 '9gdA"I͑nV';c^QYE0'u%V?=l ~. T<'X L.;$9pɌRecNمWYy5T@C&2 y*TZߪS esh˲/Sg1`;<]A̍uf.Xkg~Q ؟dISrSnK3d375PWV \*%T>ǝ`l&%|g1$x޿g˘9<~> stream xW[k0~ϯ} %{l%ee+#$NX.$,s$ىsk7:H=ɺ}:;e3Am,p`sxm4ZFM$3YBr{lQpݔ<я9y[K(z#l!Nf$DpL TfLJSR-ӨCae :\~dv88vQ,>84"w(~:G^nfApgیVԋ{sƵe< x>! |T(36q(;)&5PeogwIi]:SsJG)`xâ!u"d ;Ս&RE-ؗ`-1UB蕟zkեY¬CχܺhI2lPT/= _]眙r6OQb9hce>SnkA 6`!nXq\4!-lgYi&%SjBHQQ,5I@j`}x{bNCWaLBuOn, _!wI0T 1,:恩q8t3"a~=Ocb Ai2niv⡿QtZEUJEa.?hPI endstream endobj 109 0 obj << /Length 699 /Filter /FlateDecode >> stream xWmO0_aeXߓLҦhӴMJ+Em wg_BӘ.?L:s"L"[BwrnŤ8FR4Ybwe#v%ǝDRetq C1qCdq4w2)x&21!*:M@c*X0%G%/{ €r.~qbsU#ځZb刲 +FzءBNE7W:?֞s2Fϵԉa]aiߟh`LS~, d)RN^F߅w۲ݡJoeY&d7CV+oA @c-^-^(< knMԆW4ͧjc%S*I/ jk12y֌|', 7߮CO5et@SOCgi6=8*vDEx9y<4V$9U4#ڛOJ$6\eiIxbIݵ5Jٍ=wjN/ M h֤,pߕ4!_$Ѻ9oC]tm/{Σl%v#?{jr ҍ)2Sn..˂~9vF_Ja endstream endobj 113 0 obj << /Length 1210 /Filter /FlateDecode >> stream xko6{~}THYl< 6ާT[V` x+iТ(^<1^< (t9( oˣ'Gd$t(or ^+7yDHRt vDe{2yK0@Bg%y87 <(Ʈ {:qAfIE rlT0>;K`w2rQvu3\}s)[FTKʩ&Fy{E1Tj+ܢfՌ -i {@Wdk d&x ȿ8 dVhdyǭY0*<} m*uc u/ `~ b_W.eΪȨT@^8-3nص"~8q?gyfz6>k h|O#G W1ׇAg',FnSh="X]C7WҡԹ](̈́L8"bV!xFJ~֦J]6"ʼnUd;*Q PlNcuT 4ԦCmFYaTgMIl=b!3SɄpCQo5U`a(cC|̯+J",mgZW+cgLAdxSURlPfJZ-ӝښTX5Ssψ;J%'aE'xmdݖhg`ARmq7D` гG7ۺt/X1sU>zD5yieʺ}3rz19ñ endstream endobj 117 0 obj << /Length 1652 /Filter /FlateDecode >> stream xX[oF~ϯxa#תE*P}]v$ ;-x6ߜsƾ|řվ2YgPa^g?...ӡ2ax(PqyjYzqB$8׳GKP6qJ"Nċ(2?Hl.AL0͗:i&Jf9V-pU%G7\6TX3pB-:Nm\ dU7RL˪9N u>t<[`q ,U)f8XJͻmXopFkڨ(j\oh[eCwdRos1Mn 8mHh }tp e h޸F>0uE]蠒 z>~GQ@Acu~˖ڱ3նrjZs4,(0ߡ K)ϡס}Kwv=%XD*h{X/q1j FlGWĐ~ʥ(u@ѳWbjme0[K|Va%/F wKm"t 'ɷ]u7d' J'#JDR$'Gذvǝ(|TDh[I>wC!gF/yilT`ɪ gRYTRYivgqks#|f& z?'nLp+}$~>]?pu*dvf㪄S,$ղfal'{}ޑx4wߥ$u,^BN 1֑c+7NtJ5;*4fkpC +&[ |z#-3ʍ}2`ݙ&YR$jF܎lvM/VMw4[E9Ov*N2{^v60"(l V>D  ⧽ՐąCfݯ˼*Ը+'2 8ϊ)I?gROGRQDm*܎QPCb6Sw-$ə91ϔSri㖓Z@?>^ EPnҙ?d*O-wbSwz븣MaZD+v[&HW-AlEPyUeIFINBd^_j%:;v}wO\&+_hfT?N!WѬGD>?Ri9>4(Vg|y endstream endobj 121 0 obj << /Length 925 /Filter /FlateDecode >> stream xڭVn@ +Ԡ@嶚̦ @4 KHG΂!#KHHH̜<ṝ$E|y48=2)`4d %K8͂PY"ڒJخY E(O?(4q 8y.@3"HLe3pLCg!Io^<wj6usU66f$$hln픫95li)5?=yhTЦt6 xpT3drLۻʐtIb]m0]Vg S],jTzl(H KΙ۠Š@N {@©k@@5E y9mX{ cŚil$2&-둽Uk=/iӡzK"Dr7s]Մ|MvԴd!{Npqb?2 ycm WsL ?C 8pu;\L^½vlAٱ5&xޢǍ6,?qR m%<ḴGjB:pPa ʹl -A^vVE.C>.e0?}XR+V+ɕ躈[5 ?1剦џ$rch.#\ ~҉K"oQIw-Ei~}EE$ݩȭ?ǥdYw=0$JIWCcKj5Bi9Q5}= 4nNݭ8Zër8zYR~Iϐ1w\GFT=y&j5Y>=&GoNߟfj[׾~ZeLgM%?a H`|_FZm endstream endobj 133 0 obj << /Length 1331 /Filter /FlateDecode >> stream xڕWms8_♳" 1i4n:vw%E&+igJ:#3|b#c?)X89_Q|60ā3[8Ottf-xѷ]gA_ePt:^'͔ALr~UkxКY0<;|*B]Teԓ0|JVhh*w=3{݂WǑW J?#s$/ؤC8טd7;V{xQkkT Wp̱g`iKh+40p/ Ƅq&"+8߳x0ZQ[l:dhX]<NP+lKV0z^#y)WZhJܙ%V0*.E#t6# FD猕'?jPkz* uΦ3?{˰1޾K!0`D*V : EU׭&(L8ј3p8f٪ S` $Rx+T81PxfKRy $9H"l86ymʘ8qD\g/+pwFq:;-fujDV8,tYR&ȕHIn-RĥC:EKx3eGH*>` .%[s, ${DHd KjA!M7, fZǾla[eɉ화)$nh  a#pi{%AZaʞiʢ|g#qP03XhHh4oF+@wvwU^^خq>AD ;Ҫ)&n!:?7V%3w֯ pK_kͳeɑdžsN֯E?D)ȃ);Zt ~68>iao8_qKlo(݉]lXc FZsG +`en"_-MeL/;:;`up^iol4]W}d*_ Cgs%WPTԇjHk%'xDrdu{:jFS PKu-4uۺ{> stream x՚mo6Se la - ؆"mmY^jMLV?ɍk;1ߏ''QQAI9|`"aT`(K:SXlʲ&(o^t9ʓU.*apFl `bxiLf1Vy OE#De0Pb87V8zȢgH>eRzň'pVؘ -ۨΙ`YlR?!": 6%6`(&*F Q K9*2~F`PI.F p4y%sr>|Є2(c39L %lK d:wV>KeAx"/8b$ƘY2`0uț`0V g0%j8HB*'`q1,9$!3`~ F0 D'Bt$ Hg ('D$I B<;.TR?ԯjU>W&a[cMՓ'ţѧK\s5x|0F᪁b w^to7jdt{h2h[|4j|~{Įiv?Vs&ӷu,kQǪh0]د) $ca :RD_~e,\b(~arD~ĕ"Y~{1eA pz{`I4ˋ=˘ Մ6˚ ɻdz6kYWF[ mK@a`={`aib;7 JN,zƀR|wsJ?בt]a7r|}=z۶ߕyվzya0u]s;gB*~%?iF+msi"jivK<-t,TN,VN1ݥr\W1:~J.?Tr[qY(ߌK⵹̹3JiFe69e!#>#WFA #2KPp/B`:dȄ4y]6!21tPa"hSPd5@eP1pPa *o ʮPq˗%6{!oDh-Om8ijbpkF%)kƮptn"n ƺFn}?@W=޸ܜJ 9j#JswW}|^hybGN[ËbMB#Eycu2nZϪ"~CyOXڦSR98(39fmn^]TgtF[/t_6O? sn5Vh4 Q#OwK`|6I7*wGa^WV endstream endobj 156 0 obj << /Length1 2360 /Length2 14497 /Length3 0 /Length 15887 /Filter /FlateDecode >> stream xڍs\|]ɶe'-Zl.Ue۶uU'JR%UF3{ #+ @L^UNM†@IfbA:9[KF hD6Vv++7 N|qc7+3<@@)fdeaߏSZ+//7_[@h hjlP7x K>ffwww&c[g&{' !Z%@ tr~ P0%@o"XAJvf@'?@UF[Xo`ebK`ne(J1x0~ 8ۃ݌lM@o QrOΦNV.LV6dmTh ;31{[[3ĭ{2vvT\]2,.NPc%o'jɠ<|TV@ogc7 o"VV hae: 4BV>_fԔ'آoF6#;;_CJV _2v޿1gh'`` ϼp~TctK"/ c[+ȀFXhfj2.Ơ6#/d+gI+_c7YX읭~#+ v#8/JUαqr=X@C f-y03ٻT }N[ `M ^?,e .)Alf?dE0AAf?R41vfa'cӏ@Х7wCg|ejY7[??T3{ \f 8wtPu5rl{X[~n'PU-=,vѬA ?8A}M4)6Ș腲q]m6f3;.@3DWXAc7` qgС OXkbW@YKT%?+ljҀ/*dO  Y/>B:*1⿞h h0kol]~_#Bθ3å~JcCc)G OJҐP.<$xhs8 hܴ1ϟgڔBb#誡XD:{0؏A6~|0ӿ:Ye&eu0*;Ѝff{E9C\MK:G-;>Pz݂d92v[$GWqٓE}wi`Ytkxs"KXң_\S4g]VF;P Qءy2abc!"ߚnlv$ ൟg]Qd5N NSFu O|i2?lbIH~3Hޢ^(3t9ح `O(:h'vc{mLDÛ'~VvbY7n!MI~{+lB4M@}2[27[y8@c4:ƢEP2vvDCp^$Y y7]Ȅ ;yCZSᡗ#]i#5RO*/j7 /%>P4&Ƶ;dg,"a֮L^┨/o P$m}s8uC` wf&$HՂDhv MyECe}{_%zN0aK_D;';3TvOK߮:f 7us} v&Owd3Bq2[{~7qUWߠ@wBB dp  Lc~pt|9q ߾9b ~W\7[!߉v]ddfRث}3w: J"E24;2*%c׌e(<u7A]$Jɱj^޲Fʼnt:v_.jlHW\n(Ȣ`,X䠅о;bßȃ8f;D즦},f]F)b FɂDxdsRV㸮A(M-[AܓSh/!/ԗ z[l]嫷OP,HW?N}l: q=R3R:Q^L%I蘇3$uu iX)MWF0zN=(Cboķ {A)V!㞲FƌW{` ֎jfmFT/^`ӰĥV͙yܫػ ޤ#}Bn މ\Ȅn3%Oع(S\D~ƛ,#ֆ!xB}>"lzvʡ{mJX`ďYr#XLK~|` ŗ!]KAgA,DU' b4SS``?2kY. 'lS#'uEj Z c??HwHgqG\P6eJ랡o8pke>#HDn2i|>Q1eYDOaOz75!Yc)|vJ1Wܟn/* 9Y6bPb]ZIq~JD=DV|p&g)^ çR(ѐ\}\U22.1!&j+Uwg} 77 Me]2^}E}ы;~}lxvB4$S~uLk>-\Q?f؞oi[_Յs>~mK,3ـolQ!CO lsYbT6Vyܞ7e $q7f+G:wFN ]#+a&,5Iol陮ij!ژcq[Oۇ(҆#u CoQz&]$WqģJ"/:n+eewds,Eά袱2Zߕ+ٽ/|W2wAL)`A΍ɞb3W||a6%(aX!<5`s"жKY)'(ބyl2Ĺ*EԦ=uEθy7ɜڬӅ@Wx,kq##\)׫m ѷHD޺O!Vʰ% y+E EdBq5} HJ7w"x*#2M"a FhCgj4Y?{? Qx H\;-/V.Mt_ϼv,F/Gtxn׬b x8fF0xI DƝ#>F/Ix2)?kfЍrطU'jWlecW}gtIZBe $(Pv,-a_Go!Wԉn>Xy$ %{~NEVDkX]V#A lGtEEa( Q9ֹyQ;"QCJr:{/b oN {ܨn)E;ŶE*|-U8$36׻b*r$U" }E{oCil뾧1|N;oWaPO|[i/P ';Q#/Uf# lb?ch,IԄ5lcۜ.GV []1?upEj6$ {;B -uF{ظVZVGf/ M6.6i9`\?1>6qcy\&\=laXgf{O1&)2i'P "M ~ny+ B w:|-cXW4[t}y'zFvE @^&Qb4_m <(庾ʼn=UD̬=MZd2W.o=: ^ǫ0%>,CrG9"y,7Y2n fQhz`z !8M90:FXČPFP;+=ުp g>3OM)J fC͏B wAlҎ>}N}q2L"U<̙3NJbJ0רR\p|`SZ9[VA exDp Eʻ0 S} -ռw ]8b{W2qSԊD#;n\ȱ~NUP]dమi=dX{-ηΧ_X3Dzd):}^T`gloY `6ڍ$>fJeFtJFfx$[B0֘Kӕ4|jEYf*x{I"A*hqL;cRDFz(_^RQBmJ1HXdlJ_sF':򢹈7;A34oA[LA5s$q./(hv5*0՛&CշNΦoyP-eGIkv5;Xu"GT@񸋖ԤeDejiK `-J={dH3B [-ƍه5rڊ̊^b`d.vxg"GysH%EIen9ˆ'y0Zɦذ_:杪g]3&e/(Y;j@7Ȅȱ0;NF-E}:Wnk'55""8LJ4ոH%v™^d3&p:e#~|3Q poO+Ljz |rqUO\ l!>D\/PY[ZdAb~j;n]liW 9pHf;m(?Wv^׻y -xBʯE]bcD 0Z=zŹ)a<_|V)X8 ~E ܔ3rT},;"M~ VŸׅbCBuLɅcM,̰k]èZzQc$EڽT(}0הI~9})JV%}adk[ˏ-D3c9Bj4BօZ jĎIp4>꼟J.6T-vTؤN]Q}X5N[{b!7#63*NDo,'vʲ/:z3#p4т\Ԕ>umoE~v/hlP j#(BOG9R^ᝢܝ$_$E*+a\!Hw׵X^VS$ h͔o㦷Xp?*A:TK$IДf=e5ȈmNh totW-w)I'?EsA7$'MZ].dxI˴7R@{iW /;;+m3zzD< Zor HObw㐐 K|w˄ U\UɟGO#Zۣ?sy&/َSoB*=d/Qyo.ŬZul#x\SF9TԨշML3͵M*+6h~mD4YAz𥈌dMSm4ͦ ۠e C!)q0+b֤zzuu C9cϭ58*+ek3|:lySj/I(x6rh{JI,Y"G0g7&6(>jn~C^Q/D M}9CWJXpHAAvz} 7:?TdE奯?8G|bk0Ʉ")T$I(HJ j!P8c=ܾPZ^6vcal E2`NXA#b>Z@a ;] KEh4EPc+&IoR)~,K<t# جYCc F$3LkV=|@:4;}I>5p ΢ 鵽~#Wvx]~wfSgZRY߽mΥV q޹(#[+mxȘ;DmꙄ4F(nkDoP3͛_g)G;8_( %>79m 4wXcDa7J *i&8$g{CԉAX#zҟ`ERm>P&btI|x>96DѯmR1 !SKc.eC,ѰVf;ŪtyMӥIQ5MUқƣ w~lwi!^j_FfKj{Z-eNoNASsvKENd65{tϞ5&MN1|jU3]!Ig< ~h!j{9exObɨ[bq3Nc& )ҟ ta6š4#wp Ry\Qe .[ZC[##\?25+q=Uh`'96 <ح!WQ NQK1(l*m#)SFE2$`y IL_OGtm`ۨJ,~ae5/#M'MZbeJ7-i(֬lDݯSPLboi_0hr}A`[ln0kQO.+_iS03gVfQ0[ BpDdQui<*?!N4G^ҪU[ڸ{0VFeeSmߪ!wdfowd`b#:At4in }/iJy7oIc&eM<3xͲQ&cx'/GSQrfo6*.\8}{peVf:;M}d1^̄=zfl 9!a{/Zxr:NUj2zJ=Hx)JjnG{Njx70r$=EubXi(wqPlK+}bjHMSroe~ۙa U/lW(>w3R|=.!0v4mʈ,..H Qz) (r$Ys7EdraY`U Z.&ʃ|:V0![Wŕ <9sbGvSn8 !JJ + s*p`)7|z`,@~D\oFzũ$>{FH錏ͶX')W ^ a;;_b$? =Cr*Gpd+fϏս>nťx !KԬSpb30ʮׇr|.I3Mޝ5rg2c ulj.ڜyHW}o^ ji~qQnki5esOxoZ۩FBN&h3|U]w cn_h6CG$",DN%dS(63X$6Åǭe5E$kbIm*nwvPZmZ2<ư_KBר j)_vGZ2!b_/soXg>k8 1FK G3,n½צaP{Q bXLaj{kSyku{JePIjz&}?X1JuяDUhrSiYN9<6Qge_Ipwc1M{8r%sg}~wh: m~k7,W<B Jf͟ ۼRz?pW8jl@[QCXh E̝]i_JSaxT|]QzlR!:ȢQ3$u,;AURƻ5ӄC H!UaL&j~ GxG͊#E*\®l =aFSNdT{2ط6PxpS٢$Wh.}O*$X3"X\ʢ=I9\Ԝ*0uX *IYG ] ntr J{rC9\TU/w;fx{L1 «2j?3.C`ADxbA Y@!n}\ 5# neɇ?;n&9KHnֹ+&2\ G!F 44:!w|1g-&οG_ NN|6@u4p4sѥɥ\X,&ޱ#zÀwsCyBl;I%MA13sa\|C2rGdEc'-TLDHMdj~C;,GO'nVӡ4|=~'N|C򔆞LMzXq7~d"lX-M_L!leCrcWa94`Ed.O.Q Y@o/O6i|68B\lS| |DQy5T /No)-So\e)!QK8({IJКE;,1N>T }zcIyhxAYJi,21}2%p~GmdKڠ-:9[J.E9 j{(1*-9W1m$]f d;И|kd~W !uGeV_?KU:uWDK<(a)8JŸH:Lm1t!(^?xU VՑyI0Kg^(sl۷ޢq)Nx}E䳩wف,brݽۣ3_FP黳0ME'u>6)A>ٴzfWcJz*hĮZ[K3]>U9yZ:7T2G#$>x\f۽[&K<1BmdsUQ=&} L乡opS/)[An-k&6ͩ{u~)84j_Ҵ婋j~O8{XjfA# _Bϰdvi+yз4A*9xn_fQ~4ƤuSshu&6*>OH[^#>EҸ&ONolB} x?L< 5y=XXUM=ym`2񇓌7Bywen<}ùo ڎn vOVΐ"!,ƷCTF"d=O,q9Fd=[gzt^XP /Ѭ씍9]ƈ @Fs@>MFdZc纨zv&u[n06KUԮħyic<7حCPs66%^yo5rZa̛e#|/d}cwϫ'u$~ 4`sJ·^Sm.0Z8N+.dv768{̪qfyvfMZ(:)90]="kr%iHhCb!X~l kekWl^uUYU5 &bjl!|-Uo3> ģ*9Lۗ@Yx{KQC*o45+0Dِif1*H%~󏄟֋n!{"FQKzB0m#\7 W_0>E!l >OWj0QU$b}# g䡦1*0,EDV1ڂv&SSHS>|J/WF&Ni,K]T!HꮧEz[mfϷ;RN<94PRNfd8hsM(x-v/ThqBKB.ă#4ߋf3ԇ61ybۄr *az^II Ѧ~ڨQC'Ɵ[ uM3Ͳ\~/D~q4z\?5Eo,7wR{|ؒ퇮CRvO\Mmd탗C\H.Cr8x 6WAV@ O`8#,yIAq \EY?"^>V#R0-VG^qIYsjƨuil='4\Bbyop r  6)uC:45hʬ|plFIuӭmRQJ$ f~`3o05@nB::Mŷ|U-qs ;gnb|wÐp)7ԔY/L۬$seWWNhfNeA)^ZrogX_>$qc92Ʌ\}< Yuy(i?-,2?WG\8wYF#U+? FM?e4j5x TTlv3a_k' u^` ;,kr^[!M0(;ted,3靽]U$L救e* ѯ[6!G _(3:=8;:OeOmҨ ӕEy%m/o~^0_'Q O8U8\ҡCIYU{ׅfN&5mjOT*7"ƫ]Џ U"f68M|B8 h ' ׯwI%~;4Kǩ]0TKIrX\xj)kӺl@Ս^2WWmh8iΆ憰Uh>Ax#y\406/LCJ^}tN9 bǦZnޞЯ.Mwf,kd{H^ϗ_u}Wqҳ\+bLrw> >[%atK, oRLq2xIzG)( XY; R52+:w/EZ%[^y#AQǴ?]LC\`堉Eh+!zEo~"U%K>?u@J'K ShY9zk1D?R_B!tEoETx>暻jw@}G*RՔInHb{v|D;[HL {,\pw &>[Irt/~<|NSn3ت~h b8kpw?~vH i)ʪHP42{xz%?P+"(bXH@ 9lyά{QȬOKkS3hl-sc5[Z._jѴ~o4͉%> 3ѩUԻx30Z!|R|bdG~8vtES7=Q8#.nV;pP@3tVV-38IUԀP>{)tǁ-wĹwj>vN- %6XF$_E$Ojɵ_ccZ' n7WOr ĈΣ#)" endstream endobj 158 0 obj << /Length1 2445 /Length2 12764 /Length3 0 /Length 14165 /Filter /FlateDecode >> stream xڍT Ӎt!%H*11twt tI4 ysZY筽RYE(`f`eH(qٹX9QhiA`[(@g@0D&i*8d]m\^A>Avv'; &n s+@B+ Cx`c ftLV@;-@ {'(jbl) p@dXQhV j`wg "] .@g&#Prm,38X9 @ MM=A -$- 3Lغ8@ML@&R7HL S3U#ۯ06KٛK8.(9 }dg6C {s_e:i؃\2@D(e@0 t=̬~{:RrCjvtpX@,/o7 S_0@K=1o 3Y?Ͽ f`ojKho^ ';@@@#oe?i*co;[H?+`7dq{nyr俢H/=?z;?uC(_S-'WhrZ 0[H4h Y/5~4[=Pnpx@RBN)3N^' ;d8yxhkl` R/LyylbD#^o`lM_`8lo#.o ` r]7+Fv®/⇰+Fv®AE`q@ 6X˹* )x l!S7q! B >b-~CH?/%wD__zW?AL,~W $m4iұ?J\Zl#@\!'=8@[ y3Ctt3ߕ6 %uC`Hn@l9ƗuBaG5r姮>P|IJm߇֡Yca;륻h=Dbn=72q!z/1[NJ,x*haTB%' 6Lx_!B)_A){W+\i\K\M*ְw,9}ZQ)OTu2.L!608-ޓR7i>kt^|8 [?$R%F: iC.+aJXhT&,mHȂyPvAma?:Ўp_[ ]sդHtxLK\aQ\}8Znx. pFzW6xl4g|Nnj6n ئZH XbNeG'niu .9|[~cɋt%z#'M˽<{wf,m(W<Ͱw5"}Ēi_81~[$i5[?TzlDRdvlwJ;!ӗo4˸=b2ͩ`Y bZstX[aY./j">f:{n#0Q,/w\kQ+&%$ ACl-۱9<9mpAnWsQ:]pLD)ȩ"%wŋ[ggA4/*T l< AJ K")ٿ226,MT)"ɠ;PT !#}|QoLC`˾"?z>X !R[d쵏ˀi qf[ޕi=Vʩ۹? a÷J]mUx`UGɧe9CSaG=) dսװr< ]vJN?? UҿE ֿY`пzH41?+!76P9 j S:'m^zR0˻8 Ӗ6j--סą_>~Z}.dV+Mm|Hq%ZSkLYXFvϯ"(Z\ϴ"C10-|ȓ=AiѲ%:E8ɼNT)>::eçM o˟Vld0'S6ާK6*btJh#ȩia讝R5~hSZBG4ް~D-Hdc&@X2'/D~!`yS$";4 6.a)иkTl*ېႜIH0 $lķ~.D 7^M`O4f) JkQ5AN|H&cͪeN<@*' i ?-"&iK˚aYNp[ E A~˵"N=u|C^_q.殎 .nF |m:)e㤮P6!,/R3$T:xPvG,gE@۵KRfZf. &Eat_lj%r[h1&ON~Qvt~/0aO]BXw oJ2ZuvA3X~ÇPD,EϪ3,B-=prDP^˛kw5GQv".I4ՆH?j #^߸8%y&)/gǤzbO~- 36"DWR#zݤXךF~+OIF;W譥:,02߯'r W~*QJt36_$r*wJ4x)qU 鬼g/*Y]Ҟa1v08(. ?;zڤ /ːslGq%'X!/mOg!ԧXL̼r_-D'r1Łғ(tIW NM>mPY$sÁ5'=LoFQOCz!>YiB!.<MݜgsU>۱* l{W̶ b-6Uv햜fOm먎 Kq;\ 4ohld=*U+b\Ua$ * Tް/^uX{_'UBW5@neY@;J{l"Z/2jܦ"#ebM{ 0P\;SNJ+8`A}N©KoָqwkǬkhMՍULDK> )Ztme !Z_وF+/и`ɜ`u9)Jca-D:L"ϩC!긶o:{iV,2*r64y[3j䠚5IE`u*[yv=Υq8'NTx$'ҌWnsL%{~sswY_3 0&ȄeHOCL7ЪHO)h? @ZmUܜ/6H7{L~n]z6g MMo(V ggG&񣲕'q\cC*4]r^}@'5^4QWCiݵ|0av^.+n:hj'Ď?*zwsmik_g$vFO~BZ[=m0_ R;9OAPLT Yv[i6clw. l+eAfʷ~`.M)wx,EC*)up?81.(lp3& dZ aUi%f#YzO GojbqK9*oSR,Ƹ8pp̬ދ9VJ3 Tq6!9s'Bv• HV6l`$(LrBAv=H`t- !T q KS-"aoM)|e23w~q%VHS&=qmvt-o3e`> EWʞVNbOxFbJhbDR#NBgm3qYw aIR5![(8d97jɒ08K֐wLmSK¸,saV\CwGVx=e3cTDme|Xc@3UשSGv_cR+|I:~-I3H~?L^[rY#ՑsgfE7m2J^a:?Nñk`S5ȴ~Zf>_ik՘UIQT{kk@U} *F]k"J6 \M3A_ c'Ҍ=/=HRyxH :P${%c_/$N<;%R VY֒`iv1KS+/ ,.¯WL\]+GvŹ,q5Eng6I~%OYyh؍1sesUl Drݸ.2v,0u.<}%I1V*ĚUrD ;Tx(͑X:(oE7CxW5,;Y[Eʳ-sdSb٘49(uV$6i=dgqݞRc_m*^wXsd]tzOϬll˥`*??^tG D#kd=-xzӕb芛؁i\NnB#gW;o@AD% 70G< dϝFˢզBզ fP(9+b|:1>FO*3#j6^XDxhw[xr83vVE=>l᧱Dq|54b;b >?2:CȕP@p N^\;2(3{ݷΨ9ApwxJ?97r|'lMJX+J9 X J1e601D^0Sֿ{P5_N))!}s]={°"ةm?+7āns@}b *YҿsB<#j DY*@FڴX$N"mV hhsmՕ2҅IRml;ps(A1 w_Yv8wת]FL>^^VHE?3/th 7o#7$-kt@4H̬p!9ML ą0yՉ58:i)̠*"zح(Im,6‹ƒg qja )묞+Xn&r\R&g\4FnLbȠ~LQXPVd Oq m;E2Җ!wšF-G?Kx),QѰ](c]jT$\.بTʮX/3rG%.n'#|t Uw7Rۭ!7{YH'ac_Do pJY- RUGq4@FB\B0Q,E59dՌ>Z"]sܘuW_!r[I3bVmM]0üE׍KH͙b |]; MFy}Qv; MD$Y:YZBuHrU:hz 3jeO$|Xkm9,cKrmHo k/3KG*oO^@6%K3=X#/ۓujq 7MfVjV)|}8cqʬ2fOe`4 8ߏ1 BN?w>TC#Vnl] Kp pDvT\b= 9JpʹI䳜Vw.Gɦ]%b!ʕGCWg[)7~wQ=R h^jp=* ވtoݡt:Klݬ{p\߳>G<ږKs4+z<|,_[ |0J_kۯ`}}w NF'jOU|]S;wC2t/,&1w#llϣhL6:?{2DAf`-O$l զ+ǁB|uET]2S,r^ qFqCCa 'Z(?]uMNӨuKL<ܥ·Jo#K9au_A%Oao7 \`f}$?C{ c"j6{w"NJܱG)r矟J`bxKG_1Χ~Q{Luo g5$+<' lq PFf&pGwQUg~y~׼*GV$=&A!mK؆<ӼHgkCd.LY%V;x"pSql+T51YbɀvA!-".'y@R fCG0x//p,5!+_̜ڸ+t2" *LH'>fD%;ZP[u tq}38S@(=~bd/ UPy+N4Lpy;ħ_w9П"1ndie=/C2|CuL~`;Aا( cĸ65×]f_wo }ІΖ3`:,Z(OT~TZB+~*Rү(fBń46E"E1u?o8Ei0! § g ;wBkO,ײ/ _IL$[,eOT+g2_F5Iޥ enoR.O6H R -hxMNc4]2^l"J%'q^rw|- "7@ c& [~0{n 7_OfV>W٫M?үiLk ~ 3AW)=NR`Ќ|@dMnEںՇW.&eaHdA4 j, B}Gn6G4 ^$`3SЊ` ,i lepl|J\dl4WW6q&kЭ51:rljaV1PJ<= Zټ% Cw2"أ+3*01Ȱ ^ow$["DDE7{7OL(2W+L]mx)y_i{+_V;۠R*8Oy}0j@+MUzgK7oƏ5 ty;-x;Ș/|nѐ ږ&FTb09K6JƵ@% u.33>Z MO/ /*:UH>Tdzf:rSuvr J:i㩼<`?4/D:E] uK5|JAr9dYV0F!]|^ޮ#!Mm fIBW.BgޑGSj 𨥕kMm8_v{fn>>9-b}LyDiSjץ$h]Z)tG`^~?/5e_w7)J͎_"=bYˣ96A#kE[/Wkg|A%FF7ȒzZƭ%.`6=mG4|%Rq^9HNPcJBYoiu6*NN@/Gz)TkjQ,):whؑRC'/.Ŕ.'hJY$^}Q+,} 횋gN[VXTlֳz0ٴ=0];ش-v5%sT&}D7g:aೂ^vIF"J[8kџ=zT]B-}òzZz fp$gW 0Z6q`KU[T%c ;Қ;> ܢ>+{w?31C=zV %97V͏MśYZs^!T.ʆA WJz&F [*4g P=I%lf;&2y7_Tp(I:zM}y5N2:!)dLj;A3:٨scQr+Q#;NwBV:k֒F'&ݧ6vFv\{ W9oB&\"k7O{wy0 v<څ6&I:MN2]Oj^kw"HAn ~9Wf[5Z`N H,smTO$FJ~gW`g(.H*Ԝxh6c}mi(2&ԑ,stF:!8g6 m"+ap*㾈T7`'AIA'rI7Q_7: W}wM[L^V9`炙7KizUzMCɖ' :<ӫQ59;uiD0/zy8Q5[\fTFu#k Cď5Nk3@eڤ] 6D,oZTYUbPmbZ'Dꈰ9%3>m%էu 'Cz`QVm6K"a]4fj ֒%Mo69՘hzT$doʳ/ ~H'o31].S!amǢO6ӇN m\L{_ x[]sc}B(~jdR PciX[Gv4 P \NtLonu]֔>CĪޕxn4i֍5KtY9ԜZ40sK+VvmXNiIjM Xȸ Jɐ( oiAOtOe|KMd;)rz|qϗfgUF\zedIcC*QҘkW? YMr*!H~0zD,#Dea8q8jD&С^sp>O6_Sem!e%'.Y;`e87#\6 Kߞz~ ب R@yQQ_N]Bυ.أ^~n-Ory%m0& _7F>TA" b`P4j OLPB`TgCU*FZoB& cTeAo/ىt$仅Air:VT(G z\s_z1иF6GP趥 &ˇifv\-=1+YwBU5zUE; OWM$ @en*qTg )5kQRattP Sk%E{;ׇd~4ا[*Y fIu̗/Jls5rݫcnTD^M9*Ek'AU!Ru UxZ݇<`~t9/0Twmԗ)uR{6{*s:e07.lU=Av ^C7ӢZۉDIo5Wdpaaܓbl~LrЫ%ٕO 4 !fk߫s`\za#Ȏbck?wv[ŴKdxZ#a]IҞo1[!du2¹ o7?LH!]Z$9 wXv]WGԽ<35ZݯN>mF.tf>?3Be ,ޅFtޅqhj-J=E^{ 9"I 2}j x*́™8Pcja^/솔%oN ;ZQ~|%e&|E>h˷宦߉|xlKk{ĐڥBj`…d pbq=HASO7>>T3=MI]kݢT^iKKv̌n!VBSZ-BK-iw5|w&q*sO%O7+_t14b31hO-ཤh?vxΣ!htpq#l"\n<;"g!dfJG)gޅgS[s8[~ HPe3lj0w0-2^Ӑ4 !\/8-O -MenVs*EjQ,?pD/_o;T D mvNvۮ D}9rc{j?&N_3 !;ea_[G\a n]5ֿ:jQX?li۶l' `ӧդi3(֢ ˚j[o7;*@~{j/q^@2WL_J<\5U>?}bla>pϓkNOaLh{ŸP‰i|s > 8w22qU{U7v3cTm7ǘpTnxc&tF1Q_LDèaJj ).qB!sʌyZ&HSp5{vt^aj+H0j!#Uc:O It(?E r8;w'ĞAcX!0\`wxsVw&9>YfsWfcߦM s/t"gʦ7}h؉*M]e`aAfT 0!6#?ٰDP?7ی ה+9kDKw I<~~ OMO2ٙ1-Mn}Uz9-T:CnQKbcOBVII4 S{ 1o`%A"WZ[ucy 5S®Ja{죈waxZ-}=o0v;}n#w=:eG'ߨ&l{ T)aiKg앿lZʹpԋ tb7.V`yY l{? RFbqS4TTjKa-?U<(Iadd.kug6 #n^fF'7.,CD7PDcVг. b3Ya tiI~^v۪#7}C_d.xjB~H1q~m_|®w fI2gm#5j\rIƒDŕrfq |OTe+3&4[W7 "cIX!lE"jIE|>^+KT*%BoKjtVSm[5B-:Xcp'tU]wt$SZz_+ P\ɪFW,x?U&! N0=Z3F5u 8}P37b5៓t{|#m{*w_ѸϞQB{KG6#VL0H?:vOڹVA}jDxqzs\`1O|̻ZO |^tj 0Ukg"0fq{'{F*r!-h=k\bB GQФ&n \u ~7BC}3HUT9nWǎ:)O t(pEIf>|9Զ~ s:KI-BÇ{ i33XQ?_V6sN6"`9Zz`t9Q.fe};[*R<{SxTȱSɷ 0fIC֜Amtr̟QAlM*->I'T\0T@ Q6} P xbH:*ԬRUOEr 04w]#E.AUJN endstream endobj 160 0 obj << /Length1 1768 /Length2 9125 /Length3 0 /Length 10238 /Filter /FlateDecode >> stream xڍT7N"! `Ht*J4J#"R|9s}1)80(O( P12@1[Nf # 04`%S!Q:0(@ ( B@ߊ0$@q4aP0M 8 Qq~p:p%$x0(xB=P@# Ni$SR@חex }_%tAJ'`@F0'/02:Tt6@ SYO^_ /_ ?A0Ou8A=Um~:R#`({G):`*>D# jf E"~ P}pݠ0_h utU  PK%"-s#"@ P\~0A jx 8?8Q?{/"8B{3J;J vXQ'd0GG,iW0?@ OHD !&<}4M5N0ğ٢w>_/]jpsn:!:< qK5HP_Շ?7W_V B-FB?>)7i(X[|@prpCPAQߐ*P5p?5KB""@A>:c?DP=8TL K'āH `@i~#qo$p=xBeNAA__P DvD Q@.PߩaiToáP'CvQw+v 9H6])m,vD'E g]q>x)9VS gϕGN<޿ ҷxWI3F<.Nzl@Olgeɑp*BV͗Ė.PzF”NWň5gb]E[$-35LƯ1b! mq?-s2 zjE#ɩ)4S%hA+)'EUtbU­ -Mk#,r+șz2Zܭ4)ϔOr/XC!~hmiT&S/S$9Eނ)g_@k_Bޤm=>z^&6ےtܣp9/W*w-NLW[Y._~6ow>WUŠ%Td4G5L^F?kImE;ULg1ED}DfЃ0>O݇kah#cM-:׾{ZI/}[+`]nJQ AG@/9hċŔ8ֳ#B3CvY}mKfqʓk7s5 `n/;̵P5u>$ D;05[䈎d?Z#[<v!r0W }H %v-%g{@}RvREqWY@Ad{-,m3Cgrd4)P;9!PI73}uHz9ۈeǡ~a׊ !a{/SQE򊖋]a拫 :E%]K0oL RJ)|3ǿô T}jhkS_:fD@P=hjS`GSI?_6+ qډ_(Qfz R)O.V]b Ƚ.?|3if7Rb\#gkvɘǩo4_Wl woR\Yi;-eMf^':ŶΠ}FL$#|UƎ H((^I\rK%TV-@[[DXU3;_B5kS_eT-u=8}Ѵ fdTIg~lK\Rx'VSIjC}ucT<7OVZ*fs;G0k (<יּ-3se&̐f!I,5zr?׻kVOeDmo]˧#X1fn:wMڭcբ> olCG& 8(Ad,˟-Ŀ4jZIҢ3mʜs.LWr/O >y!)s<05C%ϗTrcyTFFy6DGR$ÌNg2%[ZᚮtEqc+Fx1ɿH0t o`zZ tSij01 i*ZG)6_|T[TIsY_Rul&(7ߍ7.iR})]L,!9ZuK-k~޵ ±` uhĉT13k_櫄['\WU_  _3;i + g)^%\UQٝ4-U_ԣdbvsJW`2}p/$fWS OES>.hOk(&{n1?EoeonRT:bsڴ` DwLdpxMNH$D/Yy߰N,d@k7FFКy*)L7v5 K$!ST}F3+ܦWTK5SjB/2mмwjӧbڳ 9vv7/H[NLWxt@|6DpxY={ X ȑ~-,JksZr?cˁH/]˼R^Q=>kѦnɆ]kk=."xCͳ~c aCb};N{nvl"ŒBa:W.~e^2oi(` Mˏ\f#tiıiQPrUq^DK3\ʩl%pLMeLipͻg ܅: DGǡ'cXKRKGFdx(,b/ᙯ?y@wc]/ځ iUkܓ aHzt¦QIXyCOqD!PQsEO3IYxJKkAvfiu/TE1"$osuOr?akzZRWiEtOer3 |;'5Φ3Ww9 J,el_}FɆ=H2È4tuqbϾ3:5cNbU?4d؍FgX0pWg4 ^{"ŵl#YX }655{j_Sd 'T.=*%s~ 4M\鍴Ԙ;vO_\.W*'} VjDՂElQ|Һ6~J2)i+#qjqyLzU #1X)8 [%Bo>w`BYrSWfǚ]VΝ -u8%}|A=e[R멚-X+VV1S# 60H PAVcAшlsK/R7ΫFf61G>dhx7%$,8˩űS/=#؎0>x=f=3׀OB$H9 M|Jׅo%22yWwc,Ce9s(6.>9 y־yHb&:9e<seXt{z_(A|Q/U!o&5.'U -]Z!rUY(TFR x ϳ }޾o. MT}M9yܕ{Eѥ͜^(fsvAZ+QARwAա} 6 ?e!>!Mun{/,n0 ?_dM/Ӓ& ix7B S7ַf9Hlm=ޠHEg,c輑g2dM&fvlEu:O-zڦCXͱޒuW~oC>[IKXA(q|Q=cxPeb*|APlOj AYմUfeC?XI<{I(`r|?9`闹b"vc =9z0m렫&,eݳ:Dz DK蛰2o ()=9[,UBx٫7L=ڤЫ~~Z*ExmK"tq*^9%fj( >( ҅oH[K?m4>Q>Ce;~![V)XöD'\rE@'fi[APEFWbOliUeK4U={/غX)PԔCW2No0``M 3mwFm1v4Ko 8 {pS,ҶU>Hx14vӮSdo#9zϷ 1yJ};Nіǘ {l+Yxgd#';5aǨOt}:- Wez)<?{ :]zXϻfO\.l½d_9O<ٶPa ܯ2RR<7OƼwyrzR`KTau/q짹+.1OIedxIFi 4R+ K/-HNs=i񕏝sA6_KŌ?X#$6 HIv1p0W#4丈Obc˘lކ,@ ڴ!wzsI&^FQl"xv?j9BTb׈6h@&B'%iq>1oJ=[g)`6BχƑb.m` pbcùi1gJuFxKʻݙPFjtZ|Kki­ot8)|_IC ļt[,>NTL1Ja{?sJ-H$W|75% +M؜6r]fﯪwW;BO9V+-Vp&/:ڻ8xkZʸ@G"\=kl&œX F^ qL9orX*fZ)ȵt99ɞ"7đ^Kb>Jiꡩ[wCoGWf12aj,AGK,#PNj~Ndb6+/Z2t k-/Ց118FH^LOt-ZЏЅx %KnMhS4M5钲жM'MAQ i!-OS>-{۔7C+$+ WjW^{+i`nn.y]GXY§Q"- j4'ݩ/AD!j%<[q E#X1[ʚQkCKހzs MHU.wMBRoSD]=}= ~8O'<%ix%DŽ́[#@~[{'QFͫRR)h"a2xg=QvG^= K4{'y(`\a}YR`g˙c}J|]G 6Xز؆1GA R}NAF2[4|ZS| g:ZՒs|Fz]ezJ*|By T:5'WYF),gWR~R$uMS]%0dO%74N؄q?\hذ>m你vבZr~V`s%P.L!niaٚ (D@^~`W a|i{<7ܹUbj+Ҡ"HXܭсZٍ1 :m?lAŮܴVr}<3|eoZe!dlJn)*jjV9})h17g/eS"\~'#ZnV_ fN2?,xB]7 g Q)6}ؠ{6B؉$G=#}~qB>#zֺ*l᱇"`q3.xhvy.ȆD{JVXhGB[nݍh+ :rΘ֮)Yew;6uh%M E>&ʁ"-GlR~SM9iJi ̜ma7DG'ÚOu¥MqzYL e=|ݨ¡D8CntP'2po'gXJL>qsFj 2p D,P T2~]M(6ۓ;f "Ζءc2+C_Jf͊HS;FOq<ԍ}v1϶(Ix畆o{5Gk=NlReowb;=OY^ɩ?6qv?;:H߳ ~_\.($`g$PL~!Os{զ⤻e>ћu&dTx\Z=7%Xc7L,  B̍wk:;ܾN4g񔮪K&ND#aRoAP@S=CjU9,Q~ҁɥ^aPZNÅ}Twe:nx|׸G/`^fOݩ:{Njs*h la-N dψQpdMrTgɁeE4*c޾ZrhbU,6, B YI1 (.E+ے]61\™ ۻ;=MBIF=K.ֶp~SO~yѰ%%'Ywh.~}m"7:jCc#RhM.6iW2Ac@XH ]&_egv ߘՔV*j"q6xIvJJ̲n7}at}(⢚pE~LȸbpY%wO=ADC) ,HZ) tnh)Ϥ~X.aQY15C9 ]KlmY2mȌ]cQ]f&wxkIE^msxA>qA JU{q5p^lGuH X%_:;Rb=/l4яG|jӷce8 &iŘ빋)qyE1 KI5)zf^Í .##$j \mF!LN*!P~aLJ~bхI0<;ފi0O>:w >t XaO_}>w%{n,:9{SnSj[T}&j 9;zRjB2vi` 0JԖ]oYUL/yuyޠdPJ%pPCv@/a3׺o\(C&1i01sM3lY)Ɇ&# S@é` *#%}D.Ҕjc9ݝNݔYn|WxV7?N\gb'_GM/_yֳ< IN>캏:ժ ??<к˓T"5Hu~±"KF/PξHȐeFGMUJeYw`5#3dyuJA7ǣxnw~&KϪom|@ZJ;YPf,͠l%xig]K3Lڹ=\7<%Xy@PI>Ѱ`^ nUwm 0ih.r!7m& (q}#_D*e ٗn~O]ĸ܅1L:fog8"XFCc msy.¤jL}':@,qzS.W>t_35UMQhy#2Eל@e֩T_ԩ-ӞJdx-uG7|7Yspo<7}LBߡܣ[cqDYcÿ10 ޏa4aŰ*7 ꩾjz\J` F/Nz`cn>8K͎挌}[UI#y2[ֱc!J"*fݕy/ԕmӍv0ӜD]V9n AW6265pzFa̠*Ƞ_e^|iӍ<*eFX!Kݎ>Y}=I6i'[X()ic@ա'3 endstream endobj 162 0 obj << /Length1 1548 /Length2 7434 /Length3 0 /Length 8454 /Filter /FlateDecode >> stream xڍtTӾ(tג HwDz,R-J# ttwJHI|w|svyfޙyyhTP#;(TTW@n Ah ˎΠwmmR`{"@B@ HWp[:$jt?篿&3$(; nAE%DJ$ltvv8p-D0GKjj2@ l5t%:la}p:@]NAVs8?f; m`[Wf ((p88f`k}< f 6'. W;?= #4c5@m'B徶;ۺaf0sBpj유rRrM,^ (@P%4\N/}8`~fAww흠t@3` ٢ 5߿=?{m]bNi]i M?[SBpgs܂{4*`؟e5~LUO 0̀R  `[@^ Ve_u8Y[3A`럌{:9/"~l csf0'9A?9\f*0GzîkӬaP`_/^]Ҷٯ5{-qAhu-c'->pߞ'nNxL ?7p pN!p"S98\.e=-)?>P >3 <kU\ܙ}cJsݮ@^GƩ wKԤv&f%$_U ߍLz\ .ɦ;>Z8bf2Y FRb#?֞o*̹ivphӚwЯ8'8ii< K4^'/,twsdw'%p{E%02UF 3">zO|?I&2UEŎPU:ph(oJE|+$1V:h?H*R }iޟ2p+}S{m8] `D$tufᴣ{t4\PIpHFHM(GuB QE{zVNڮLHy>z`%IQ=Nq,\8a<]j:{ƍ(2SCH!ʹc /+˂OЂF9vdʸCc[HmGF4712NӎU|'Zf4 pG4eO7zҷ=r0(dfŘ=Za!.$;_ޏ=#Z0 oox3 vTAJwĽLpIGo9Th( w,ҕkQiYhnن(ͅ]6 ik0r !rZ~R֎Ȩf^*MnILZ[Z54nҾ`5* _ڕPBk;qZqV,".WrjyrmQz.u&;iʫy"-r9?O{J}I%?v6.-j:G58s`nKTHcm0>;'ڼ@s,3_Z7Ϛi複?Mzߌ,̫Dn#2\>h_Qw3{FK%x|@FK,]HINд ywӎR[axF*׿ ?>Q%PsտV=F<-oڽt\j!:_'$ /}'[KQvw;kRZe:50閽M$N(&Da5|6#1'6?8Jlo;͒­mX2 qGNŋn%ɾ+yUAWFӅx6]zRoG߱%4E U> njHxR6Rdzh5Mب76/V^ qlUʶAz.G/pP=jL-x6蓤5:%)Iu=%hlFyƍe(Xvg"sƥ+92eɰ^K![1=eE%wqf b2ph%YF!iqN1T'6Q^׽Xu&㑕\!z6fG A9f}C(eYQ7Yۆφi_xNV<켜N, 50+I0%HLY(ȸ|bi3+ʙ_5FO(ř4qL޽ک:Xx)j.FeT'Zc gE w2MbX'\SvlͶjƹ!͍%[҈&4 &CLkJu- %1yn#b 藥 }GH?oW P=n-\,PF -)b墇dY⊊[+i xwT2UYY^AΫ <>0Φ<ޙ\B{N5 [C,rza$w3 vlҟIҒѡ) DƐA;R/!I]\SBa spXO(}&NļP)>LbިWM)=MqUAKQ];t–7٘H #>ƬG߆8H>8yFLt,bQ [:Nn ɏLڝd@E+NNy*i~"M6*AŗZ?= <15d^:>~ [=S+}/Jt6lv`8eZUE5]x4^e$B像ty)C\Ƥ%VD,ȷ W5Cp\8.rsmPSxwCm2B![O—zHbYMҚv}/Nh$RDS"xV2Q! 1gYX{ӉJaz\qoY-dҤ;/g4V j&M,GsU\"fK_]iȑRݽM~~.HOojuç'<՗`ZMsTij\k@+ JjQf+@D<»_wCab孇^2]қu3>!|UR~yOEz]!ҦoZxZ~0ؕ@*//\v^enzQlۢbf?Dc'hLAwS*Z4]vTS5ȭA%+?pW A<+Θ6Qb^H<Gr3C.BHB*U2A_DhZfYzsk=eQqΉD*;rAkR1+;X|LP}MVV:hQf<( [E*|wWY [*^<9/g^#kU+@Rr?CnҵЂH0]] ]ӜRlܔŷqAwxT:so)j^uތYuX0 EGUޫIpkO䫂ٻE^X,b\Dg}K򰟃G'?n{;.d^Ma迺#{m+14>vĦ,WThm6 ri' Vag7l_ojepV}af~ O~\ ֿQr _2vY:/q!ʌ} K,YA@h1* >P ]MAz4&Y t0z #"$HgHvWDޣJ,m-Cll,\oSLEڝNA uH#ձMs_2utB{+o27bg^^`a+ԣ1P(ȏ==G{s~cnn^u%zxt:@ɕHe)5 mP,"߿Α6bh&| 0hXIO:)yu7VB5rԳoJEٟ]\i(OJv ~(|ۀ;^?Za((F$qTGaKn(+c?e>L[ONdsZ1Ǥ~AF8թNSrs:FO]U@(s q W[[W;敲"g"5{z V0iECXwCk98ܔAϒpJ^>RVX@riQD )gୃm_>d~+۲^'oQt scPtڕzYDu{-P2P4>VUw>ndf·~O&W/)otdT0'D~)w (Vpib\J+|gx91HX>`(${8q# nH+ju0Xۙ覯З}dh"{1lU”эrgw5Ea>Z3m#nڤZw#GK(֢5pk )TW24n/V>O4Hs<_$9Qw;o}a%+u{N49G@25gs$[0:U؀ m2$?Yw>UG\߳2>QPƚ+J0P{mIy(%/1;lZ, Dg|%80!ԽU7 '67wY#!|;c)氤a3=gٺ8͎Dlks? <=8q Mx; f@D7q_W @ٷѶekxsi,O㊷jk -ٻ5AZ(DxExG_P::!}S<0]C%|gFjF=sg#[vN+,NU <ۿ@;hj#!w?Y>vU im.+bҴz Ɨ0'=J:aU^櫼i ~7@^p3;I=DŲwREؙ- ~9׵1նiqUwxSvӱʯlް忓\i-d\8f[^S0 U+GUA`aaW~ 0v``!umjiE ڧ7VHg>=lƸ[|"F%6Zf20 nJ E6^wTB!)%J)ly=^VPpЧ=- d& @yp=e(pMH8q)S}fi3i)mыօ' ;$9Z0Ns#;a.i6D|'Y NVQ=|~ȨjK8}"ƳO_ ! (iwsG.;Eo&EeͨaߘC|VHj&Ԥ2+ 5hTvDJ$egh6jRM;3gUྣ4g/> ;?M\it ;fkѢt_; j%%eTER&3h'g[uC޳vzhӪ3CJ5tYɋюq~#KyGξmJ#尰rMǗamXGד`S8 N(B^hBuÙWZE[I-Ղ5(L()DV\ϱFf G{`őR.{on; =!x86oimyfn_-VPVJ.kS@QZ׾E > endstream endobj 164 0 obj << /Length1 1971 /Length2 11442 /Length3 0 /Length 12643 /Filter /FlateDecode >> stream xڍwuX[--R-XI X)^\P][)~}{O'=kfϚ=Ri2[BA2P 3; @RYSSB ƁBKv``(D$ tyZ݀`{quI:[.,`TKIW~R`KX:jCN yP~Y\lll|l<aa&Z-?v:|Vogszio[A`O SC&d,{UM]KME_%$of^v37Hj@2WbS'it߱TO nx#6n67Eh _ ?vuyeT@7UK@-M4kK v{,.64?=R:fll4gvOWSgm=o) Z5o< Dy:' fLK߽ `e@]\O|VP_ `k`rXE|Vɦ/FVzTZ( oS"V`?<2C]a8~87ae sJَil "Ɵ겆 K?T\F48=T̅,< /P\WdJ:U=(Et |5d"ۢj}Rh{[pWaao@@6Tf{x?-:g6JhuDhhcJZ#xЪIs{^XF%jYVm'Z@ G8 -f09'Y?S+hRef:סwq ?¶(6xS^?f%Ho80h"yCx2u!r;қ8Fc5ILrYD_Z_h(taVWMN=HJ+U QruUp9$/Wv|y[ZLYC|m"(E@,ħ8Jei~d׵}Lv9ֈ.&u皧FDguBcm'P{eSz Bw~gW^pĖc4I(wj}4舷'\KP1VkpJZܸ ԊH8 , ˵"YMيl;&)4>IwUHck!MED̨n>W~jARMr b3DjTFI_?߲ Ef#ݫJd/׎[5۝*Sm× bnE3|ŀ*Y?so|r38ҌDCɞX)S+7΃$<ۛPGdpl~D$ЯR& D ,ܫTڟ&ze f^u"=y_t1q8ŒueF2D9fLD [':v$wqz;xIeY=},Kvet5jΧMΞ5@mC:#nd1"(߉18-+}9u kDթ3XԪfXerC"TGk)iQQ8#}rmtrx X.9tG?Pи}ʏ1'(v@ H_isLKKiш))htVp> Kd~T׌nOg{w\8Gbc)qQsth$dBIwJ@wp,x`@z`WsUNDuư*"sO]7Gy8K0'~!{vUU75g:W7p멦~/@ZTigޗ<2P-ߐk>Alڿ42iQPC ʾAt)'2|eJ[U82ڎ +O&v0鶈R`˂7vFBL!WF>#[Ms<ԂEz_̥<#m_uG\ˆz )iM#Y4QK=ZaBf ң$(2? ]g,Yʁԩ&wlmǗnW I*i}0* O|1`W`*w%RuK5 Ce9~%jyht|*Gg-`Ӟ袌R!u9-@*>;7/X,YTCFRܸĞޔ7 6uVCvǺY8[l2ХK R Yb(cM]3;[ 5[{ֲ {"tE,Ue{o6rUFQv^3hi~XlSQmA\Bsg0pᵙ>!Չ NG9WLVyx[eH~UcBjpBAtw5"(#>UG)Q%*+6v 1fv~&3]qo܃-=kݤJ۫YLw6fV_}\BOGaᅵFe5щp̽mx[Tqb?\F.]LKt%17b0VM=q +a P :<_הo`*y\~ xVQ!_^'t}[(y+F3LE`ؾLcy:ײU'^I Abui퓈`Àoy=M"xIdK$'枋|h%}WٝUG"K $(\JM1  FX;K1 toYmpBW$Q o&m闦qE;~WjЎey B_rF}(G=%N9#~.O T!cc V:o]4ER5VY(s;gf o䶗zӤ?iWdY9 Aޏ,C@cHj WŚQߦŪŤH ܂ gXx*ݬ*O8ѡ,gPK /c1BÌM:yG`(MzOK{I'r0Q7OTZ],SKw5mP^ 7s2MW ]{Nݭ3ǰĠaR-QCnl`EVtܸwd\Ƙ"T9{ k1}]}w_t֯On"[J5Z|/$9* GERF?\NBVtXnxډ]gVi0"HE. gAdžԅ̓kYj$ JN!l)}Zz[NhmvJ @үq]Dy +U%D$Tp'~Bgǩi.W!q⏱p>˷A=@fJyЩ{b7taJ.b.]}4yf,ےLSo1 a3 ˇ2GtCC_iky; IjϴWΏnw_=᱘ݹz>gI%kB<#:s;?V{} ~~+ۥ. ʦ5<߲e/NDq«n;ќ4"C=o|̚uF&Rt·x=+*EzI0 F{:R 5hc$Nlyߔc0\eBsj%ch[%O-ɚul. SUkc/|lB Z`'Ni5LϢ)}FD ݻ梥a位 o9u/kI65&1N~MAۨ}*=\f:ks m~_fD-xhugG֕}+jvFny$_sw +[dqwY iUP+jTǝNj NX2)ZD >R5m/#mJT"ofBBIJ1jTuv !W}d~Vqؖ cTavPbӭPTA"R!8BA.Gxq#k ]Hn_/b7kn:kVw{ui 8dY`;FW"! ʖT]W_hd,H?tTavaӍ) k L}|) "<([w7PP0s,Vsy94)Ǩ]{+zڅ!bg]g(xz|^lb8 ?X_ōN౓?6lZT9$W U]PԢ~)K-Zb` ҅J:!Y?ޔU6#xj-ˑ̅pXg;l0o׋xJyseא-Wp)}2ԯոXfVpm1ᐻO7u+bR[֣΀Ozct7-8r̆gbvN+O<&׀A<#E NmןƸ I1l`]9*~cn|3ۡGj! ۺ}+4ooVɔN<C\Zb/QsEvdwT==Rm} *rS@nbҘ]jrqow!Rsپf!}kShg=s׽ kZ' etcFof }Ԕ#fNz%hz2ʅ*|sr [4Mo; @_&z݅"}#8pthx|_T@{~R3츝m6CP\|'@8uq[y.EJ2ȇq@'jgC2.w;C x,7k|akso^3%Ĕ3IѠ~|'8HN _GYPS 3',_0ka)yՉ Mgg {,%SHٮ_+2goJnfxJAƂE'T&cÅa 8Y 59g/Z`J)؜k3Z=`VD=*{!KAq4/%Q{-[0,A_Te%mg zb`z4}@Jzm'l$sBoU3]SH+X;bF;rZG+ѯ} U/;.g-7~iqQ a J~$u&gTܙ.FިVfLJ)ѓQ֊itEѨ wCVGp]4'"Tslvg1 *⭳{5i|k?|d ykZ3)-PV}dbMEd6"W鍻agR#?.i8'2S-{?Yy{q}~VSQ|2Pl43G6u|%))QϘ(]8xWEC(f tt"(̢X]u\ԡfE޸Hr"1(kwn?u߉t]R>N_D+tu8܌O)Ö#m?, +) Wx$S!] _ֻ?sdRw})Xpyx7t-/[ $aSjga%M nJ_7,Q%G#D # m5>X2"gzKMv3̗vRNr.~h7We}ykQ[p k;OK,}gQayQG0{"[+S1HvhU/N71rS՟(GОfE6Vwf,׳IZAi<8+2]?[󹄒*NV iu5VM%]e@B | [Y2AC;Q T(vd=GzHk4>>Kv E9W]zSՌt襎6-o~ՏU4~90F;DNWhn΃Q7h?$G,4"υ]19. f6"l,$Z )N"X^Qؚ~3 ~tUr_T55*!uz{(z^XEthp¶q)`Y ]c5(⎈ulf[Edbj{1s>F}C9BW;JAW_@|p> j(d+/Ӣ=j還DW;~Td)ͯkp>]gzᒧvo3Q&k.':R(3FF?;:3{uu˿S'O%;sf^F'A zs`B(r= TYސ).9 {j p+ʋ_cSqz_>|@U549uZĆىǽCX6(/_ڪ:9}/i$ +VF 󡎚F+_]5>L]]X\4Q4fEρ'^?f ߺW uOgJ|v\MunT'>ߛi[fB!`;=YvrY*a%591JZ7# 3$q.@q*f^hCTZ8wG٘J܊JU?=:;V6dmLJYo=d0z6緙H1|l?~_lw2vLјud喦ԟsK)rY0Ul~ʈ%xߙj5*f^phD?7שm{2i}ǀǁQaRA4<%JH trGOPKn  ޻GΥ4'3lRDgV +o@X@u:՜biJQHyƜQ1Y1N$=&wt5SK4hr|4D=Izꀃ^;8hմL7bȤH禿wԝ#?Cm/8ʈͬo4mݘH+?B՗nUj" W(w&YQF/ܙNk6VdM 죓"kf86/pu3Yjŝ=?^o.I"HMyᵛĨwQӫ[ŇZ!;RAn˃;>fh ǤW5IC| ؞b4+<a^68 s #>@^+#Vy}'qGE;JN^HߗH59wغu,_g}q#܅&4W~Z.uK\> stream xڍTT{Mw#!9tݝJ3CHH7҈tJIHItHJ7Ҽ<߷Y?{áfr9A8فBiUmmEN fthtz W7DiW;T&cTu<N>!N~!  U c  74:i'gW;4~-$A`Ks@hiv}++ n \=AV_MAc v[de @`K ڊ*ugoc Xpx lnilCl`@]N۝`eh74;[@  ' 0On`gw7v7ï&9~,JqwCU d =x\_{_C5b̡ xe1~l@^ (@ޖR8RrCsvrXCA/4?7sO `tXlѡb W7:ϿCfqmUP6TQ`Oj~l6.^ w s?v]/S?cϒ0;tzA \&WoIr0c10w;c_w*:AXdp_9t%$!6бf[v{4Or_4n2P t5Hܠ ݢN* tn\|sWWs4襆/V ᅥqr \~]W>^/M$ M\ PM|ͧ%h1C7A;M|z ڑ] Y&h-ү+aB/kBi hBktE:Fs@@hN ,?Zvi]N~OZP.3PrY=۬baCi v:'7&|T) b^ (NTq+^m:W sI^J|Vk)R!$;Xro f]w{22M-ZY-ѫgh` +*X3d6WVxLZo=}n~<ľUo|}q[N3'*nEkO|XK!_|C*kHҎirD1 w@xlfPp%q}B:S \i~\+\WDӖ3hMqEX6b),O(.+=49t{/~1pRKs5Tv 65N<ky'j@&Ltq 8[L}3.ΝÉ,1;zx]-[0S:Jڰa=(1f(\v׾[|?-v !e2%MʢݨzA{{S#X*i ߀}C*as015W mcu/һt7ڴ7 ś<R~mxV{m)(5S83)GSUGgp)l%D/ kl3Yj&TaQBh! !nE/` _IJK,ks PLO_6 p&wef>:C*L5>xz4idP0~/zB(ж_G_u5 KɿkgY41H+Mw^Ňɗzy '㕩zc|>!e~451Fwn[)F&!tԗB%ЗU2[y}Uc bO-CB"S*}f ԟ>Y*pu5ё j5 >T0M@s 1{!.}y@W,y9t9#Y&O%wMIE5mf6o4@GlؔΏHQ/;&KR)nw.1]r~n߾701^#$%ᅵ&eM \)v9NTLn ?n6Yˎ׍[ ;S?VC\vs,=-o5:)%cs?v^W. jgQcL,tU|:_*w*d>=A,4bC V'mJ\g$x蘿m[`1҉ʸ#]> NQ#!߽k{_-Y6CH8VMᬰ8Y}GQ/S=oD]4tkp_g- Zw?2GՊsukR?<1SKn |f0L3~hX2$HaRnkɗF^S ,H%/օ?iNUx́4oS0CM;5MZ.MG9^y ɣ"W! _@WѽGHUMG$V|$'yWr|wrci#益b&W|3cfrٳǦ4z"#`c%$e`IUK5Kx[kV]khDV$W=3wqj|JKۃ_m}zsWYo?Oϼ}X힍Mv g6!)1Y8J+l>eEۼQm4[GRF&*EMrg!S~żF3M@.XT e]m{}mB߁x޵6睓W!pFOK3?yG`-]=XҕM‚2ppۂФPv,O 4 |a69fLk~Dc8kwR;0ayQ!]OYFkMHwݭzu} )N* K۶c4? e OSVq!H*3o9b,27\ZcfbVXVLާ&Þ/D)z;h8癅~˯"?}͐͘:O+bW9ZMu$e|seZwWb#({1TBӾFLaw H %R48|%v^KBɽ9/~t `9Dh[^diu.򦮾^ 5Wf˭n] z5^GgŌ=ÓkN+|~蟧FN(2'8fڂx.9dj%>/E.<-.3(U\-yH q) tF~xg),iVx"OϕןJQ3) Lj>ҖsB'BS ֮4h4(.^CGz=PvXafZ}U:1ѧtR[[ j\U~G D\;HDŦIhl[V/XY["%{P4Fh&j>.w쑏M$1SIEf[;. Rַߗj s\M[;fy/#RLY Ȩ Nr,jrut_^>!$EJ4wSnmo0hb.eX,M ?*5!}+ME.Ps\ } fM~v20ͳQckkv1DO:XԅGښObS+Ywuٙg;,r#o|S|رX dfaG QLP xۂm2SkLuH)%VoGڡ\rZW]Tɣ_~TLVe9O\o@wcÑt.aoUfA$=aYv* цQ]; lЬ:j.i,3Xã!|(}MŐ }JFE3O*?0U=ndw(<\wGe=o%#@r&UI.Sg*Š]Wk+o>k3/[;2@ M=Cw>8瓄 q^O/ҤʗHגX1~/- +I v.+%}RWg+=ƞ>I-p<羪kC!P;f6@=I`k?"KQ޺ 45b 6|H4nٌkCy Uz|.<,Y=tya›8,Rƴg8ֱ= |j6V$n5@$A) rl!7G8̮7K} m[&{ile<Ҷ%b ?-` cXpXJ @Ȋ8BjjAH)|%yZ qB|[l{L3>5A݅Qf^1UrnlN#Mq}擐[mpkv= r)NZ}sAƷQtn_ @nX-Z>pn\f5ž Dh?sco@5C3Ku#nr_ע50j1ҕjI2Ek%B&oyy4+Y[qr-T)$NN' [^+D8ھ}I}~͑CAG/xZVjS\z=)+ˤ5!1>m4Nm D Ul,cן$2aduX>zLjKAjeу1<Eh?0<5g=,ۀC++&V}5θOvJBBsqޥQH=,?x ~{2D{T$c-L9ҁ+).Ja:+l(j'a,G}bxi.gXT>ueqYL&2z9 {srI.LU`_іD4\p/wV{U#\ҢQ_Mn7JiQ]t+[#eJ'C w Sᅅ":-ik0Yw(?$^jT||eCV t oUЈBDG3r jWtJ;10B6Ÿ]z  Vb}W ⽌*,u:>OHƨyH-c-(`?jJ~yXmFH'4-e$CX,Ͼ|PԃgCii'iR7p$`@kP 6d2 h. W>j\XrbAwV$|V(2#Tġk;F;3@Οxﰗ /ڦXF7? [`#eA+OWL(1 ~ZKUdݺ\} KO. +ĮxM3,7/HY4.΋3IΈ(i`کy|^nQ۾"rsE&KFy߻"+rbo ފ,NEKWS$xKtgx ߥn)79Va (M*sDUx,\7b`"w2oay5zK;Yf laJ]$=) ,:W63;2_N=v#%Ѭ3,DZbFH%R0P-:)`Kv+i,TF"e QRS<J_UiR Ҕ]#^Y`s8yPmXP!=:M@PZ#KtCzw%ZFHZfǰbbg8#F˜pV%sDb_w;v2[<Os` `O!mZ|)VŠL_1sFKb~5(囼MyAVxW}v1z}oov薛Q l3#QyÛ,Q3ԝA,i䴨r'oPH +$S7,%h.LA]v1@4ѷ!M]玸OK6 ebRd0&~𡨉nQ`=DVwHHET&_"3f |O@{}wt>^:CMG\i\5zJ^+Jo-6ɟ "©&0W(\L48/YT+ ^Ŭ Mzo ZAR+ hg7 fa̙6h?)h7&E*?E>`Qغp98'hq˷ LfJ3Q|~k8a9k%F83&:\ aaH7vKOrVKdjQ?? Qio_|e^_q(&Ճ/)]n= S"F&9Ɍa]S^/}i>01E¹x(D``=8% Wt.Lc'G$Z4ٝu!R HC('V^X$3TZǤ+\_ϖ UsyUB'}XiZُ8\a-ʀ#\#|s::no:l䉅He-⣩gh]3#3T29uteM*G/@3ɖ > 75:\Tѵi>t77`bJNrSOcm/3WOp| &sp?y4c7-n!O4Ⱦar%,fE:v vU@/6'mKi *>IRFEm"#ǡ@;JP}xKفH']lҶR%( W%'zꇄigϸ (TS8Vqd2~$^]'ٸ\wk\&l|ʺ&ϯ\5֭r:njƼ|CO%!p"V]f15F"Zr>)g2EQGq^:sGN5-4#n<†[e*8k8Ǫ=|C8, L;9Kï5XΞ?X' Au:ULEp(gbMpkGSl z͆ul#)B(~Y>ʒżl=Tdys h@0`s f\}L0eNP.\:\4m߃l\Ccw.o#nrEc/xLBV$ee'-3>3?C=Bg}3C>~#̦_<2|!LUqJNBEj]YWyւDu Aaꔪ4&{¨h]hbҳr3#|I lBIXn&k_I5HbhF6ا?j𨠐7Du5랷h #@_YP,BO9N.Qj˔X'TI {4x^Tyo7Q1dmDVDi*BFwTUR+xPR~lѮ㧱i.`;)(U-O|a{t0@Xi|@ jQ/#i4ktu8& |'8?Vq7,忀1Z ~sV T*P Ǩݣ[>d7$cIX`l ŧ0V>RЊ2+);8kUx}2kaqr~;`O sQS4_a0nx(\?ٮz1NY60p]b.6#=^:_liT}Zsk?0,}@}k91]]7TBf{ NŞf'lH?klt :|/e6] (C>pF 3q0֣,`G^oI@lmiM%#_4#qGR!1eG`PMq۾RNq2QXxr[ZR IX@df9nUYE=u>oU"mכ X+Ju^ qxN IzS8fIȸeNJ> stream xڍxTSۺ5"wkҥޛH !@HRIo)Qt Ҥ+"Ͻ㽑1o~m9#FN(G e ňo;1 G!e c650 G!:^HI1 Po S;E:($M̭;`uA )ew'F;" p()`0>>>"`wY_ǸLh7 kdg4bn =@hl Vj =ȿz6+ݟ_`Fj`|1B0@`o0v~h( ̇x=0h4kF_i۬tREC4PvD {#`p5G{A`&ۜPFRR@}!. y@;3x<0 8 !@4(?\@'8p:ÑΎ5Ca >2 DEt,5T/  ,&@ $@JJ#V CdjO|g.P߿~(`@gWݑ?,s0X裰Z@7tNp/jcX5(#Gk}NFp /e77 5B0( |XAܰw4]PYW A9$ #ƞ5v%aUMf3`(O_ JD 7e^4 /̢ r#:딙}7G VVb%0{UnrVG]Pp+g(y\q:dcVr|TˇR=[>_u̘x=G\a{ T6^fW g*qm߿;XHV&_#$e69WGET"==gAjt@{WjMoP=G,9fDIZRb\sp:g~Gӱ~OϬp}`8wN٦#?Z~+A핲"ę7EVFzWbb7kZ+PZ z)F]/v酥;IzQAVk4wLO{z&.gl_Jkƃ綀Ғ>o *\6ΐwtPd,Gs~ 6h[A^[i84}3[ _M(c-岺|RUxSF=}In@ׯA`Mr^zױ/~53a&ERfMcbyj0Mw[~bOC\Zn;{Uߚӳ'M{ϗ3M+ ҍwQ;[Fv Y> ԥNCZn|3pIlCF ݱQ4COֵޯm [ϳq*eiGRY6My:<#4b^xҰ!0!M6G/dBk*Hǘm6H"L~ƥQᭆ@SdoDh_?>>DMQ;Q>;tr`!-U=[^|6L&Zdnm7CyUJ怔wF(Gpf IdzY ?x16=}f7'nȖRJ4im͎?l/LzZiaa.ӔP.q;4XY(KREreNTX?pV`T88( zr`H`:CK6U<ZZo0PIjPمr0:jPLYm16 ȶ=V"=8r"R3]($F{ؗiI2p{x|)yJnv<e]V: Օ;!{ha[>_SU;,ćc>cT2~hVٟo:oo8Vnv3 6epIZfGO>%S_˓xq8f:6GOm|̯ ~%9׈]9+5,yOr'N+0CNh­X6Y<ИԐְKbr83n'6MUwhlj#A_Wf4l(ˣLM\f, /M ^ޜh)+f!ޥ4;rHqdI>'‚|&P2}˸4:V/+0]%gwM#VRnόzFƨ]c-Lm~tXyp5(*|FZN ~L,=_(F ZX +q[b`;K'9'z?qY6VZ(»bK~eLJ6Y~|x썳0cqmdoE.a%gF^/C <~Tr5?=u-l!D0z.}W;.xY0uq{gºs*='+ P)K1*!#VR gh6뮱o ֠u}{ DfczI,byyNJrp:$W?3~g fxyڟ6LHN Oc}#KYNHȭM""f$ПR*{ KniVLE=m?`_S[16Yn K*Zpi )YL9 e޷ڵ0ؑ-+0 I жnO,*8 Z.Y4/=~̺tfÌYGeoVҒ ;5xM,N޻/4Q98IXD&cw]NƾHle%ml  :{( tT :C9< EȎF93P(c+%qab&3 yݘu8jT_i<,^fLӭfqPF.U"j;/z=Y'y*Y9UT\DU4 FЬizu'e6_y;| hO{9dw\$u更ƻ4F ua 5`F:+ED|bLeBZ:z x`=eXi AGfsBUk ]`\͢ᢵXraqqޫ?Z /j)U-3F>`G>I5ZN+q| Y.Ii|3ʔ)=j㷄?uxtތ5>+@wu*sy&HHB̝?dvZ{Vtwoց{1Ø-Wȃ^m=mT Vx>3lo< Ӈ\]'%q?x%S ~K/SNICu ׫ J&hC4+&@lM^F?~ )\{l"֋mM ykTW6 p|̌muFj* =j~nٮ;)X1eVVe^QYk '!}$0CQyHKٓUk-VGPjZ`@Od6#.Zj GLG~[i5zTNN 5u\4ڴkF/Onlݸ3wz6Z}dr ] ՟^WK?&k(GC͞"՚1eVԉ"Frf/txS'bܼJh|Q𓯍XuTz`GډgV/D6__wr6_0Ģ^h#zi:^iٲ-G&o`z"ɐDi4BĪHa|rC[cKV6D`jn8 sSu)Pe(fyVRTщ;bUk)].9msKz('%8EI-f/^t'ZAuC ,8~ۣ8<(ƛ_(hKYFXg`AyPdqdIi(\e֎j8s^9ō/}J8S3N4H-rS! َbE{lg![bL+aA2Ƣ}% А|WCSuM(]6lcKo\Vݙ<ħǰTٌG30T*O[!Ŕ6{Gcju\=ҷРJtQeUPL5c~VFr5~<4K\08dC;R`ME?r]z;P+PX {PGCr}#jKֻ q܅']L-R%0;/xcFTpJmDOĨc9tc&F86'9!ypADr43#{7 }s&*H hY)זWнRs!z7lwIU:LJYj,\!}0,OO}2K`9 ˩؜5~ /;#dLuϞ湙gjSGWZ>GVU)t _Er|laY#=6@vȕŽo xOղ4]&!4kY.qus&owng> %('陈>nmQ兗 bM^ęs:t#^&"ֲ3֙" \OILތ8!kEuFl]ѩ C-Ħ(Q4¡:AŲSL b- CשƖmO N&0&i9vUY\NߕߓJfj<=}+,^шMU|Jj F4l=7kkL$mV7cb`vij齁/oEHm9/Ǿgۛx j΂" I.K3O>*<^7oiᷓj:$T~IYD ^e~:;tLXqS:6@ \$1\{`a1zz N I]w5B|RIaJq4TC-vt4HpX.Z" V}K!^P0$6B6!;ED mi klE endstream endobj 170 0 obj << /Length1 2510 /Length2 17279 /Length3 0 /Length 18759 /Filter /FlateDecode >> stream xڌP\b%CNpw.iqw\wwwww'hd}v߫jz|2s QRa65J8102DUUYl̬ '+@G ,DNo21C7Cy[ `ef/@dg)Dm@fNo+ژE;@26:N46NCAgdhhh`&@Cp9@ wCkߩ1STAQؚ::o+1x;"-PX?􀿋`adoD m mA6fS(!D01mhhhob24z3+tCG[hsrdtYΑ7[mLDm6N9ws-ml]mX?_.f7߈$SGoh rmnv@ml?+49[_.ۘYSH hr26k6#VgV #``af?2|=F/mwDqc[K 0tp0tg~$V'6bӛ -;orr8L"I0A<&3Ib0IAorƢ~cQX&zTL[j  7?CGc`l G?oDF ַ V@S9gqf?O5Ir«#'l#9FZًb`G[cwUB2GOow(.āD% Fsws FFfRcDt ~ sQdM9Cz+"ڳbnmM}T&/>=z^JpM5:'JV#0O4:øj+/'cIܲVqFh1'ژea\y %ռdB2+&Bӭ{w%ُ&dft5GGaPs5n0¤8kcuF'("zn"~B'Y6(蔧Q`< \VJ)'ꆎG*7uDߔl.V$ivk/ UFWiPR !h T;mx`<<*E\fwx?5<+8Kf4U1&p调^mg,)TyAkrPYpuխ*ƪ/vڠ'QcDY)az]b'|0[4VP>*r㇯kR#t 530v-4oĿ;r ڭDHlf>G3K%ͳ;#j&d줳8M $,Fԥ _'5m.B› SCyޓg]|-R>(>0ņU.Vt~$0,5c3~CީW7H5k }|:dT l>OmuJ.%I|q-Gi:7FBOY0!=>+F/1n+ +StSNJ4 Mh#F_UP!Gg~֭ychWȧ}6_Liരnޯc?LWcw/ L͎):@ĊDTQ5Ȯ/**T*n׋iZɉ]o;r_"Ȗt=/݉GKTAIC~8򔬅Կx~K5"1`SETrī>,ѽSVN-ҫx!>٤9"[9V[HAK "/YmL5;(~K  BoYGD8iEC괞Xu|,u!,,@&j=g $2 Ay=,H*ߕXI3/ K\ǖ* ˯RycȘl%"9āuAjؓ~ dCDPlA FIH~6䠶zכev{} @UDdLUҼ{[9]VtN€3GQ4.˓&π%4AGQheAe4jfO uP߄SP|c_E6sfȫfŗEnx${1'5C&q^$e^=:B'#6F h ǵY)gNmsrdKqmޓ7@Pgv+xIœdŦs ;ϲ-}8ަVBJW)2. F!7SI#'FWɶ]p"r0#{G\`D,/llW1͸(`^la: = ejpNUviw-0*gjHrʓyR%a_URSR(A]f!7>#"V9à>0N( Asn1L&_nbI[Cϝ7Cj2{sc6z?iX쀶8;ثUubzlUͼkބuE*#̌_Y~ \ь " IG:$wm$991kuEr2٧-R6Ƭ!nĚY,$ ;ﱜȈz7)T::)q@oȺ9M1"C l@߱bˍs^~8[ C!4Znr*ʡɻk0-@>°N;Qut$+T-S-̌2bM`yỷJ# );K> x/{}7uHⴭh oQ$*<a~O9{SH'iWC9@ߛ /pd'TQp֔GIϋ=M@!k8I)xضUΤp]I;3D: CyT&8wf#MԤS5Jl댷qSfN[mv#u47Nf:ԵW"YD%phUN\ pK9n?ެb2]nY9pޛ*~*nGWez3n$g;8)~v}ٮ:mL<|O#YcK%#X?Exqmr$/M%DELY"`y[~IݵɃOpjwi쉋}i`-fZࢮ]|LmcO ~j w?])D L1ݖuk Go\qFUzmfT}0S,:|v(BMddb įݔ^Կv'L{swG5ƒ c.q+W˱3x)`\hIƠoY(Hz4i>)6O9"9nJ3E*n᧡ l< /,XTW=ٲ[_F3B \&,R1c5Yj?PM+Gᛤ>!م~m8OgHgUJIwqPW\ 6N/1nƔ֚]0}20*xi&zѯ[KFVW}pҙ1%NC|'6/i iB1@Xԭ''fUq"!,,wqv'/ruquH?~*@9eQRɁQ qؒ3Uk4WA:hpMY[|/;M*Wn utߏUuX!J ]p"c =vwŠhO^RV5L,0L3,h{9,Чscf^@\/?6*+ChqwZ=+(#tb vgBHu|N^~H/kMr/rZ-J.iGe4d NN9lsRJMʼn,X;rAE|)LR֧MZWv.vփVh,}S9i#Qrʺ7j7џ!{lOiT[t@/L{o #x g869<*.@VgO_WxcmnwC:D/3w`5ؼc<$6o5`UFd6 3h|^1( t:Q{/?`f*7s fhx~<ӄd/ћ50+̡WLs2j=G7..\#a@d|99EνGTēsuu1ED-f7cޒi#4VJHyڔE ij≯a{C4U?/"8άWBv)a+pħbbEh#Aೖlvt7c(#%b9(8T(n@TqDVmsD[g)ZX{?M;nK=| MvKr Y`2(%SUc!nc f^V0ϐ:;NVB̸ >=0WqGEfx>c 6!KC9L $L}]o0mA+*\а1 TrN6Rl/wI37ж]IPYe4+{DG`ih<>}VYҶ.87z dR梁at!++iIOhh: RF=Dhx"mP-=} "9tnDÌ}rweg{Fq򲸉Fһ6jMflߺ#b; QKQvUl?T͆#u׵-*r_8*]QC(`y]"FH fKΊ?oa}BjPkxW=1<;+&5r iیDƨm 0ʿ_3_VUfRAn[+|e~ą3P \Y!Oְ[Jo]8+:ct9FEN, O?KR]Vֱ<.rx;5$$L,dt5pzfr׶!3Po>+|kԠ%H S8T;b=7nXjQ 昈c ZbNndjKZ SC- gƹMX7B{V!&rABߡG ?pu02uImE@I?$StCiI&elR[S4NJQ^e41k߃_B~ 5['mRtd1C1ô9q>OB& 1gz |V %c9]Ss.Mͺ5e9m9XZ[FW11 !]mϴ[(U#\7x֩I%$TPln}|:ӟPYG6kzɎ-üz<ck"N|dG+pl߼{N*?O}zh|cep|D(ۘ՜aVHCIw|x|b8(zMVth'J9Z/ԦW2Stj$KT])ߵg,wA^(!$~6Ε}Hl80 YXoVta*/0IDШ9I -k b3j @Jhqn~QWE(, (8E#CysKYv30fʒ rHpgpߋvj4f\2lHndeW\,>B۰q `eOknW+>2<ȁ!^3(fά 9|Lr4gQAVG®~&Z~BM曯ZtxD` /‰l#ڔaA'2D'`H;Ę ZE9zfVP3,@qz >yXn%ܥ.D65!'\rkBڬƴ1E1'ЊϡfвlPe-Ft=]Ys!m{}n[ݞ#B8]锆=Þ UT9xʆ)b  ,)ǹ%bUŚ^694/M'l&$83)4jEBy,5q 3?kAlު)}|jw +C. ~hۏkA{r6Gw%Հy]Kfo= jxdbU1יF͌mUnx4hz v@&B$Uc-\`w6UZ.4&c:hLvEͪ|Xq{rc肃[qtT9M܏wļjd_MYqQLP=pǶ+aӎ2`iKxAŎ ?rꂸ=@Pu 5s+kN~7̢3I3IUr%Qf+<9;.65{çtpFDHobJԞtϵ/x*<…NM {ø0&upG^ސZ+\Wbm$?, {e`ohv3p^e㗼D3q.ŢN+0E4~30 oG8h83(ܢQ!5דL{:O@eb̷%H.UiH &CՌH%-W CN)'nFG*JhG—T\-aG׽r\ ȉ_ϺIϮCߗm9B<9~FO0h&`4fѷۉ=_Pw^7ͮMS$;9 +X# ;K!l:NmQYMpp2ѹførw1NkR~g0lwSypv@]sEڨ'*K$Zpb_ sFpksVzTat6߁x*Bxcoiq6ǻR7)"JzvZ=M?D/ܱ y:0˖+F[W!UJT^ Xv/f3u> <ٛZ9p~f z/*/5a !=K Sğ%N/ E?$cGxt*yNbyk!1)NE:Wb́]Vfc:X'4C(٬,|e񜞴XS!]˸ل9TɰkEDb\<{|c8kB+<ͳ7QO} 4 ]ܖۯB pnmK|~*[_V2V7.O~1E9{iEj~u3bѻIǝ~ 7CWhPw1+*]mGP`f~”EgTS`Y~mDkQjš Y~(|wNO,1ܾgcǤV jmSx-?9 F,@83iD11-ԍe';UnɆ:T!ۣ/R5QZY345q6ޭ~g##$Xĭ2D`5z}ʕ%;Ö@/<[3;Tr~̯,TanutzlUFUhęO9s.f r-S-Vv*Y@&9"¢P(L4m)\2Q5%K90iZiǰ0*09g5&pw60K82_w-t$(p!ÜN9%* E{F&϶e9ķP4z&b0kگlH0{:eBtcIvrZ !f͵8qPciIRF0FnPg?W-2\[$~6k[-#F<1H2m^cb%WP)!\4>5xFSu%4W}uB9mP}bP{`WkW8=Nۤ 4-&Xg9Vi!yXE>Y~:^ђG%j S 2J{U;aUMQvI量9DRhىV譻"[Ɣ9qezTG#cwTFS+IzzdX6+)GXWu=B) {$[|3Btd#J& I˲|3W#2Γצ` P( &H*:mqx)%F0/R.]tQ: : ÆI;VԳCx}2J͂c~Ͷ"l֍.V-[5g*Mm~^.CeA,u)@mb;tʟX1v%ǜ˸Y&|~K{ -w ϥo8ϻxxm톜|Iܶ+c+1雷t=:HiWj "du nI߇ }?op?l,'X60:5tii֏dpZZ*{5b%" K”w类,duiɒӋU~?kL'(w)' ^(Cڡg>eR-P٘hI$J~7lq6P%U;%ЪB+M!ʬg"F9n {))s%J](֪>H"jEspmg G:rз>e xVd]L֖&dA-Ƌi}>d[!FAA}c.n>e~krR6S W9~ k"78&[  ;lж N@D+dYDRȤȬ1Mq6>r\`}׿f;P'AߛHᘃZVYF!舧o}&y(;9\iϗrJ {/tQRZ;-a,ΘKGWT79f f4j"]ccvha$,v7,!KVtTPjh :`S8Y(1,5ޑ h5g2fgdL5JY,>n.K C]H(wtYfO_(2AA'}l"&3Hp$(UWD$1kzA< g0t*c4ehіT ^Dۚd= wvOݑQ:N2{sX\hKIM$XK,$VM~9^BQ¡|9$P/>(Ja:~",sEKaʤRdHl7KW'9z0¡V~/~e}F5gtOc9y( X9DzY:UCRXRH7/BlclDZavz^+gךڒM뒭?aC찃O "x!W$2Wk1z8fL/U jk!?9-N+F(eNN7+_.,n۪)')){<S߈y lo5gBo~":Sw2H4,4i}V;D?7T]vNMVb%M!c9\{2+:${ouQZKhz&iՁcu{\f@iSkEW8*MA~lW NݽE>M`… 7Xɑ`7)2fzp:fq崸w ^=IkIq5‰XQ(lLz[bzIEIyY`80teGmo&d[V[E<A _2RzORYǭ;>P5'SfQ@gl~t/ A%*vCeJo {9ɱk~:q$~jeH#f_B74_[ٞ(fN`wO pESntO9X-ƩZp~Ό`Z6PB1<7vg3\}u$'(2x0#F 0;7r+?[6hy0WQU~ "t[θZl /bj,;k9u208oDaX'KG/s 0N~0D[yҎyп3if‡7Q3)5ؚTBg}kn/9 AQ06|I*uƑ3|$ؕZ9 1a%-{( (mXI!Zlq||~k&]99AV0nM|X?A͋kJ`Q;^| f*!eIi2 9Xւ}q\tJ}B(w$W>Q]P#*.GO/6\ۓiA}4@H=BDtԻ*WHXg6b0 e;ϨhΥG[Tͪjyϓl-εhAB{ŷ_?tO,s+#~KA=82Or:V)~wD@^a0(l g0sB[u{.,V[{ (eҐuƳHI+yz7fP'{*ȥ& DD]l( iLFd\ w$xkgȟs_UA qe칷SSOgㅇcN1 <»Gk/QP"U>-ИFAbB3A#ebA,ryZ&2Zi߮sb{9y 6i뷉 $*A-6w^nѦ ͰAR}'Xۥ}nGDO.bJ[oRdTQv{U *Mȁf:זVe% FԞ_C4"X8TeFQ=R*DN5Xhpog33tk LvTMz~ic כG\bCS*: 34)^c-θ~5)> , ΰNFlW HΘ']s>}-0- F^A2MAhz|7E_.y[tP(YOmzI7^r5՞tOyϔu4XPs_2q(WMS#Hڑ# ZxDZ&g2Q(u@CyDX9E'+-+X4dzDĢElmT D `7,bE-v:6d:eo$iS DQ:ST`(Y?a+]撌,[RbJAd"L+R 0m_bg%M<-/%s ΣZsM6lN+iXK Gґd㕿7;:bҙs`QU/jdoKl_?C>yA+gp&9ksgLu2tx􋑏",mRgvd{H܅078(`JG $r裏\zМ fy8i6O)( tTc;;nenyygzTB52w IYπtԊ/$g/6#@dFcf?>]~qFm]^<j1uAK[8Z(hmeT'rEc/gq Q2jf҈5yfsOn˂{as-vJ y팜樫@ޖjN"gA{=}v)Bm^?OԩmcC` gHl1c e'KX.ʂmx.䦨ޅzcEWR Ae@Y`7+S46!pNKUw~)[Gc(Z1'-wY5dI Laʯ*R/Jۭ\* ,|ewqn'`7ވ2픋qi|T'L55VxTO\ødݬo_% ,|^uu tw'\(02ߗ=gVfg~ @Mƍw6 9"ԶW9W^V5WKBf:E=G' j0n3B^ETJ RsO:~4d M,{\b+\j}CʽH#V7jp}) r͝Ia0S<^8V!_#qswP+1EmJ58M-_5j;:SO} TWcϱBkogT6Tn9Qo~F%j{ @\U6nTu}۱+޽(cu_3޻O-܎菻~ M\B:)ޝ6f=fA"ɣrRQ74\ǛTtyDc`Эm$ok֦賕M0ا]-Ij:m&LWQԢv4o w혴Ononlw6h(^$r3BVK$=1y_y`NIPJw}f>:[`'&..8i C߇WD"jn,eUn9.(%~Dƌn>( )>Y͓ 5 "7źdu$yo oNOjL)ǻU!&R:e5O`V=B]Sf$?*Qn(Uʧ&SLz\kB%URGu%YՎ]>6pl9ZE>;0` 7gYA<&j>v5&1!qr'U0GsM5%Y7>6Toh#޹ ݴ5)Vbhu x$!B 12h[G!ct8vK_Vܩ#Dh:ys-Hnl`ZKp#TvKHyABZMuT KҙU /!D~eeyV=Mvee۸%>)i^htj⊯ LHGT%zThU;LBj+s~zϿ]2D', K|w6 R5+ߒncf͖ALzPK †Hltpjwoˢr̪ a\R$;ڲEJA'U!"dLAs\~UABI*R;lA(m)˛J{Ɗ zqV޾3t{ lƟAN*!iD}a xEW!dpI5[Jeպ?cǯDvG/PWf-tmw:5yɍҼ$EN'/exAbރ;wv?ʍ endstream endobj 172 0 obj << /Length1 1675 /Length2 4226 /Length3 0 /Length 5258 /Filter /FlateDecode >> stream xڍw 4{?"!\KegDXf][D5%]d"ew{s9gy< ?9Hq@jaT "ZX@DB @ܐi/#qx-BH2 dhAznTJCe!@4`^(`(aH<-:C99Hy~;ġ04`#8#I07G! !L ``ooo1;^ sR( x΀y!aˀ51Z~08a8$@H4F q);`kc? D_@Š}>rw, B;(7$`e F!04ǐa^0́dpT: R5`PX^ r;|t̚h:&i@pҹ]+96X%eC9!  >L`E)bRX p$ D9"I?x <FP(@  $F:Ĥǡ|5o+_Vbn jj@$ -'; oO]#Y,.~E߱0$"4HA/G.?F_iߊ<܎B? =˂[Oi 1I@ sp iTNn$ A"LP7~- F`tႻD#4;ΨcC&.% p8/-$q))JFX !\Rw#G{xr2v(0tXq B0Ǡv:0n )o%%BDc{ yw AiL8Iznd{p XgPR^ XPTM=q8::5v郄Ӿ"\"꿖rz{%ond`!֨j­)g!viϾrt cV7_ol@#ήUuxo ^<'eM撶@us5j=qde%NSD4f6ïSki7K\j}(9j:sy"͡o ϵh}K ۬r6I(pX'~,4U>+ia"!t5q^q@TQ NTPfL_SQv%+x֍_zG,.JBCY\W/Ġ)*&vz}o4`/]^5qַy^M^hgJ*qm라w`ᒕL7}شzί <4p<©JoB2jW1! 1HFSzd76q]' KqxQ:_}<(33j9PfmSb++ 'xO&=|BvG"Ek4Lv d+DF7,9̹)}a>U*`9ȲkLIMqNjRrfJmK_|i%iϳm_r+8[~,a"8rG^.姝i<ʠsbJLL[ԣiOurX{,9bZwM3"krYŗ FTdMgT4*D,'VFWvoޅH 'h(9ݵgÐn0;S.u>Yr.;>ʊz#֨JU*[PߊH͉^!K+ ;bMEB^%W0ܻ"BԢoa9m2f|vXE_ڽMC#B kcdttTZ,L0--ۻPzJ(]^/;O_ܨ=/ƽoWUPm{?ߩL"^PT[~tbZj5g ۼ;O4^BaJSqF=+.Eb#cXSVOHd%\` n,f;=&i.H3U#Vf2Lӕso_*:Mֱo 6=˥j X>qj_[7ZbD鶡{jBĤW]'guȓ[J75n#yCr>TqW|͉CDzu>;+s$6ǃBJ⏜!znAfq́ l^ŕԫa& SgSJ$eD9n pP^?#FE|W:p])ߝ,W:o#ؓjLN'pq2\$Wdgөv0 iT{9Q@lY51?cV8[{-Yvߔ2\9%Ҕ0+G,E' ֆk^w^^!'iZwՎ) 0j+zSPD5ȴ2(eYv~^vSQQs:z#Ղ߫[{.Ⱥ|#&U#藛N  ֮TaI~?zxq, :<`ggnΘipT=+qL{eTQ؝O|}M8VQ (+ Ҥ,OJlgPl K*No`kkEM nX>͗͸xHs=RnG+Yr{>]1$[M6vڣ}݄>B'Mm$/r:CJ;#ti E۰Kp]U_f~dҷ^gE2=]䌰7^qٴBwC߫](l(RVDD:$\ek ŗu,m?ߓe!gbӛ+6ދV!I` fv|ȇМS /?XˬOU+aL)PiB#wݨO|# ¸Md,gGD/YUw˞4&_nNkz\4VzBRbf~Bp Y.;jL۩[ JjfWTZ.VLuZXf!=m:S6s2l*A&I ):}c-TDNBRуs%\@׹xʴ.N5o÷sBcxs`uZMt7.)r汛¦ݾa#pGq/Xͱ.$+RrG1,Hd1kV?>`=<>NpM0],N<5m#4SLvÍ|eJ֨YGCuiO:: ɿ)J.12N|MY*GO?]"9lEhϫ6 ^sg *]UV=|$eZO\NVqT6X%HP Sq*WaIȜ%> & 9_gI [MwH2(cU7l[R*yٵ+dͿrEU6 (Ώ,}Ə%B ڼK"Ä^X듚۝I"3SPc\0hh;o,W^:PiUE;'f n[km8IpO;r.pq&{~ S̼jhk05xK*?dQܾܖ]Pfn]~I3`.a`>Ͻ/ɦDj=dg7 siށVC]e;,Lbg4`Jf U\ $iP Q0BJrͣdl顠njf/ueF)3,:SՇiVCͬJAA\*2wdHUDP;͝"h֗1n@_ʐm)~MS4K$6h~#zn[Wϡ.8BF?n8Qc-AnNMxL`ZP#/4P(DK zmNAO_c#_0S<# 겗l/QL~.cOj'oH%CHF~!EA'y"@%fP}7( 0`#r^eP+PfwBi {3ԹPeE|ҭ?> stream xmysgn6~i۶mL۶m۶mO۶m[ӸssukuLBeچK4De&r<&'&]c3<.{0]0gBYRNJd?m"NH?E8,'fM?J%3iya7[֠J5& %m, kjPBC)d-]?w_-aw1aZ-qG fϝ"qظRSc6uf)A $Ya{pmsa3𕱲(^|m\C~-rݛ&e D.Om@ҼO[>2WGHQp%/=F$P_Yc GIf}xl}Q;=\[!z?ߒUrCwZ뗻St14<~;*S,> \'H:;ND1OL1ЎRvJOon69IP>h!M0@śd #SPb|1)b#0qP?݌JZz{&2ʐ"-rjg#&tQAf!Kh@ȚjbOpgNh;?°y~Z{vJ1nIȡzrKte]'@_Am~0{Ƅ̪++; ೎ْFh g߻g\ue4q0-l2H^QjSڊ@d0Z4@ce$Nh'T #OvqG*hFD%OU)f?u%U,@="l_=UtUP-tU)[땴=.3wB@:t:ufgpBpC.5 Y”D!ݓ Vǃy7iaԥZd~S}rBȼDh@|J)vn$Z˙h![,NvQ~&!dQqWNr1nBf?eDAqB EFj|^mٶvԥy^јA\R"CprgDfg&ZuqYqn 5r&n2rCͫVyL)U:{}|Kô8?^ħ DQ*(61dzo!q_=tXR'|bL -;dQhd0SʪiZO6.F]>sc&wBCciZI<iLu E|I IFy QidCKy25?צ:7|KR 5Mx{~HFɶs༧S9zz^y⣽Ժҽ8ela6ʃZnJl'/ 1'@IKǩ@LL-IWx6!uOp"ǝF䊯A3ʓt%9Y3 N.Ѳ+?;F<%1};XAGZa2' O"s1v~oN9Mѯ+S &.^|AkBn "K'%1d{a3(s͑cqhb,'Xl@2k?Xz %ΰ\c|4ś'/4ZDL40*DFyifoÒƌ9~%<`1)|mˀa֒N~JhѽHY#B -xǔXeT@\3^ {K/ X{b(yK8Iۗ5k@"6U gJVS]fA%(R$cR$!ѐULB#i۝|-6 [xL}y;uzK8Rlt{T_5ethv557K.Rɳ@P6#'QpJxhJ G_LjEZQv ;gsGF'U߸*UTƁPcHy1>zNH(׬oڵzn)u&QG@ T>\`[as{|BJq/bBK'ƥ*`_j/blvY2%}35K[joR% l CmNe2&^PkEQc!#5%(S?# #@$EV;gE`\lYԥCmhR~`K!ĄE b){Y4(k^C\ѡ b&t߱m"i )Zz ^k옐W+㪥iHE7& o~Gg?)D) %ȊgF ɘ zlTц&8Fj1qfVzhoiPgdQ3TI z1H3kȏs*o),PxBbwv_8`@!1mR4g)U6w VfIvnՓOxFeab^zޚv".&}#_Q|0<2*1!z-k#nG睨`b6CAqz9qfiJb5>R`_$clS#%F%u.S~v_GS,=4GA=a8/Yɵ#e9a.OXNpJ|Y9hnV=&2=ejcE?*KE9l7ȟ qJ:#ĦDrmF6%wwDkY5&oZb ]3PYmXztQT oZPn [cE0;pY`0[d ?aXۮ"jtI'TL@I nFP(˹^Y=^CªӶivGE >ur5;M#)ʽ=SZ3[<RhyctoXl X-!-ᥢzіcIB; 8cNnNk>rT +SfX@{6ͭTtC,OhP=$=߷_OI̩A\*=YurHFV`84{lI{)^:`ږ,:ʳ>x=\v bMpPGJtjLK`I='[vU>2W:5Lip^1d/UJN Ab_H\hCm> i^)K!O5 TkFKjr'i?}&}}2-*ztQNq V$<6+{gnA>꼫XY2藜qa* L[bj@b$O_diEZl7lꔎ?|;e _]ggJ*_po񒝯1^esVh:FQzzR8z) *waCm/ʗpBګ)'qt2>y-HJ%uEHX|픫Om%5ܗ| 5sdkMz wĥr`2l҈,jK @.Ïu4Og'Y*jnmvɫӶJp  @ϼիU0Uvof] dlFrEm&_1Yٔ`Wyks\(:TItt|O7}tu@ \p=+w2ݯ xp]pDAxs?x7]tr>"FfF՞\$ 'M4jѕz&G4KZ ҖqY;Eb1W٪{fqKVn7`O?KӢUwjFft _e[f?Doxxv1b.0{oICmpt@D஧Bc U ~8FT-Ϣ DJ{w0g]~JՊjR,O JH Ė]&:6'y=χ,AcHjA_b1hFiFLO6 /p^E1ZLrڐwR&obt„ʮի_)Ha۸hT=5X6٩{h90DH vgrI()^AOXǔSWRUt:vHK^u߻DGiL\6Gy3x'K7N4֝ pf2l* `x0k^J8)E >U#:C+6NXƞoZAGTD{qH LPnjȄw9Ÿ57$|h |%S/c}ΞW[%OǝX6#.-w{FRdHISۆ ~׭Qx&RD+p[Sd3_ˣٽ\(~Z5Uq"\ ԙuM_u?r[$IH뤮Z K1+M|;n!` ~Y\K󹐷}SXq㵞ݷʍtF}ȋāmOsnO7 T[inGq,yk}} %jG2b>.yoA}X nE7ۨP0UN%f%3+O8|N RgM?D "<"F7 lH~`P"is7ʭ:IZ3{>C eȇ{Ch/fs]]QDtX_D)`Z?Rb`j}EYNVdVf[f4Uy㼓\nVDx5zb\F HETY7%B-=63Ǚ[ay<ڿE&b1`U10᥺+J@*CeUᏢPK:"|\)tj'ӑ º ~rҵ5o~ xw2{Z_q aj%υ)uAf+rDNjx7ӫQD9bWB=\4i}PnOf];-^P/?[y5 B,53fq3!d|64D˲-`QOiJ2|WsI'N)mq΁:|#2f>&g6ʦv(ߢ\[h! 2_i5[yy Ne뫽y/b8P #m,+ g!uhw&azz~rej_mODK.V OxL_qāBozCqpOT' E r&kB<$# ƣQ+;#QLV_cx6Aq^ @RR ߱lUWD/uctsnPt#kkzTgX) ryZȠI{OU@,C k]c+UJD>B9edEG$ puTmB;vđ8OGZ/vG¡{KԖ렶GRA-6i-xՈY{oqre3H5zbOT~ ] !Gf=€ИBG*eօٴ+ OGl{ly󎇝2BP/)W 0iEb5-Ŏ:yϝe2O25/54KWp9M&!7 jP~p ow6~T Au6y#17x:SU^P HOO/EO4/i[_2flHB_b $2P{S^(p[3FG8nkz~<Ϋ^pnbru1HE;`60&zO 3#`I83]ͫѻXa1]=OFerF.zb{dNierWzX2$ Tjwgτ3"M w̃KSޠ!=ST[MʐދY+Ӑ կ[ MQs!>ѯ,kÉ .fWh28av-Ԡ.[QO* ;oCW7iF eOy .G15jG1fbڸ5"KW^{{N-) zj! !o}7ƥƽ;$>`7ۅ5/Gy.ʡbtwX(^dAÌp1,:d/2MQk(bXOCtȥs2XLDT.V1sM~(9'`mb u3aU߹$asZF_lqXtTkx0ӱrr&.hkh1ؓ@-+r-#%y_UN| \mZNrr6nA-֧Pa_ɧՄ;gkVY<*zZ~Mk G6 #Nz㲅'2gU~Rf?`W34Ak,$f0v8=kpv *rH?֑(XaV>(254fQuZn=Wu]"*1_]mͱ:dR: G_MЅ/X{ _u-niC"Ѱ}[Ҥ>€iy裆Ke: ZaN:Q*Qy_+OufNrI,[@<v;יNudC #zo#Pq|D6[giޖ]':&c9 2o0B01at3&O3(t*Y~ @z9:R'}83ݩYj\f]3ƁS%r RŻO!F9 FׯF+hՏOHlr"!JSO$>p~(?)ȽbXIy^ DdS[AI;0Y{ yY06פ>Cd􀅴lJluE(m&qO,)ҷ}`bՂ_K[b!4m?cיb}yS2hBEȧlv1eE0+7xI?-V[8^1³:(RjjW9rjdBMAUw!]ylBzQ_IlYtP~.%6/UQ?yw!BlCˁEqQRIDCQ+DBFu*Ontߛf Kٵ#2tC穚m7qJCdse2CZE^L, rtm,k'&&pē"4 }"1Ӫ?u"G >  ʓ.w3rQH.s۽x$M 6M'!UYC湃I`dm.fC ЛSd8#ϥيNd;PD_ s E"Ḅ\P+gxq'V.&B\V^GL{D̈́$H;fJ JZo.vgFubeY5cF7sBp Sx4]2I"If9 Pɻ ppX:K ]?Y/OfE P8eC/G: ԩ;*!zKF5EZLğlErl.ߚ(5 q"% {:҅R@uqN3T[f۵$Ө!ۅ{ľSʃ9DQqs'~WƀH&j}.Ie9JU+<E/Xh]y-b1-:,HeG A7("},Dw$3s(L~ ?ЊkP~Uf)6~MPiH #mL<,sRQS`msTTUAM=Bhb⌰EBN=EQXtH6m*LZ`Hɯ;p& i/`UF,,itiV* y*մMCdxy`"iwze[٠ŵR r$~^di|, ܥ`~k2]f)O48#ֿ*OhgShMAiQP% Kn9d\ V:ZvJT͞G;亅IV {. {J)gP_&sFT1%%xa+bM 7# VBdUWyO3 %hO[Mu\ (dMˊy׏9Aٯ6 Jn7ZvV.,1(>Q~w1-]h!Ok:cQwIvҧ4@&a3YS-&9o]Xgrf;{"YnvAxQw˹ԍȨ=xXG|L.r\<ם?Da^VeɟfA{Q)˻ki<vz<2|V:'++UǢyj3*ئ3]f9qtK>w U.@&G9&'C~Æg)\3DGBj/e9]a1X~PxwPkJ)[Ҕ[h.o"$[l^~VBvgY0kX);Ga zN19Z8JQjTw^ZTڥ`#YͰ5<8H0]1, sg}8 Gq|4rzbc Aף3l-g81t- SIq]^.ΈXϣCGKKhҭG͹5\vo C`vt s-+5^$3 LˑQq:*1klyZf5!LwbgUڀ:$V-tyT"Іf%w)9{LxDAbƣjʷ֐7z=LI/ex7Gf de@YP)*O ( _Z},i3+خDlM`҆(Bݪ;.Acu&ryd_ƊEuPJ{ '&S۩xG(sй.>wm$ߣm吗W? (v>IP5I-Klb%d)r)RhJE7Hʑ}X?<~ ,k8TTgpOk/Vnt=HMs-09|7} s^$n58T$QL.?v_ d2MΪ DІTM9J';"ޯMW#hu/pU߷6QU%R6ǁ^8h]:ѸpiDQ:VNr_XH(V?",0}݆pK$wQ6>:iW`'TuNl+ݻh((b'Ӽ a\E\0O(HXܐ E.Gڢ,>:ˊ) CMԤcrJɅ+XSOycҐ5/D8 d{끧2kvHɤwaaoldݹj7Hv+LӋoshS%d<dbUd5ݷ#\ G#F0zC\z^u,jYe(qQJqѽ@mEM(ߎvR17lԲ;K; pu]gR ڲ,?Ղmc!#9\QnIY rĒ~, zK4'g,p+k d^w4A ljpьw<i[ endstream endobj 187 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.18)/Keywords() /CreationDate (D:20190926165831+02'00') /ModDate (D:20190926165831+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) kpathsea version 6.2.3) >> endobj 144 0 obj << /Type /ObjStm /N 62 /First 524 /Length 2839 /Filter /FlateDecode >> stream xZYo9~ׯc 'w[@_S.0An[ Bka#a&.M.h}7EpQM<(-HK-KQFC*AZ$tL#Z:Yg4BCDL0 $ O׼RX.Ʊ1M|#!DXo,En$}jE)>hVKI}j/s`t9.Iy_' w3GO~ſޜ>?)3p}އҰ+"? E󢼹_QM fXgUqtE ޱ~̟o)9{_&j)aq]?G!YoȯuلIѫ o/{~ߛ#J$򾘔Φl\!)_ߴ+^NWFb2'_d>)$i:8?#l_@^rY6ޓ{O̝CoW#{=y_;vR/.p:Mo[LZ_b 2>QqGm|LGc>jQa Z Zr>flpp9j βYn9YAy9r r=Z)Vv gUoRt bOeUtn}W{b7^wl ʫ ltP諱AI3)\|]ޕUK˶D0=m!+ѷi?{#MY=$}.XGD*V5Э&`20dúŻcQe]묶F9zu<.l @Wx/ʫ5XV7ӭWϡ(ĕ2 _Ary"2Dg5`j>*8׌^>?KOY?1mj # ?[zpMta8dh!<oƃC}1: 4$ endstream endobj 188 0 obj << /Type /XRef /Index [0 189] /Size 189 /W [1 3 1] /Root 186 0 R /Info 187 0 R /ID [<6A7C448AEF1CCA1D50F2DE15B060D5AB> <6A7C448AEF1CCA1D50F2DE15B060D5AB>] /Length 467 /Filter /FlateDecode >> stream x%NUA὏ͥ""4"U.6[&Μ|D_q̑/'_vvթsj?EDA5P%p=.crQpi nЛ`A8S;EI6h2t@'tA7@/a#0p`Q0%?Ģ*XY^WNe4y>R>y(Y|Yd>= options(prompt=" ", continue = " ", width=75) library(tinytest) @ \subsection*{Introduction} This document provides a number of real-life examples on how \pkg{tinytest} is used by other packages. The examples aim to illustrate the purpose of testing functions and serve as a complement to the technical documentation and the `using \code{tinytest}' vignette. There is a section for each function. Each section starts with a short example that demonstrates the core purpose of the function. Next, one or more examples from packages that are published on CRAN are shown and explained. Sometimes a few lines of code were modified or deleted for brevity. This is indicated with comment between square brackets, e.g. <>= ## [this is an extra comment, only for this vignette] @ This document is probably not interesting to read front-to-back. It is more aimed to browse once in a while to get an idea on how \pkg{tinytest} can be used in practice. Package authors are invited to contribute new use cases so new users can learn from them. Please contact the author of this package either by email or via the \href{http://github.com/markvanderloo/tinytest}{github repository}. \newpage{} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{\code{expect\_equal}} R objects are described by the data they contain and the attributes attached to them. For example, in the vector \code{c(x=1,y=2)}, the data consist of the numbers \code{1} and \code{2} (in that order) and there is a single attribute called \code{names}, consisting of the two strings \code{"x"} and \code{"y"} (in that order). The \code{expect\_equal} function tests whether both the data and the attributes of two objects are the same. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_equal(1,1) expect_equal(1, c(x=1)) @ Numbers do not have to be exactly the same to be equal (by default). <<>>= 0.9-0.7-0.2 expect_equal(0.9-0.7-0.2,0) expect_equal(0.9-0.7-0.2,0, tolerance=0) @ <>= options(prompt=" ", continue = " ", width=75) @ Here is an example from the \pkg{stringdist} package. This package implements various methods to determine how different two strings are. In this test, we check one aspect of the `optimal string alignment' algorithm. In particular, we test if it correctly counts the switch of two adjacent characters as a single operation. <>= expect_equal(stringdist("ab", "ba", method="osa"), 1) @ The \pkg{benchr} package is a package to time R code, and it uses \code{expect\_equal} to extensively check the outputs. Here are a few examples. <>= b <- benchr::benchmark(1 + 1, 2 + 2) m <- mean(b) expect_equal(class(m), c("summaryBenchmark", "data.frame")) expect_equal(dim(m), c(2L, 7L)) expect_equal(names(m), c("expr", "n.eval", "mean", "trimmed", "lw.ci", "up.ci", "relative")) expect_equal(class(m$expr), "factor") expect_equal(levels(m$expr), c("1 + 1", "2 + 2")) expect_true(all(sapply(m[-1], is.numeric))) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_equivalent}} This function ignores the attributes when comparing two R objects. Two objects are equivalent when their data are the same. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_equivalent(1,1) expect_equivalent(1, c(x=1)) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{validate} package offers functions to define restrictions on data, and then confront the data with them. The function \code{values} extracts the boolean results in the form of a matrix with specific row- and column names. In the example below we are only interested in testing whether the \emph{contents} of the matrix is computed correctly. <>= v <- validator(x > 0) d <- data.frame(x=c(1,-1,NA)) expect_equivalent(values(confront(d,v)), matrix(c(TRUE,FALSE,NA)) ) @ The \pkg{anytime} package translates text data into data/time format (\code{Date} or \code{POSIXct}). Here, a test is performed to equivalence, to ignore the timezone label that is attached by anytime but not by \code{as.Date}. <>= refD <- as.Date("2016-01-01")+0:2 expect_equivalent(refD, anydate(20160101L + 0:2)) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_identical}} This is the most strict test for equality. The best way to think about this is that two objects must be byte-by-byte indistinguishable in order to be identical. The differences can be subtle, as shown below. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= La <- list(x=1); Lb <- list(x=1) expect_identical(La, Lb) a <- new.env() a$x <- 1 b <- new.env() b$x <- 1 expect_identical(a,b) @ Here, \code{La} and \code{Lb} are indistinguishable from R's point of view. They only differ in their location in memory. The environments \code{a} and \code{b} \emph{are} distinguishable since they contain an explicit identifier which make them unique. <<>>= print(a) print(b) @ Another difference with \code{expect\_equal} and \code{expect\_equivalent} is that \code{expect\_identical} does not allow any tolerance for numerical differences. <>= options(prompt=" ", continue = " ", width=75) @ The \code{stringdistmatrix} function of \pkg{stringdist} computes a matrix of string dissimilarity measures between all elements of a character vector. Below, it is tested whether the argument \code{useNames="none"} and the legacy (deprecated) argument \code{useName=FALSE}. <>= a <- c(k1 = "aap",k2="noot") expect_identical(stringdistmatrix(a,useNames="none") , stringdistmatrix(a,useNames=FALSE)) @ The \pkg{wand} package can retrieve MIME types for files and directories. This means there are many cases to test. In this particular package this is done by creating two lists, one with input and one with expected results. The tests are then performed as follows: <>= list( ## [long list of results removed for brevity] ) -> results fils <- list.files(system.file("extdat", package="wand"), full.names=TRUE) tst <- lapply(fils, get_content_type) names(tst) <- basename(fils) for(n in names(tst)) expect_identical(results[[n]], tst[[n]]) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_null}} The result of an operation should be \code{NULL}. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_null(iris$hihi) expect_null(iris$Species) @ <>= options(prompt=" ", continue = " ", width=75) @ This function is new in version 0.9.7 and not used in any depending packages yet. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_true}, \code{expect\_false}} The result of an operation should be precisely \code{TRUE} or \code{FALSE}. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_true(1 == 1) expect_false(1 == 2) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{anytime} package converts many types of strings to date/time objects (\code{POSIXct} or \code{Date}). Here is a part of it's \pkg{tinytest} test suite. <>== ## Datetime: factor and ordered (#44) refD <- as.Date("2016-09-01") expect_true(refD == anydate(as.factor("2016-09-01"))) expect_true(refD == anydate(as.ordered("2016-09-01"))) expect_true(refD == utcdate(as.factor("2016-09-01"))) expect_true(refD == utcdate(as.ordered("2016-09-01"))) @ Note that \code{==} used here has subtly different behavior from \code{all.equal} used by \code{expect\_equal}. In the above case, \code{==} does not compare time zone data, which is not added by \code{as.Date} but is added by \code{anytime}. This means that for example <>= expect_equal(anydate(as.factor("2016-09-01")), refD) @ would fail. The \pkg{ulid} package uses \code{expect\_true} to verify the type of a result. <>= x <- ULIDgenerate(20) expect_true(is.character(x)) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_message}} Expect that a message is emitted. Optionally you can specify a regular expression that the message must match. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_message(message("hihi")) expect_message(message("hihi"), pattern = "hi") expect_message(message("hihi"), pattern= "ha") expect_message(print("hihi")) @ <>= options(prompt=" ", continue = " ", width=75) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_warning}} Expect that a warning is emitted. Optionally you can specify a regular expression that the warning must match. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_warning(warning("hihi")) expect_warning(warning("hihi"), pattern = "hi") expect_warning(warning("hihi"), pattern= "ha") expect_warning(1+1) @ <>= options(prompt=" ", continue = " ", width=75) @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_error}} Expect that an error is emitted. Optionally you can specify a regular expression that the error must match. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_error(stop("hihi")) expect_error(stop("hihi"), pattern = "hi") expect_error(stop("hihi"), pattern= "ha") expect_error(print("hoho")) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{ChemoSpec2D} package implements exploratory methods for 2D-spectrometry data. Scaled data has negative values, so one cannot take the logarithm. The function \code{centscaleSpectra2D} must eject an error in such cases and this is tested as follows. <>= # Check that log and centering cannot be combined expect_error( centscaleSpectra2D(tiny, center = TRUE, scale = "log"), "Cannot take log of centered data") @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{expect\_silent}} Sometimes a test is only run to check that the code does not crash. This function tests that no warnings or errors are emitted when evaluating it's argument. <>= options(prompt="R> ", continue = " ", width=75) @ <<>>= expect_silent(print(10)) expect_silent(stop("haha")) @ <>= options(prompt=" ", continue = " ", width=75) @ The \pkg{validate} package defines an object called a \code{validation}, which is the result of confronting a dataset with one or more data quality restrictions in the form of rules. A \pkg{validation} object can be plotted, but this would crash with an error in a certain edge case. Here is a test that was added in response to a reported issue. <>= data <- data.frame(A = 1) rule <- validator(A > 0) cf <- confront(data, rule) expect_silent(plot(rule)) expect_silent(plot(cf)) @ The \pkg{lumberjack} package creats log files that track changes in data. In one test it is first tested whether a file has been generated, next it is tested whether it can be read properly. This is also an example of programming over test results, since the file is deleted if it exists. <>= run("runs/multiple_loggers.R") simple_ok <- expect_true(file.exists("runs/simple_log.csv")) expect_silent(read.csv("runs/simple_log.csv")) if (simple_ok) unlink("runs/simple_log.csv") @ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newpage{} \section{\code{ignore}} Ignore allows you to not record the result of a test. It is not used very often. Its use is probably almost exclusive to \pkg{tinytest} where it is used while testing the expectation functions. The following result is not recorded (note placement of brackets!) <>= ignore(expect_equal)(1+1, 2) @ The \pkg{digest} package computes hashes of R objects. It uses \code{ignore} in one of it's files. <>= mantissa <- gsub(" [0-9]*$", "", x.hex) ignore(expect_true)(all( sapply( head(seq_along(mantissa), -1), function(i){ all( grepl( paste0("^", mantissa[i], ".*"), tail(mantissa, -i) ) ) } ) )) @ \newpage \begin{thebibliography}{5} \bibitem{anytime} \href{https://cran.r-project.org/package=anytime}{anytime} D. Eddelbuettel (2019) \emph{Anything to `POSIXct' or `Date' Converter}. R package version 0.3.3.5 \bibitem{benchr} \href{https://CRAN.R-project.org/package=RUnit}{benchr} Arttem Klevtsov (2019) \emph{High Precise Measurement of R Expressions Execution Time}. R package version 0.2.3-1. \bibitem{ChemoSpec2D} \href{https://cran.r-project.org/package=cyclocomp}{ChemoSpec2D} B.A. Hanson (2019) \emph{Exploratory Chemometrics for 2D Spectroscopy} R package version 0.3.166 \bibitem{digest} \href{https://cran.r-project.org/package=digest}{digest} D. Eddelbuettel (2019) \emph{Create Compact Hash Digests of R Objects} R package version 0.6.20 \bibitem{stringdist} \href{https://cran.r-project.org/package=stringr}{stringdist} M. van der Loo (2014). \emph{The stringdist package for approximate string matching}. The R Journal 6(1) 111-122 \bibitem{ulid} \href{https://cran.r-project.org/package=ulid}{ulid} B. Rudis (2019) \emph{Generate Universally Unique Lexicographically Sortable Identifiers}. R package version 0.3.0 \bibitem{validate} \href{https://cran.r-project.org/package=validate}{validate} M. van der Loo, E. de Jonge and P. Hsieh (2019) \emph{Data Validation Infrastructure for R}. R package version 0.2.7 \bibitem{wand} \href{https://cran.r-project.org/package=wand}{wand} B. Rudis (2019) \emph{Retrieve `Magic' Attributes from Files and Directories} R package version 0.5.0 \end{thebibliography} \end{document} tinytest/inst/doc/using_tinytest.R0000644000176200001440000002450213543151031017071 0ustar liggesusers### R code from vignette source 'using_tinytest.Rnw' ### Encoding: UTF-8 ################################################### ### code chunk number 1: using_tinytest.Rnw:79-82 ################################################### options(prompt="R> ", continue = " ", width=75) ################################################### ### code chunk number 2: using_tinytest.Rnw:107-113 ################################################### lbs2kg <- function(x){ if ( x < 0 ){ stop(sprintf("Expected nonnegative weight, got %g",x)) } x/2.20 } ################################################### ### code chunk number 3: using_tinytest.Rnw:116-119 ################################################### library(tinytest) expect_equal(lbs2kg(1), 1/2.2046) expect_error(lbs2kg(-3)) ################################################### ### code chunk number 4: using_tinytest.Rnw:125-126 ################################################### isTRUE( expect_true(2 == 1 + 1) ) ################################################### ### code chunk number 5: using_tinytest.Rnw:150-152 ################################################### expect_error(lbs2kg(-3), pattern="nonnegative") expect_error(lbs2kg(-3), pattern="foo") ################################################### ### code chunk number 6: using_tinytest.Rnw:166-167 ################################################### expect_false( 1 + 1 == 2, info="My personal message to the tester" ) ################################################### ### code chunk number 7: using_tinytest.Rnw:198-199 ################################################### print(expect_equal(1+1, 3), type="short") ################################################### ### code chunk number 8: eval ################################################### run_test_file("test_addOne.R", verbose=0) ################################################### ### code chunk number 9: using_tinytest.Rnw:245-247 ################################################### test_results <- run_test_file("test_addOne.R", verbose=0) print(test_results, passes=TRUE) ################################################### ### code chunk number 10: using_tinytest.Rnw:250-251 (eval = FALSE) ################################################### ## options(tt.pr.passes=TRUE) ################################################### ### code chunk number 11: using_tinytest.Rnw:257-258 (eval = FALSE) ################################################### ## run_test_dir("/path/to/your/test/directory") ################################################### ### code chunk number 12: using_tinytest.Rnw:265-267 ################################################### out <- run_test_dir(system.file("tinytest", package="tinytest") , verbose=0) ################################################### ### code chunk number 13: using_tinytest.Rnw:271-272 ################################################### head(as.data.frame(out), 3) ################################################### ### code chunk number 14: using_tinytest.Rnw:278-279 ################################################### summary(out) ################################################### ### code chunk number 15: using_tinytest.Rnw:289-292 (eval = FALSE) ################################################### ## if ( expect_equal(1 + 1, 2) ){ ## expect_true( 2 > 0) ## } ################################################### ### code chunk number 16: using_tinytest.Rnw:302-305 ################################################### if ( ignore(expect_equal)(1+1, 2) ){ expect_true(2>0) } ################################################### ### code chunk number 17: using_tinytest.Rnw:313-316 ################################################### if ( Sys.info()[['sysname']] == "Windows"){ exit_file("Cannot test this on Windows") } ################################################### ### code chunk number 18: using_tinytest.Rnw:343-349 (eval = FALSE) ################################################### ## run_test_dir("/path/to/my/testdir" ## , remove_side_effects = FALSE) ## test_all("/path/to/my/testdir" ## , remove_side_effects = FALSE) ## # Only in tests/tinytest.R: ## test_package("PACKAGENAME", remove_side_effects=FALSE) ################################################### ### code chunk number 19: using_tinytest.Rnw:356-357 (eval = FALSE) ################################################### ## options(tt.collate="C") ################################################### ### code chunk number 20: using_tinytest.Rnw:373-374 (eval = FALSE) ################################################### ## test_package("pkg", side_effects=TRUE) ################################################### ### code chunk number 21: using_tinytest.Rnw:378-379 (eval = FALSE) ################################################### ## test_package("pkg", side_effects=list(pwd=FALSE)) ################################################### ### code chunk number 22: using_tinytest.Rnw:407-408 ################################################### run_test_file("test_se.R", verbose=1) ################################################### ### code chunk number 23: using_tinytest.Rnw:454-455 (eval = FALSE) ################################################### ## setup_tinytest("/path/to/your/package") ################################################### ### code chunk number 24: using_tinytest.Rnw:468-469 (eval = FALSE) ################################################### ## test_all("/path/to/your/package") ################################################### ### code chunk number 25: using_tinytest.Rnw:480-481 (eval = FALSE) ################################################### ## build_install_test("/path/to/your/package") ################################################### ### code chunk number 26: using_tinytest.Rnw:513-515 (eval = FALSE) ################################################### ## dat <- read.csv("women.csv") ## expect_equal(dat, women) ################################################### ### code chunk number 27: using_tinytest.Rnw:527-528 ################################################### options(prompt=" ", continue=" ") ################################################### ### code chunk number 28: using_tinytest.Rnw:530-535 (eval = FALSE) ################################################### ## # contents of pkgdir/tests/tinytest.R ## if ( requireNamespace("tinytest", quietly=TRUE) ){ ## home <- identical( Sys.info()["nodename"], "YOURHOSTNAME" ) ## tinytest::test_package("PKGNAME", at_home = home) ## } ################################################### ### code chunk number 29: using_tinytest.Rnw:540-541 ################################################### home <- identical( Sys.getenv("HONEYIMHOME"), "TRUE" ) ################################################### ### code chunk number 30: using_tinytest.Rnw:545-546 (eval = FALSE) ################################################### ## home <- length(unclass(packageVersion("PKGNAME"))[[1]]) == 4 ################################################### ### code chunk number 31: using_tinytest.Rnw:550-551 ################################################### options(prompt="R> ", continue=" ") ################################################### ### code chunk number 32: using_tinytest.Rnw:558-560 ################################################### run_test_file("test_hehe.R", verbose=0) run_test_file("test_hehe.R", verbose=0, at_home=FALSE) ################################################### ### code chunk number 33: using_tinytest.Rnw:584-585 (eval = FALSE) ################################################### ## tinytest::test_package("hehe") ################################################### ### code chunk number 34: using_tinytest.Rnw:629-630 (eval = FALSE) ################################################### ## build_install_test("/path/to/your/package", ncpu=2) ################################################### ### code chunk number 35: using_tinytest.Rnw:641-642 (eval = FALSE) ################################################### ## test_package("PACKAGENAME", ncpu=2) ################################################### ### code chunk number 36: using_tinytest.Rnw:664-667 (eval = FALSE) ################################################### ## cl <- parallel::makeCluster(4, outfile="") ## parallel::clusterCall(cl, source, "R/myfunctions.R") ## run_test_dir("inst/tinytest", cluster=cl) ################################################### ### code chunk number 37: using_tinytest.Rnw:674-677 (eval = FALSE) ################################################### ## parallel::clusterCall(cl, source, "R/myfunctions.R") ## test_all(cluster=cl) ## stopCluster(cl) ################################################### ### code chunk number 38: using_tinytest.Rnw:711-720 ################################################### # exported, user-visible function inch2cm <- function(x){ x*conversion_factor("inch") } # not exported function, package-internal conversion_factor <- function(unit){ confac <- c(inch=2.54, pound=1/2.2056) confac[unit] } ################################################### ### code chunk number 39: using_tinytest.Rnw:763-771 ################################################### pound2kg <- function(x){ stopifnot( is.numeric(x) ) if ( any(x < 0) ){ warning("Found negative input, converting to positive") x <- abs(x) } x/2.2046 } ################################################### ### code chunk number 40: using_tinytest.Rnw:790-796 (eval = FALSE) ################################################### ## expect_equal(pound2kg(1), 1/2.2046 ) ## # test for expected warning, store output ## expect_warning( out <- pound2kg(-1) ) ## # test the output ## expect_equal( out, 1/2.2046) ## expect_error(pound2kg("foo")) ################################################### ### code chunk number 41: using_tinytest.Rnw:841-847 ################################################### bad_function <- function(file){ oldwd <- getwd() setwd(dirname(file)) source(basename(file)) setwd(oldwd) } ################################################### ### code chunk number 42: using_tinytest.Rnw:854-854 ################################################### ################################################### ### code chunk number 43: using_tinytest.Rnw:855-861 ################################################### good_function <- function(file){ oldwd <- getwd() on.exit(setwd(oldwd)) setwd(dirname(file)) source(basename(file)) } tinytest/inst/tinytest/0000755000176200001440000000000013543151031014771 5ustar liggesuserstinytest/inst/tinytest/test_gh_issue_32.R0000644000176200001440000000055413532276246020307 0ustar liggesusers out <- ignore(expect_equal)("foo","bar") # Message should read 'Expected "target" got "current"', # not the other way around expect_true(grepl("bar.+foo",attr(out,"diff"))) out <- ignore(expect_identical)("foo","bar") expect_true(grepl("bar.+foo",attr(out,"diff"))) out <- ignore(expect_equivalent)("foo","bar") expect_true(grepl("bar.+foo",attr(out,"diff"))) tinytest/inst/tinytest/test_init.R0000644000176200001440000000031613500000356017112 0ustar liggesusers oldterm <- Sys.getenv("TERM") Sys.setenv(TERM = "dumb") # will be unset by tinytest tinytest:::.onLoad() expect_false(getOption("tt.pr.color")) # reset option set by .onLoad() options(tt.pr.color=NULL) tinytest/inst/tinytest/programming.R0000644000176200001440000000035213462146043017445 0ustar liggesusers # some test to check that we can program over tests. # this should yield 10 testresults for ( i in 1:10 ){ expect_equal(1+1,2) } # this should yield a single testresult if ( ignore(expect_equal)(1+1,2) ){ expect_true(TRUE) } tinytest/inst/tinytest/runs/0000755000176200001440000000000013525326355015775 5ustar liggesuserstinytest/inst/tinytest/runs/test_envvar.R0000644000176200001440000000025013524610553020450 0ustar liggesusersreport_side_effects() expect_equal(1+1,2) Sys.setenv("foo"="bar") expect_equal("a","b") report_side_effects(FALSE) Sys.setenv("foo"="baz") expect_equal(TRUE, TRUE) tinytest/inst/tinytest/runs/test_exit.R0000644000176200001440000000014613525326355020131 0ustar liggesusers expect_true(TRUE) expect_true(TRUE) exit_file(msg="snafubar") expect_true(TRUE) expect_true(TRUE) tinytest/inst/tinytest/runs/test_cwd2.R0000644000176200001440000000005513525316246020014 0ustar liggesusers old <- getwd() setwd(tempdir()) setwd(old) tinytest/inst/tinytest/runs/test_envvar2.R0000644000176200001440000000022313525316246020535 0ustar liggesusers expect_equal(1+1,2) Sys.setenv("foo"="bar") expect_equal("a","b") report_side_effects(FALSE) Sys.setenv("foo"="baz") expect_equal(TRUE, TRUE) tinytest/inst/tinytest/runs/test_cwd.R0000644000176200001440000000010313524623313017717 0ustar liggesusersreport_side_effects() old <- getwd() setwd(tempdir()) setwd(old) tinytest/inst/tinytest/test_tiny.R0000644000176200001440000000463613532276246017165 0ustar liggesusers # check behavior expect_true(TRUE) expect_false(FALSE) expect_equal(1,1) expect_identical(1L,1L) # check output value (wow, this is pretty meta---man) expect_true( ignore(expect_true)(TRUE)) expect_false(ignore(expect_true)(FALSE)) expect_true( ignore(expect_false)(FALSE)) expect_false(ignore(expect_false)(TRUE)) expect_false( ignore(expect_identical)(1L,2L) ) # check behavior expect_equal(1+1,2) # check output value expect_false(ignore(expect_equal)(1+1, 3)) expect_true(ignore(expect_equal)(1+1, 2)) # check behavior expect_equivalent(2,c(x=2)) # check output value expect_true(ignore(expect_equivalent)(2,c(x=2))) expect_false(ignore(expect_equivalent)(2,c(x=3))) # check NULL expect_true(ignore(expect_null)(NULL)) expect_false(ignore(expect_null)(1)) # reading from file dat <- read.csv("women.csv") expect_equal(women, dat) # check behavior expect_warning(warning("foo")) expect_error(stop("bar")) expect_true(ignore(expect_error)(stop("foo"))) expect_false(ignore(expect_error)(stop("foo"),pattern="bar")) expect_true(ignore(expect_warning)(warning("fu!"))) expect_false(ignore(expect_warning)(warning("fu!"), pattern="bar")) expect_true(ignore(expect_silent)(1 + 1)) expect_false(ignore(expect_silent)(1 + "a")) expect_false(ignore(expect_silent)(1:3 + 1:2)) expect_false(ignore(expect_message)(message("hihi"),"lol")) expect_false(ignore(expect_message)(stop("hihi"),"lol")) expect_false(ignore(expect_message)(warning("hihi"),"lol")) expect_message(message("hihi, I lol"),"lol") # check that info fields are filled. msg <- "whoO0Oop" L <- list( ignore(expect_true)(TRUE, info=msg) , ignore(expect_true)(FALSE, info=msg) , ignore(expect_false)(TRUE, info=msg) , ignore(expect_false)(FALSE, info=msg) , ignore(expect_equal)(1,1, info=msg) , ignore(expect_equal)(1,2, info=msg) , ignore(expect_equivalent)(1,1, info=msg) , ignore(expect_equivalent)(1,2, info=msg) , ignore(expect_identical)(1,1, info=msg) , ignore(expect_identical)(1,2, info=msg) , ignore(expect_null)(NULL,info=msg) , ignore(expect_null)(NA,info=msg) , ignore(expect_message)(message("hihi"),info=msg) , ignore(expect_message)(1+1, info=msg) , ignore(expect_warning)(warning("hihi"), info=msg) , ignore(expect_warning)(1+1, info=msg) , ignore(expect_error)(stop("hihi"),info=msg) , ignore(expect_silent)(1+1, info=msg) , ignore(expect_silent)(stop("hihi"), info=msg) ) for ( x in L ) expect_equal(attr(x,"info"), msg) tinytest/inst/tinytest/test_file.R0000644000176200001440000000310313532276246017105 0ustar liggesusers## note: the system.file call is just a convenience for my local test.r script # run all tests in test_tiny again, this time # with the file runner. results <- run_test_file(system.file("tinytest/test_tiny.R",package="tinytest"),verbose=FALSE) bools <- sapply(results, as.logical) expect_true(all(bools)) # more complicated tests, using ignore() to skip nested expectations results <- run_test_file(system.file("tinytest/programming.R",package="tinytest"), verbose=FALSE) expect_equal(11, length(results)) expect_true(all_pass(results)) expect_false(any_fail(results)) expect_false(all_fail(results)) expect_true(any_pass(results)) expect_error(all_pass("hihi")) expect_error(any_pass("hihi")) expect_error(all_fail("hihi")) expect_error(any_fail("hihi")) # collect side effects using the call in the test file out <- run_test_file("runs/test_envvar.R",verbose=0) expect_true(is.na(out[[2]])) expect_equal(sum(is.na(sapply(out, c))),1) out <- run_test_file("runs/test_cwd.R", verbose=0) expect_true(is.na(out[[1]])) expect_true(is.na(out[[2]])) # controll collecting side-effects from file runner. out <- run_test_file("runs/test_envvar2.R", verbose=0, side_effects=TRUE) expect_true(is.na(out[[2]])) expect_equal(sum(is.na(sapply(out, c))),1) # detailed control out <- run_test_file("runs/test_cwd2.R", side_effects=list(pwd=FALSE), verbose=0) expect_equal(length(out),0) # premature exit testing out <- run_test_file("runs/test_exit.R", verbose=0) expect_equal(length(out), 2) # plots should not cause an 'Rplots.pdf' file being created plot(1:10, 1:10) expect_false(exists("Rplots.pdf")) tinytest/inst/tinytest/test_env_B.R0000644000176200001440000000072713476015321017220 0ustar liggesusers # The "hihihaha" environment variable was set in test_env_A and should be unset # now. expect_equal(Sys.getenv("hihihaha"), "") # The "hihihaha" option was set in test_env_A and should be unset now expect_true(is.null(getOption("hihihaha"))) expect_equal(Sys.getenv("hoho"), "") expect_true( is.null(getOption("hoho")) ) # Surviving envvars and options expect_equal(Sys.getenv("hehe"), "3") Sys.unsetenv("hehe") expect_equal(getOption("hehe"), 3) options(hehe=NULL) tinytest/inst/tinytest/test_extensibility.R0000644000176200001440000000026213524455575021072 0ustar liggesusers register_tinytest_extension(pkg="lulz" , functions=c("fee","fi","fu","bar")) ext <- getOption("tt.extensions") expect_equal(ext, list(lulz = c("fee","fi","fu","bar") )) tinytest/inst/tinytest/test_gh_issue_17.R0000644000176200001440000000010313502711633020267 0ustar liggesusers expect_equal(1, 1.1, tol=0.2) expect_equivalent(1, 1.1, tol=0.2) tinytest/inst/tinytest/test_utils.R0000644000176200001440000000007313530032421017310 0ustar liggesusers expect_error(tinytest:::stopf("foo %s","bar"),"foo bar") tinytest/inst/tinytest/women.csv0000644000176200001440000000017313462146043016643 0ustar liggesusers"height","weight" 58,115 59,117 60,120 61,123 62,126 63,129 64,132 65,135 66,139 67,142 68,146 69,150 70,154 71,159 72,164 tinytest/inst/tinytest/test_env_A.R0000644000176200001440000000153413476015321017214 0ustar liggesusers # We set an environment variable in this file. It must be unset by tinytest # before running the next file (test_env_B.R) Sys.setenv(hihihaha=8) expect_equal(Sys.getenv("hihihaha"), "8") # We set an option in this file. It must be unset by tinytest before running # the next file (test_env_B.R) options(hihihaha=8) expect_equal(getOption("hihihaha"), 8) # We set another envvar and unset it as well. We don't want to bother # users already following good practice k <- Sys.getenv("hoho") Sys.setenv(hoho=2) Sys.setenv(hoho=k) # We set another option and unset it as well. We don't want to bother # users already following good practice oldopt <- options("hoho") options(hoho=2) options(hoho=oldopt) # Here's an envvar that must survive the slaughter after this file was run base::Sys.setenv(hehe=3) # And one for options as well base::options(hehe=3) tinytest/inst/tinytest/test_RUnit_style.R0000644000176200001440000000037713463134023020446 0ustar liggesusers # check if RUnit style functions also work. expect_true(ignore(checkTrue)(TRUE)) expect_true(ignore(checkFalse)(FALSE)) expect_true(ignore(checkEqual)(1+1,2)) expect_true(ignore(checkIdentical)(1L,1L)) expect_true(ignore(checkEquivalent)(c(a=1),1))