RUnit/0000755000176200001440000000000013277600065011321 5ustar liggesusersRUnit/COPYING0000644000176200001440000004313113267374743012370 0ustar liggesusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. RUnit/inst/0000755000176200001440000000000013267374743012310 5ustar liggesusersRUnit/inst/examples/0000755000176200001440000000000013267374743014126 5ustar liggesusersRUnit/inst/examples/runitVirtualClassTest.r0000644000176200001440000001001313267374743020642 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ ## example code for test cases for S4 virtual class methods ## the following class definition code would be part of a package or script ## ## execute these test cases via e.g. ## testSuite <- defineTestSuite("VirtualClassTest", ## file.path(yourSrcPath, "RUnit/inst/examples"), ## "runitVirtual") ## testData <- runTestSuite(testSuite) ## printTextProtocol(testData) ## package 'methods' is usually loaded, but make sure it is checkTrue(require(methods)) ## define class className <- "MyVirtualBaseClass" setClass(className, representation("VIRTUAL", x = "numeric", y = "numeric", description = "character"), validity = NULL, sealed = FALSE, where = .GlobalEnv) if (!isGeneric("getX")) { setGeneric("getX", function(object, ...) standardGeneric("getX"), useAsDefault=TRUE, where=.GlobalEnv, valueClass="numeric") } setMethod("getX", signature=className, function(object) return(object@x), where=.GlobalEnv) if (!isGeneric("setX<-")) { setGeneric("setX<-", function(object, value) standardGeneric("setX<-"), useAsDefault=TRUE, where=.GlobalEnv) } setMethod("setX<-", signature=signature(object=className, value="numeric"), function(object, value) { if (length(value) < 1) { stop("value has to contain at least one element.") } if (any(is.na(value))) { stop("value may not contain NA(s).") } object@x <- value return(object) }, where=.GlobalEnv) testMyVirtualBaseClass.getX <- function() { ##@bdescr ## create a derived class with no own method definitions ## which inherits parent class methods that can then be checked ## ## getter test case ##@edescr testClassName <- "MyDerivedTestClass" setClass(testClassName, representation("MyVirtualBaseClass"), validity = NULL, sealed = FALSE, where = .GlobalEnv) on.exit(removeClass(testClassName, where=.GlobalEnv)) ## system constructor this <- new(testClassName) ## constructor call succeeded? checkTrue( is(this, testClassName)) ret <- getX(this) checkTrue( is(ret, "numeric")) ## class default checkEquals( ret, numeric(0)) } testMyVirtualBaseClass.setX <- function() { ##@bdescr ## setter test case ## also check correct handling of invalid arguments ##@edescr testClassName <- "MyDerivedTestClass" setClass(testClassName, representation("MyVirtualBaseClass"), validity = NULL, sealed = FALSE, where = .GlobalEnv) on.exit(removeClass(testClassName, where=.GlobalEnv)) ## system constructor this <- new(testClassName) ## constructor call succeeded? checkTrue( is(this, testClassName)) testSeq <- 1:23 setX(this) <- testSeq ret <- getX(this) checkTrue( is(ret, "numeric")) checkEquals( ret, testSeq) ## error handling checkException( setX(this) <- numeric(0)) checkException( setX(this) <- as.numeric(NA)) checkException( setX(this) <- c(1:4, NA)) } RUnit/inst/examples/correctTestCase.r0000644000176200001440000000163713267374743017415 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ test.correctTestCase <- function() { checkTrue( TRUE) checkTrue( !identical(TRUE, FALSE)) } RUnit/inst/examples/runitc2f.r0000644000176200001440000000313513267374743016047 0ustar liggesusers## This is a very trivial demo of ## the RUnit test case execution system: ## --------------------------------- ## functions to be tested (usually defined in a different ## file from where the test cases are located): ## centigrade to Fahrenheit c2f <- function(c) return(9/5 * c + 32) ## Fahrenheit to centigrade f2c <- function(f) return(5/9 * f - 32) ## ups, a bug (brackets missing) ## test functions: ## --------------------- .setUp <- function() { ## called before each test case, see also .tearDown() print(".setUp") } test.c2f <- function() { checkEquals(c2f(0), 32) checkEquals(c2f(10), 50) ## check that an error is created for a bogus argument checkException(c2f("xx")) } test.f2c <- function() { checkEquals(f2c(32), 0) checkEquals(f2c(50), 10) ## check that an error is created for a bogus argument checkException(f2c("xx")) } test.errordemo <- function() { stop("this is just to show what an error looks like as opposed to a failure") } ## How to run the tests (do not uncomment in this file, ## but execute the commands at the R prompt): ## All you have to do is to adapt the directory locations. ## ------------------------------------------------ ## define the test suite: #testsuite.cf <- defineTestSuite("cfConversion", dirs="directoryOfThisFile") ## run test suite: #testResult <- runTestSuite(testsuite.cf) ## print text protocol to console: #printTextProtocol(testResult) ## print HTML version to a file: #printHTMLProtocol(testResult, fileName="someFileName.html") ## In this case we also have a shortcut #runTestFile("directoryOfThisFile/runitcfConversion.r") RUnit/inst/share/0000755000176200001440000000000013267374743013412 5ustar liggesusersRUnit/inst/share/R/0000755000176200001440000000000013267374743013613 5ustar liggesusersRUnit/inst/share/R/compareRUnitTestData.r0000644000176200001440000001067113267374743020045 0ustar liggesusers###################################################################### ## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ compare <- function(td1, td2, tolerance=100) { ##@bdescr ## compare two test suite result data objects obtained ## on the same test suite ## identify timing differences exceeding 'tolerance' [seconds] ##@edescr ## ## ##@in td1 : [list] of S3 class 'RUnitTestData' ##@in td2 : [list] of S3 class 'RUnitTestData' ##@in tolerance : [numeric] positive scalar ##@ret : [data.frame] ## ##@codestatus : untested ## preconditions if (!is(td1, "RUnitTestData")) { stop("argument 'td1' has to be of class 'RUnitTestData'.") } if (!is(td2, "RUnitTestData")) { stop("argument 'td2' has to be of class 'RUnitTestData'.") } if (length(tolerance) != 1 || is.na(tolerance) || tolerance < 0) { stop("argument 'tolerance' has to be positive scalar.") } ## helper functions commonNames <- function(x1, x2) { return(intersect(names(x1), names(x2))) } compareTiming <- function(x1, x2, tol=0) { d <- x1 - x2 if (abs(d) > tol) { return(d) } else { return(as.numeric(0)) } } comparePerSourceFile <- function(sf1,sf2, tol=0) { ## FIXME ## check if test case file was considered in this suite ## i.e. test case file name can be in list albeit it ## was not executed in the scenario ## thus list is empty if (length(sf1) == 0 | length(sf2) == 0) { cat("\n skipped empty result set:", sf1) return(NULL) } commonTests <- commonNames(sf1, sf2) t(sapply(commonTests, function(x, obj1, obj2) { ##cat("\n test:", x, "\n") if(obj1[[x]][["kind"]] == obj2[[x]][["kind"]]) { if (obj1[[x]][["kind"]] == "success") { return(c(x, obj1[[x]][["kind"]], obj1[[x]][["time"]], obj2[[x]][["kind"]], obj2[[x]][["time"]], compareTiming(obj1[[x]][["time"]], obj2[[x]][["time"]], tol=tol))) } else { return(c(x, obj1[[x]][["kind"]], as.numeric(NA), obj2[[x]][["kind"]], as.numeric(NA), as.numeric(NA))) } } else { ## no timing delta ## should check for timing in second case ## obj2[[x]][["time"]]) return(c(x, obj1[[x]][["kind"]], as.numeric(NA), obj2[[x]][["kind"]], as.numeric(NA), as.numeric(NA))) } }, obj1=sf1, obj2=sf2)) } comparePerSuite <- function(s1,s2, tol=0) { ## absolute file names recorded, strip path commonFiles <- intersect(basename(names(s1[["sourceFileResults"]])), basename(names(s2[["sourceFileResults"]]))) do.call("rbind", sapply(commonFiles, function(x, obj1, obj2) { ## match exact file name in abs. name idx1 <- match(x, basename(names(obj1))) idx2 <- match(x, basename(names(obj2))) if (length(idx1) != 1 || is.na(idx1) || length(idx2) != 1 || is.na(idx2)) { stop("ambiguous file name.") next; } comparePerSourceFile(obj1[[idx1]], obj2[[idx2]], tol=tol) }, obj1=s1[["sourceFileResults"]], obj2=s2[["sourceFileResults"]]) ) } ## main ## test suites to compare commonTestSuites <- commonNames(td1, td2) res <- matrix(ncol=6, nrow=0) colnames(res) <- c("TestCase", "Suite1 State", "Suite1 Timing", "Suite2 State", "Suite2 Timing", "Delta") for (ti in seq_along(commonTestSuites)) { res <- rbind(res, comparePerSuite(td1[[commonTestSuites[ti]]], td2[[commonTestSuites[ti]]], tol=tolerance)) } ## postcondition return(res) } RUnit/inst/share/R/checkCode.r0000644000176200001440000001165613267374743015657 0ustar liggesusers###################################################################### ## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ checkCodeFiles <- function(fileName) { ##@bdescr ## utility function for code checks of files outside usual R/ folder structure ## requires package codetools ##@edescr ## ##@in fileName : [character] vector of file names (including path, relative to pwd or absolute) ##@ret : [list] with elements per function, that incurred any warning ## ##@depends : codetools ## ##@codestatus : untested ## return list retList <- list() ## initialized before ls() call to avoid listing ok <- x <- c() tmpRetEnv <- new.env() tmpRet <- NULL ## generate listing of existing objects before first source'ing lsTmp <- lsInit <- ls() sapply(fileName, function(x) { cat("\n file ",x) ok <- try(utils::capture.output(source(x, local=TRUE, echo=FALSE))) if (inherits(ok, "try-error")) { cat("\n file",x,"could not be sourced:", geterrmessage(), "\n") return() } newElements <- setdiff(ls(), lsTmp) cat("\n functions",paste(newElements, collapse=", ")) lsTmp <- ls() sapply(newElements, function(x) { ok <- try(get(x)) if (!inherits(ok, "try-error") && identical(mode(ok), "function")) { cat("\n ",x," (",is(ok)[1],"): ",sep="") ## this function will be used in signalUsageIssue w$warn reportFunc <- function(x) { cat(x) assign("tmpRet", c(tmpRet, x), pos=parent.env(tmpRetEnv)) } codetools::checkUsage(ok, report=reportFunc, all=TRUE) if (!is.null(tmpRet)) { retList[[length(retList) + 1]] <<- tmpRet names(retList)[length(retList)] <<- x } tmpRet <<- NULL } }) }) return(invisible(retList)) } checkCodeFolders <- function(path=".") { ##@bdescr ## utility function ## code checks of all .[RrSs] files found in one or more folders ## requires package codetools ##@edescr ## ##@in path : [character] ##@ret : [list] ## ##@depends : codetools ## ##@codestatus : untested stopifnot(require(codetools)) if (!is(path, "character")) { stop("argument 'path' has to be of type 'character'.") } if (!all(file.exists(path))) { stop("argument 'path' has to contain existing folder(s).") } fNames <- list.files(path=path, pattern="\\.[rRsS]$", full.names=TRUE) checkCodeFiles(fNames) } checkCodeSweave <- function(path=".") { ##@bdescr ## utility function ## code checks of all .[RS]nw files found in one or more folders ## experimental: does not convert extracted code chunks to closures ## thus only functions defined inside a chunk but nut all of the chunk code is checked ## ## requires package codetools ##@edescr ## ##@in path : [character] ##@ret : [list] ##@depends : codetools ## ##@codestatus : untested ## Issue: ## local path e.g. 'RUnit/inst/doc' ## is no expanded to full path ## which I would wnat to use as absolute path ## in the Stangle call stopifnot(require(utils)) stopifnot(require(codetools)) if (!is(path, "character")) { stop("argument 'path' has to be of type 'character'.") } if (!all(file.exists(path))) { stop("argument 'path' has to contain existing folder(s).") } browser() path <- path.expand(path) pwd <- getwd() if (length(path)) { if (path == ".") { path <- pwd } ## do we have a local path rather then an absolute ## how to infer correct absolute path? } fName <- list.files(path=path, pattern="\\.[RS]nw$", full.names=TRUE) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") tmpDir <- file.path(tempdir(), timeStamp) print(tmpDir) if(!file.exists(tmpDir)) { stopifnot(dir.create(tmpDir, recursive=TRUE)) } #on.exit(unlink(tmpDir, recursive=TRUE)) ## change to temp folder to dump Stangle output therein setwd(tmpDir) on.exit(setwd(pwd), add=TRUE) codeFiles <- unlist(sapply(fName, function(x) { Stangle(x) x <- basename(x) gsub("[RS]nw$", "R", x) })) ## codeFiles <- file.path(tmpDir, codeFiles) checkCodeFiles(codeFiles) } RUnit/inst/doc/0000755000176200001440000000000013277574246013056 5ustar liggesusersRUnit/inst/doc/Makefile0000644000176200001440000000062613277574246014522 0ustar liggesusers## ## RUnit ## ## utility ## create PDF document from dvi (usefull if R CMD INSTALL fails to build the pdf) ## $Id$ ## all: RUnit.pdf clean RUnit.pdf: RUnit.ps ps2pdf -dEncodeColorImages=false -dColorImageFilter=/FlateEncode -dAutoRotatePages=/None RUnit.ps RUnit.ps: RUnit.dvi dvips RUnit RUnit.dvi: RUnit.tex latex RUnit latex RUnit clean: rm -f RUnit.aux RUnit.log RUnit.toc RUnit/inst/doc/RUnit.R0000644000176200001440000000440413277574245014243 0ustar liggesusers### R code from vignette source 'RUnit.Rnw' ################################################### ### code chunk number 1: RUnit.Rnw:251-292 (eval = FALSE) ################################################### ## library(RUnit) ## ## ## define sample functions to be tested ## foo <- function(x) { ## x <- x*x ## x <- 2*x ## return(x) ## } ## test.foo <- function() { ## ## checkTrue(is.numeric(foo(1:10))) ## checkEquals(length(foo(1:10)), 10) ## checkEqualsNumeric(foo(1), 2) ## } ## ## bar <- function(x, y=NULL) { ## ## if (is.null(y)) { ## y <- x ## } ## ## if (all(y > 100)) { ## ## subtract 100 ## y <- y - 100 ## } ## ## res <- x^y ## return(res) ## } ## ## track <- tracker(); ## initialize a tracking "object" ## track$init(); ## initialize the tracker ## a <- 1:10 ## d <- seq(0,1,0.1) ## ## resFoo <- inspect(foo(a), track=track); ## execute the test function and track ## resBar <- inspect(bar(d), track=track); ## execute the test function and track ## ## resTrack <- track$getTrackInfo(); ## get the result of Code Inspector (a list) ## ## printHTML.trackInfo(resTrack) ; ## create HTML sites ################################################### ### code chunk number 2: RUnit.Rnw:312-321 (eval = FALSE) ################################################### ## foo <- function(x) ## { ## y <- 0 ## for(i in 1:x) ## { ## y <- y + x ## } ## return(y) ## } ################################################### ### code chunk number 3: RUnit.Rnw:324-337 (eval = FALSE) ################################################### ## foo.mod <- function(x) ## { ## track$bp(1) ; ## y <- 0 ## track$bp(2); ## for(i in 1:x) ## { ## track$bp(4) ; ## y <- y +x ## } ## track$bp(6); ## return(y) ## } ################################################### ### code chunk number 4: RUnit.Rnw:342-345 (eval = FALSE) ################################################### ## if(any(a==1)) { ## print("do TRUE") ## } else print ("do FALSE"); ################################################### ### code chunk number 5: RUnit.Rnw:348-355 (eval = FALSE) ################################################### ## if(any(a==1)) { ## track$bp(2); ## print("do TRUE") ## }else{ ## track$bp(3); ## print("do FALSE"); ## } RUnit/inst/doc/RUnit.pdf0000644000176200001440000034530513277574245014623 0ustar liggesusers%PDF-1.5 % 40 0 obj << /Length 1808 /Filter /FlateDecode >> stream x˒FUEcSvʕXhyz53HBoqQwOOW/oc3Q̖R&L|il|,{`7q_#T,Y] }ZXv\Úߏ_h0NR2- sfd2-sOvm_p,M,f(4Y"@got# =n?Jī0b\3r/`7Z}cPt0:S^qcЫvWp!F Ej(4x6u F:V>:ѹ89( 43Xnkؔ%PqG&  4&mXGEǟ5!dd)frk6|RIT 0Y$E#QӠ̠ Y?8g#{R0u5Hx;<P| #;@-l\(w 5O"LhEk y(Qʆ $C)m8.ҵH YzR~b.?"N ћ<=WXRBoUlQh",ڥHF*mըN \Hㇹ1ҡ>E& K[;-sexe)Jg`yDֺD&u ل~T„RHET 8c?hC %莊Egl8[65=à]P#boC†V{*t'KO]s ںx#@OR~}$rBPJD,O]bWpdK൵rBoJox 96Kk%.w)X{`'QC`wE`kr$4:*Ys%A3zgwQFvw˦ FOd:-MC7~1촄NrX{%^rN\Ԝz6ay >xf L 6~Vg+*EJ Qs`F\7I=0ZUy Ϟ`Z?uc-HiEލƟ9ei8IepUI|M M\ttB829H]#ΤIw;+<3'L ,-y,TI^]() endstream endobj 56 0 obj << /Length 2575 /Filter /FlateDecode >> stream xڭYɎFWԑ[sƴ1y|`QԂEIuk.UnsKdd/?<S~UWSfr>=o~,*Eo{],2=tFKܺv27~Lh[")i'F΂aϜa8jadfq3R~FDg#g䲙?~Z* DOgQsRvQ޳Z$VVn 8^HHbGLB ά<ϣ;hu(ba˿*ėnisg!\I<3<1^/);S0 TK"ERK橊/ .׻xUp69q/b<1eQ +@|9|{kC;6HgP3_og,Wۊ'HjN-"'lc"A2[>ϝ9$6,&!M $1aJp.h,O}e#a"U1HnQ~^m<؈Ifb(~{G|Cسb@Lx'{"n"S1<2Wv|X·eV4ؼ TQ:<6uF* KE9{#u=lGxAɂ{C6v& ,}bvCqv^Hht-XVasKQJ۬cFml=fUɴy (iڇ-ua!q;\zl%  |{ fRٗ0A@įvldҤG~~W?3V!d6+:"X2}#6Idyxrqq<G!f8b΁{p@8jsWceJ4W%,JҸQCB_lɄ҅/%Z1ai臋bXXu˜}Y&._wp](Li'8n(q.-> stream xڵZIoWH&k5982 A9ȢDd+MkR 9Ъm[?~n|vwӬMSV&+on79_j ~<֍~׺~Ix|Q8: ~g_=F8H~=/g/Y#d =?iyh;zD?d7ZVcHB)*:(OfD ?oY%Grr"6+8ŗQEV_BX&4F^Bb3N,(QܲW! b9_Te1q[ #f_񹉺PI 8kIur*|' ?Ŝ飯r6tB xwxQҵ(.QJ,3je*lҰpMQ^HgB~,6m EWc-w udj\iE7jPﳌma! eS* BM6-"⮾œ媗5Nwԩ_Kg 3yuo_P9glwZ⒯2FV8A:Kk]K Z$xD )N J̈"XGO΢S]Uۀ 4IׇH?, :WG+~R;C-]Uf0??s{oV=E^p2_ @Bl.,*cl@ k;<(c^t(02?J }ޫ&{^ڍ׍LA~ܶ#E1|ޮVE~83m.&w^r"\\yoAq6"]Q4_@LE!8Ai;Qp" pd٩MQďЖ_~+, =&%qlB$ߊ@GeMx⓷4,UnXw'ǖ~J-eH@` 5%!+Pj߷jmЉBISP (o袬Ox-7a gPn:M jJ1X@O¿jib &t 0c \𨑿bMp, W+9-ZUټ!cW:'ٖMi[yw|A2|+9-. -79^ >Gp!'jBFކڔ(tM' / ĢYEIĤh ZkC9_`JH@ʢ D#&ܘD5ĩDn!}7"YQ| #+g?jH_4Vnk5E L5%% IU.OD%nC =);Xo<52SRBi#O"ɧ7n*@vpS̥p@E-Ar &hvm҉LJ8aҠ8Ѷ7Ijnt50hI+DRauCc=b4ny8ېJ񎔅fkƐR.g2l]а'9w u)/ФhI?8J.V(&qKe F./TK/Ɪ6I"{b1?;ES ӕ-L'$WYfe6k!:|C; ؞0 |CrRAm `ETcO} N,b'c%'LpUe\[|MB4hЄ uV͵yKX..P>\Q (5۸EtDUussF u)sFFdS %>1lvk>O%X{tyL*UXUvk\`>b?Eg. rt]Ãb=.7Jl8&1q:|# J$ cav1]Yz^^P^˷;V#,w8p᳀Kϊ/絲 .}K4 ts~z%v5I,M<[m#"MR`Epq.lObXQ݀ SFDoЯ* i,H7C/\V~ZJf(k endstream endobj 72 0 obj << /Length 2519 /Filter /FlateDecode >> stream xڭYIϯБF2y棨ţ'$5ߧ^(M O!7$yؖE*3-l[;&?O4B*QכvѴNn EB_CuZE>;(_<g/x|aq7z1-6}pzYXN$&,,y&PpMbJOrIqB" fYpv1IrYkU,zgn)А`١Zq&YRoMe#,RǠ,4gn/-sگ{|,Ս_;#46!@҄_:k'={X<ɻI\J4.)$B:haj~b}ź5-qouwwx;\KHqqU ܌7%YC&ЛifAz(MX.__";!4.tβ"Ia3寪ú#eV$y*{6$8([C/=/`:$b0,a_]9д~Ÿn1as D8]4`f q%t[s&.i3O,D)y)su^`mxʕ<^s w(7֒3m ܈DHv7,huԲj,7C&^i7y/0hE?sG)>*H!xJ%|T5 $LǼFi#&OIfFp 2\Cf? < 0L|#4d1C=Zt5zN4*@ʒG6+op$O,\%eYӭG5`,oqiLb^}ą 3q7'Q]niKpƅR.8+:~Ͽֳކ޽Lq&ݪ C?~ `%**ԉ_IuULyA:{YyGY-F +>aP!>GQ8 8UE~7w>ef6a `C[d(~n䉽czX\Vuq8YVa4꾮ssBݔH9)A48~`掬v yXorUodT z]:R@coXv9YJΒKd !0eI~.,R,0Ex[UUSzˀmNι^&^mYX yY'lo"<$o4*낢o,#F ^C7;Im{m GWlÂtIX:? ]Bs. EU|ݮo8kG1&]%6MN-m\3,{e<ϨnQBHȏ<ìBi|xé63bfs%5?{n&sS~Ҝ{eJ01 =@$?{l|dT&9y6#G='yQ=fU5vHMq~P$@@Vɢy?Ƈ,v+f(`m֒%3;j8~?!"ɲ^;8~n)or)Lsdo_)=^Kkuzu{!zO9D.MDc%QM4;\e,j=} z?r?[;y5^F6A ]"BO!3˿UvK9OqTm0D\.ih63l #F kkci{ Bkoa$/=irW !z8kY| rű?^ 4ُob_"=ӛA$i5y'tW{ ˄m2hQ7iaDV}bxh#4Qve%ó'uhgN%5oO endstream endobj 76 0 obj << /Length 2610 /Filter /FlateDecode >> stream xڭɎ>_јKS11c999p(naQ ȿV E $JŪW޾ؼDWqy\mvWe&Wee^mWmwX%U0`OjԙA?wwa2 /utz׿Gq37Z}z'vpFMt 72haecEY UG$cUt0 Hh@#i$7vZZ,t<͇,qay`;ƙtfQpJ3k/e S[8u>O}Gg@GTڱG< )EoQ%=~7(#%=_4>ַx D&$ C ܾg=M\#3Dǫ'gMf4Ϡne]?WL #(4 2GȐpmcIš+&Ucp--Ұ.2W0l^dS7l ۑ`´( 0|CdzzD|3EYv;7-0%}qMm{#'aDUB`ݸswJt,Z/0t(ynd}c$o-17êJp^BAo JO IƱ} I;afZc0M#ěGύ~rgK"D׈XAYVObd ^jہS^ID`'{|MbhUʘX!RUο$̓YnM6#TQ?#$+_D2abX<΍م~W~8"\y+CzI p$1r< g1a~xP=$Tx,:ȁ6i!vAM>J[X|X9uZ_3}5dY\4ĜfV=gpcϪ`0go0$=ZqUq@ Q:H7ѨO?{i-`ȶJ eAnr$HpY n"Î&v^rIКʽC,1i&i-*m(u La'>~Yf[*(K놼A'Kz8NbaWiC Ly K;vDOZvU1*wi8%qIKP}S` eeR$bL ppQ11V>{`vOT8_0Q4n6<+(^z<#隑M[Dq(6'GvoyqqK呐 mP:,IB6*ZicokJ z)ؠEp+uhQ愤A&J7i|\aFF;<5DjbUYQ67/ir-}bG`g vB9;I}q]]_b)\o/Y'F r|np::]ýH}RiJ#Jڑg8g*EJ`_ڛx5- "Ν&#n3qk=ݜ™z-~Seb*`GײVL8g1Mc{)|R=%Ad=H ͉Ū\L!>>G'ӬHՀM9 I -"UrY,:dȕKm}ޜD$*w[$"q1Ɔ-Bg jH`"UD%/ҸPW_8ʯ8ɼ$*7ĕBGT8t,(Z}*U>MuRԖ,mGɥ달'R9Qa,GM 8JxBw#s ;qCNeBUo>W(˟0 ʩ80ٹtwߛ/qTY=+]QDwsۗ췒gKK|N0nN ]Qu4+3W'>yu)yXg)4aUU7w?n4" endstream endobj 82 0 obj << /Length 2116 /Filter /FlateDecode >> stream xڕYK7 W9hC*H[=t[`n EOߘxvٲ4GhjI2Zi]VJD/K$Q;^Eʖ`{u$wxZd<ܷ*}I,da/a˪^r3cծ"E^?iR* a52s )6ϳE{ƕ;YB&T3l~ shYpǴ̦uz?-VdʜUeώ$+>VO<4[S yéӈ(I*TZ/=l<3 =U'H[TL0 :%xl1P.-I4Z_{fY ",rHW2`_Ki !`au|ɫ(x'0=Ϫ0dSFX;Yef.}Gr ?4q,ٙ³%MZ~\U+s5sU,{r,/It)b̊^i#Bt DkԢ}`| >zh^ M4dWYD_rl43s6~tkQwRWy2h!r  5N 2/vDg!wbTIP{)i/WKxH(ep ?͋2^iDMžV~8eeMȍ}s-j]LHk ,yS:ϵ=iLbYC5Ƽ`nY)As'5;`czgC2 BFFt(m]hwaM:= :1WXu9y}D^v5Rqѭlt:^a;#7smw;+2č+3݆37#5:۝ 7,+6xqI;Y <2*/p}]T8%wqV*jX) 1{R%F'W4FԘk=EHR遁o$Nd[C|FvRTQ4Kz_ p,ϔHq"_&K4DjvԸǎ<83F3#. ,'L뷴=](1t, _aNh]Yۖq t)C`ITjM[>w5xYi6-BN _v6IS'nX{:ȶjC 6*A.>!>Ҩ^][ endstream endobj 90 0 obj << /Length 2446 /Filter /FlateDecode >> stream xڵYIwБzҀ 3==9%sH@K%:<}jBSC BWUϛO?ZE۲~Vuֵ)_77WkbZh- S̳헫"?z82;x@{ ?' 7gclU?>D֭F |[{/痹h^u@Il۝q\J%F~<0\-V[;W13{e;`r^-aǃ#hNtIxaw/x]ZNedq!XUly`jyf^ +P=\1왴=tqZmfeN!IfescaYt"V &@,YƘ/l2[*M'c>;^fNv _8sfIV܅ 5`=<$>)mb;m]{I*KjPK"+cCS')zC1L7S)mܬ9ƒ!͔~rV].®?-%ԃvt2,\޳V B9I\W5TWRȃx}}fݲ>(ڑI5㐏pjX|m:Ć\9v_#,y^ΚpMWf 8MtZħgFKN/ 9^G>BAaIEd'-ҽ f mɬ]؜m*ڭ+r[4Q̰3<3FHph.`xXzRKx$7X"Ǧb 6eM`ڨ kĕbcVi2e(tydh4 gu'3c٪,a6΍wB oP05z&i'n5 t=Ss&d RˬJ(~GzCi8 Gk8&&ULTj((7MN$c:t)4|aw1 NM* h'ʿk@[5B&gԎ`DI Ե nESNa-mTi@mp ){4O;1+5q]@I3~4y?UKLՂ8Dx;⓾=HL%X$kǷͫ{e"a&twlSh"iߙ3=Dx(;Rcז^V<cHV;KRMK4/:$zщW` @{HQC6<>iܦ fAyHP;LbjMO"L} Wl/J$ @ƕk'X%\'Y!|Mpe0!APVrF:-L0@be6Z]z]aɌd5N}? endstream endobj 95 0 obj << /Length 1025 /Filter /FlateDecode >> stream xXKS0+2nWB 2rS3NIH NZh^䇜pADXjWisx!vi'užM;١vaAm?Ĵqb6G: cQj? LL%dӌkBcƦ#`SElfZ44N=[ d``.c\6ڋ.H k$\7f}]6ՅPIBYb< ظgcnl\'Q\@p1f~I]`ƃflR|l!Vs7Ǹ HVVw!t 〺\Mɘa?_ٸB)G×HShu$r|Wa%ӫ^,4 @L]fLaM` E9%\o?ѪP~Íj*5ڨV6\ynʳۼ+yl wP1aK|nRm%a)@qVqOˆ6AbC_Cxђ3J+#f+)|DZ$=Rou&~\N2kcჄ}'ώ¡^D|Thxz;fb<%0Ky ABY.ߎ!" endstream endobj 99 0 obj << /Length 1465 /Filter /FlateDecode >> stream xڽWIoFW1(Р "zhz%VHF޷ I9H"gUogdT\Z'$/c=M牙̗*§3ջ,kFM&Lܕo8YZfjTӼtk kk[ty1ȅh̤8~Z85x  p +nƉ ;xv*BhdReqflq,TTfWZb@7c@9x ѷpȫ\ܯ &t\67bOd,x˽kPIp`P") `w`wQMȸ2X7#Q-Sb;I1i\;HQLr/tpd^Re'V`L ၔj%rZx2G BYÜ@;.j^-dRL4-q֔!ks>uϽn甅trOy|$J _m3^Àn:%Nв-Yc?fZmTҞ6.ooA*O#2`H\N2N{Z:̄աlROcxi\Jd!41]Q,|69u4)c=hl)u)Zfߍxc_b`]Ip6 5өޜzpU2`,ȝ9OOqrٻ MZ>*IHq&D<{jeNSjӨ~1ƊI SRn#ߗ%XQJróF' E3ᅬ[۠F?()o1@f$WH܏Mg> =RsPTJSUb l ^SM uE %R1?S endstream endobj 104 0 obj << /Length 1583 /Filter /FlateDecode >> stream xڽXmD_!>آq~7$=**ABjq6pNqUgv6:_}yfM. 4̂<-yy/V麟5< <eeQmp 6HCxBs7YTy (˜-_QL -C_ʣxs$H/U+Kώ x6|BK&%qŇ; 5v}>5{su+$?*"qS0 ힴ=-&jW(,ٜHAX4(&nұj ua?pM=pAC^A%bpPDl-s9ܘm]$>\*TANAfH΅@Q[dU"qմ(سICM67 >9 }1i "]ԒS6Rp5,~?꣨+w( Q@Z)8W6#Y;ahx\mQTmVVHXm,wђ@dx4u#u< Nr>$^խqM862$T"S9d7b﯍`AANCFtk{WEIyK·)hp)3 ] JiGi-IMZIzHuGwXÂ_昆'Ĩģ H#ƈ DP#TMZdG촹Ҝ+pˤ 3 e6aϼoJ/[^c"[Od$ioxf`Xr&WPӄlU$`aI/0Hvpj!ga}* ְe}e+>9k_;T2 1؎h%,6Vl+V)KJ(- oY̱4 ).lFNl  ;4);wa9Y?l9%*Ϭ)ufgAw%CBh2o Ws)a`pxaSbSe2qYx|qлl4m-~Ou<۷\>$w4!PlqcwЗdJl${{X;' oiOL1҅w÷,{tC)ɹqĶP, Xc3Ty]'m̑ťh@ޯjmPВ+Mg/W( endstream endobj 108 0 obj << /Length 510 /Filter /FlateDecode >> stream xڽTMo0 W4˖,cMߺ2GvS$qa{~Hnl`-Cw,S##zlZ9#VT*r-̪"DiPK&]¶HHWRL!}ҟ`QUFN3:OXd6h'\hѲ}dHQȰk5xt` L=C@ \( bmy`yC#iRkUc*6uǜԽ@HluRc ZtAuhu-׬8| X< =`FfoЅ2T@R&wVOShT 2EW C۲0uSǹ Or\}`Z;)/yg RЉJ_s f(iǜ 0pSAA̶a;fF'{l9&o<=gHc'OWjLغR?K\)b::jy8֛}T endstream endobj 119 0 obj << /Length1 2013 /Length2 13487 /Length3 0 /Length 14720 /Filter /FlateDecode >> stream xڍP[ Cp'3Xpwנ!  $Hww.! Wrղ{?CG*a` uwa`cH)Krpٹ9Q4!.QPg?"`ˋM`Pppp8x9 t Ans2@J' XZ_#C@t 1A.V`f [? V..@;Ιj)pX`ߣ4 rh8X`bw~Iq7C/Jw`++pqAN99=! -NVÅ7+drAlA/J@/{>g3(řb׌h^Y\lW(=~6A{s0wujC\y1Y]<|af#o'_|/c}!Toguz`1s-!_`x_`/ 3w#hI1{:%%<ެ<\VN< ȿ`+ooW//#T^ 0;gO v$jk_?~/uuye]:ɻ^vAqxU!.fV˿Z-- jrvټ\/~Y-)co`זq@P(EJ<<ou4{b%2/%2 R?(_AA/ ©_ A¢_$^XL 쿈׹_z4a_rp2? ՟j/]Zy:Zܓ"^lm_z|ixiT/wOؗ?/:q󾠗r3ˮ\^h$ 9^:v|!@+(O1]2?ˈn K? 矦_b\З EM`✃PuuHM;4ݦN #")K*J"i{yCR|{CsZ˽σqf A}>r2VM-G'@&,'W~L^9#asj[hS  fMQ##3x`\^Md?S*3|s;TӹXgdNwa~BXT\hȉcQxx|mh8Pkc2,LmNrhV#'`8Y}+l 4XPCj'[K1; 9}~S8M]AnN}3/4jN рsXQe !f <ۈ’}|h=Rܣo,d$u+׼kȂy>x2&ZP~X>S#R?q﫤)H2lCH5_E!-9"E*XdT8ڮy'I≣зFX%`ic%\: l# l3)Cu!P"Rd`t=+:' ԇZڸ=k:*lRy S*[[dYmݍ\1f$lTc<-4< :L1p%wR]Tq1*ZM J-f7wD54R"tvΠf-bAUw6ϝ0kSMR %([$Gs1Ab a=Hޅ43i5^ڲa;K"+r` $1g6t 1"饓:|  7 S OM ~iKlݎ}倁V%\nj!؁U/& yIoDd:u=TnRH ysVtZxpbOu+e(bJ f u񈚓)jmgFHߓձ9]%5*5,e H ^^Fⴘr|,XPۖ}?fc1 pi{ړ>˕կcP Qtb^*9ŘRMG@DrB'f둫b3rWZ1s5w$Ǫ [eIQsJ&[vcx]'ВO?dq{:zunw1YQZ A[u vV `3ƀm}dsRdZ9mo7HLR3sЮN5}K)+2jp7bUhK@3%{g{Rd!9:*WC(gq#.kR<+iY_I:aN P(yw P#LN;VXf"+n#X0RySL>,/8>$h~o)̐<rB0 i8NXh dҸi` h܄HfUԝҮ~0 *k1;z0($/&s\P{dPN͗>wtr;';@Y7,7Fn6022ŭ$E$bV uZO瞯҄-՟#51F̪J8< k_k]8Q:jq!W``!fc _?x-#6o<#Z?2e{7| ~sڧ?Ќ\:8 D܍jތ"0:ҭ-$%t$H+K\ҳiO#5oGL%JUU/SL0kCF:BEwX?Tfj*XkmP3Wt&hM[f_oW&Xo;]P;]lk/s6z)9H T@D(u#y݊ӉPRgjƨTA)Y[ raFFATY{.^Am+T(}dYyYwc0}^( l|}>BlP525@ Gڇ:n8jK7Ox>m);־fHUT}kz#|JM;͸ iga5ڻ`o$6͕ rmrթ3,v:%d,k碙[Hv?/Q:^9h,.Wb?n!H<+3Pvf6k=$KgYZqe9T^l?`B Ju ;nDnO&S'߂}`SԜCcyrB/8|aAr|lݹT@z@'P;y)\n}Э!QDC׾Q~] &:kk}e+buP'DpfmO-V,Bc3qjUIe .iUZBs éuZt$ {2_gw,ө`$"ǿBgK>!xQGo ? % F4V:jioU LL6fi"팣bq|kt\I-Er, " uy@jMH w k˭R$䰃hVGkNJ֟o~˂5ħr!S4iϯð霿o#n bP7@&qC6722ūmKA[;rSڑ- [FaMbXŎ. X |5h)@>k6؏=adRBV{j9)z}Vj %E="ry\O26 ᨟>5e nn"i> ~\3)3*Zb VhX0%I6 ï|vf7sNר&Cr)VN@jd,2V(.(ʔ2h u5]=4IE:O\DPڛܹ2r%Hvd{@R@#A߲v0Ԛ/G m'#Oe:'J :zY/0xةeCx3~zNmEK@Lat&>d JH&, ^z6̌fD4LһVFKS+,}JM] I@d-pk+c~=25';eE ,d aL4ϒʋqIuqžԩoN+ h.UwcdeKjCa@4=K 10b2mozFwPڤsXlȵ9kB•?t%o' )+Z(A/ dF`(4'&$TbT D䭸u=S##3}&S `/T}@XsCXt |{OOt^re o淦.ٲgg[5o">8527oٳZ+XȣۡˑWhվǬNQ %,})HjMPE,@?x~^ʦ[Xu&-4gL60l; )x,-[?rYX^G=:#&77>!sEuhhqH'e%ݼZe'NEdc{)&C=`^o<^y{}Xd !B?C5YcǙn(̔key^D}Ws"AP_ي?lmu"0m%&BYU꘵@c'pwC#Y` hK]4;'2{GHpK{ hͿgҏ*ʠ';&l)^r̙cb>|`.sSw "-4n9m" LG$p|ޯǣwvz/_}@ťg 'sr4! “ *YSc%mԋl&W$rۨIGqux1M20U_CC:8-;jEO5q$\3zpO?UVLj~Gx)̱`E쒞IV5%f.WX(Al<G^]vui?CT\< h۪}䥈b6xç.Caz|+@O[b0W?xc ZYv'?w:|L9Yi"懑ǞŴ-BǎE5ѡmQ(TIPծfal++֠I4 ^ewa;⍔˪eFiD$4Zi m-5=iBj`Kky OGzZK]BԅV멏Roߔ݇-i$(0@ZgPb-\):4E?auWO ͩ!-mq~PwY{X~Z{ӊEmDϟ4Øo wҙx/֍<̛. x(Wųm;vP(H387ב-@2-CFP˫=e`csScI$+/"&Q J+/;O庛zS=t풺w~rL'Ef<{l{0~mNU/KI103 x>д *ԶqQGڼ&;։}L'wh!xQ<)XJiFB{@ )s{}[?t om7Ԓh n.[Ig}Fv;McÊ&VVCA8 GC;70y{&F|Z_"?]Zsq MQz<-pu A+P ,"J],>i2\ ʦ͢KV3}*cQ%sh$'yx뫙AQq^ cHTn2Jt^u}MwrjLX= OZ׊ϓgµ!z/7wK}`YAN@Y~@zpB4l$9M2Ko"8N5 }jBs"yPEJ8&L,d~!#g2^h: ]"brڹe^^FryݸwZ[]7h^ɬRu,$[{.nZ,ا3ԨrGdFD&]:eMAR jT!eEQ;+mzv{!+gÖRw~D"bS&B VKٱe Q2er-M_Y,~RN=A:pV_#x80%,vљ@PC>] {j#\"RKV:>D[Q.^ߎk0Yn97}1ؐ*})~5D.۾,P³Mՙmkj"bLW=Z qWCD؆AjS<{9߼tv L7H(9zЙۧ/yoaC0G;a^Y6;XdzAxP'Hx|yo䗿F}IXv iy' &-F%aW9a%7ٻ=A[F,>{P ]ryʈЉ0_ܰ. ~C2b^l=sB lyuE3<ZOYY%$ \x3lzXD?<|lEzICN^Fl`^\Y*W*Aӡu`t bs\>:\#ݹ8bvV.-*ʯQ2}Qťcߥ".)ϜKL,|[OR#E3-8{xcn仵Y8TpzCs_(ՇX FѾ(-D{{ }41"E1#M$@OyLZM}ُV+,ywJań],=d-?F˓J`m!(Lb!4|N(&P*Ӥh=vxAi)|u3N{Tie0 &=c"xD\Ǎ')zȚ5-1I:OdQ1rjz[T:f F/2!PtƼMP3q ,Ǣq϶xӭ{HpJ s7&uW׈*x*Vb@`% C`'tФ.¦x}~&TŠ0);1SPPmU.EZ="J 㨧z;# ؇lW+$ OUaQ-Pê*jߙvs7.-6EEXةG ]l>?|uw~lH֥[-$Tqv7DLm4B3c`OB|/=O`d9 @s頀E\Ga$P4DEr7~2"C!;=Va~~FR)^2)'C>vA5)cqLr#ӋK, ,jJ˭(G(ȋ5c4Όi-{qA'i)Jiܥ,p7"()cTfd~*Nvwjp[cCOo3 /#9U;8Z)AA>m$e+ah _|o3/bp CY1czF$'vFEV15zU'UW|X'_mpS 4\H.OTP (re);!qOѽ©D3phw{'VNt9j>}P ֵ-/lH E׽q`?EiBu,<-gֶH8%o @rO?͋4,2B%#Th2K*:bcrrT,̸:;P $0- ,iMŒ1۞ZYDBŤda>=x+]H'N{croZ##!jf{5–W:sEb3W!0)2I.OJ9$fyPg]f52t0[NlQ#U@C "N)IO _tGfuUV*+3#Db_itQ{TгeT/ӯ Fx*sBS<& J2eYg9`caG":#pR?OˆW+ˑl,)N=Q~\.ؘcYdPڣR[>åg ĿEj&f)BDj7.PzQiu`TBР| M*ٰ:`}a)Pf|hu^Y0`ʴ5ꋼ#1!L|RE-Zz=b(͗b KGrY 1F汐*)L,v+ӄ存"]N_)eRFloFtl.gn ecqvQD4TXYoST 3>E,i(e5MA!`x34Elz7yCr$*^l?Un|7Dȯ]z-#("dڃ_"I24Ԣ.JP$fFܤ2 e~/|~5R;lZ\Gtxu?%܈ZoӸ΀omT$184K1'dJg8We|ID9|w2eQs/`P?cQ*_k]ZsMU n3sC -=Br"14:Ҿ-&-xOqqK}-bh D\?mEp= 5 EVbZ@^lH}>J%FuϦn5~":V{y9q0w`e W(}HQt_R?`IA.` 4 K'@t@S&v ?" tB1TySs!Qr& cc QBD}1F+jK`SΞ{J vd> 2b%meIםj]|}ּ)D -հmjlzy<[Lʞx5wI{?b0dcI'i{2.GïVܧfWV' k2%VNeBBXCRuH EZO|&BhI͐UHz0d>C[Rr|X܆Z|۹v7'D*Q!U {!g(Gg955ʨSЈvz IZ1:)9=B^{ɩvjʘ=hAOW$C؄SD 8Ov4QL7"&Z>Gp)̟TG|ϧI1nOct ?akB:-'JJɀdFrű:Yێdk|oה*o5 #\ ݿʻ15`WKbI.|DSgX{9''t W% +i|̲#!~λ%X'L5E+XewC8>fDc1c8w: 7]Gʘ0 RgͫZ%e=ڃZ+GX#kݟܥu%vVX;*QqzIڈ|J A4_QV뻉o7uEB ތt )a#P3ts@GfC e},EUԲ#i@Bj"UbFhnSiE5\̏޻ԯΑpn0F|rd#׳/b7H=-GgL-nW~fZxh4jf`,|^C57'2c0ybIg#<1聴r{OvS} Wȼ01XUw,Wx 珵C8#R?#GZGFig F;w $ oہ &bWvO+, s$Q|NlTҸ]PUp/.s,֦_``ccbghVRtUWYҩO? "_-Ji!f %G9ZeQ ~Ck<_]i)uPm1.C_Oj˼em?p6ƥ@<^ьJ)Xu?ǖp-i`v~ڷ]񻙾5 >L;:=wB #[]14=QYF#…pQ֦z6~LG<%:]0_#bTos¦[+\O˴<Ũ.;STUxlQ;6;-֩.KoI%aig' i-pb~e 40gʗs耰*F |z\DG'u( K7]pGիt_pPUhXFi2 ,$߄eZXe)Rr3I΋A&߲Pw^/ k4N_(-->`WUl$Ih"P2EHXPo<^ BL^grO̱$hhS<jDt7tRrϷ1ѪXx #崲QLuO:XH|{(l}Orᄆ^O[T,BGʰ?Uch,M+$ 1ЩOҌ¯.+čoE(kͫ4KY95oq/?s<ƺ`ȟzԤ/wxa3IlɌ4nVn$U;X]0bJ,oZb0ljZA!0GA%[:;BCJ(TLh9Lr;##*8N뗱Va~ (V@i3,Ղ,*Jto^EO|^+o̒_f0Z> stream xڌtk6'ͦm;il{bf۶jl'M~YkfljoRby%ZAc[C-#@XF LOJldThnk/aH&bH9[l\\ &1udR6@GxRa[;wsS3'P 00rr:d ̀֠FV%[#s1sr㢧wuu3vu0壤;@1wYkߕ+ٚ88 lc t$rv@67F:w s l lmL&V@4 -dsTs4r0srs4]"0. Z[m1wNd-ml]m<LmM~alGbcn  HGf t200svŠ =l&"&@  <## `45$Aw0wh1v?zX1kš*WNH IebeprpYE?,Jژ8& v)s6(K@şf`e0}1?o_. ;m7!1g+?jks+v 2S`MՀYJ:΁?m4w3w˛;,U~2+s[@ted 9A :8R cbe883:@v@Ogcr:(+^o^Ћb#^bKAz?@/|@\ ?EqqA(ʮA|j(?31tv#V$5@1t001&N>'(@%XAl@;?k?4~/L @#+ƶVVqIF&I8 V?_OHO?20'7 hNfvf@Yd",Au_jjȬ WAT2 gR=^mk,Y@Akdng,FY9;+>HbgζGؿjϟ_c;6Uh_gt;97J\m/j˿ / /rH@S]hj_O+t/qYW ΐPz.9;?"&RVf; &nR,x7+>y=*N/NbL|?'|VY`KS4ޙY>޵Oܭde4d~Wa3s4mJ,iaG'ZB8* 7ٛ7"XjxH|O 9eL]x$x7SdBIR8 Esc VWd\x_#[$ `X2/V$؛ ikY8wj=`)jԩBͰklzYC jɐvM#qִ~ĬoD%ln2ͨq?L<6$9{)1rMeyqmn;й7hl HZ_{PՒ%@M6?SbFZv3s< N1ezAk+}X6fJɛ`ey6GwQcⓛ\"k衛RaQ!{~u:ɣe+1UBAJ&#}>JY׋U L > ͳ7W2/Jɾ ݂A78F\08q]sXƩ j;zqnjw WAZ/cO__k9&Ѭ`SȀs0f~>f)yp;K!zq?S}$%q%gv++ 5NNɦM{L]xk"ʐƈ33O6ܰXv ˹xBʲ/t#܎&cbԷ37C ǹxF4z1$)n-|_5hm:]xfD`g^0;0}_! ;Oo2;5~B9'$;L.HG"uˑ :BIHq 5S;%07itԁRn\y{ފ>NV*X%:OT&D^1sSЭh6>/yr|kR"j(ji-L#T }v?eKhRRk dhk)ˆ OH/Hr|t#$^5%RĕS{_ʭح vwOlU)T6d!wB:K / jɮeF ^+RK^qTU YnƳMAkcZZ̊@re&EJ:؄l}ѲPIKf_}-G7Z 廄Wm$Y(9B'o()3-EA 눅"}/`H۴]6[˘wH Q4yf4B݆*fKE-MV h]3tKK%Ѩ]"a+]]QZ{Nu;F/2_09(vXrVr'J5$+.K඲vc-ЈZ (b@k(8ELV~kΚO}²j9JC@K>)?>ᵫ8)+wq>LӪՈ1fx_7]l[)_I5o#-l eJl_.^GgfvYJjUI`5eTHų s;+#i9m'SNZEA%qdݛܪTr8[>\RN|7*uFU/$IU}K*ebo(lZ'R̎\@*ܣռQDhג!Q:٩:ɒK:>О >PfAfΰK*{5ڥė@6p[X;j[S +lfN\LNKKdbĮn4~ˈda!7};!OüێvyuX_*g6!%8VƧ[5NEK.i4Se,<9 /Az`@-8٘]?|ʔlxX\rdj1E%'5ZE崙K'g+%,(pz1!*(ELԻe'կqrv9#NF-s[ Q}5~eqaRi 3#6vQQh_+),vxȩ75ZS6 9D3􂉁GYf~ ՖdڠsR %mش u>AȿdI0%oVNtl 4Jsr-?mTwnֽl:gbt1fj<itbۿx4ؠyB2(Tt~xOC6|H@T?P3(-TU:Ijkel\=X -3Gk=IƢN\saaFپ Bᅦ R;3ܾ-Z l *hp@v '8rƂڏ*[0XLCڞdt{LяQJ$o0BED^ILeg\",qY="< &sG`S'3dc<oCdl;kKtW|`˄DXAnn21Z$"Gӽ}^rtJ-B1iCuiL1يP7V aRVb}?ۨ^2փ&Ŗ,1e=Uuv{[&{ى[6z^2Jd$(!.%4-?wb 7xfgW쒨2Ӑ9ϻfl~&[qЭ_2԰#E5<$[X\$dWXC&Ƚ I6﵇IޏfcH f W@=W+>ONX̷+7Xi!Bm?[q™ >KsC\1~CT}>VZ#gn"&{c/Cp#ȣIHD-V*϶./_j?*z;4bI)'ߎԭXۡy 6`ۙo.Y@V %3ΤoC Zy)n_Xٿrh< ezmqXmqY%Xn\_IQA nUvE:rgY!,{-e^)A< bܓ3@%W ;ojSk~EQtYBлF*^rWE*$uSK161/uy4ܬ#(|  氁ew!v!3z(C c|?g=ҕGU%e+Mz`G+uy3Y\h]+veP~%1 H ge*@{H|c1C&0i蠝Z8 n>I}V΀QFI/? |%OsC ?ǢNs)=n%)A1R55 sQj*rXZ+/ґF`ĥH FP@.ZdaP9]c)SCbti-{w.Flc}JE73e-`;O'ny 4P{W鍫p[βB('Q8j%Xl4_ގci+ޜ ];K)a}0w{8UyHaq)ֻeI G;aӴ;'G˲ 󥄚nnn}ظ߹oQbW>o㒢ԘF9F7nZ Zn goy7M4lAR$/0W1DC2ôgZ,u^GpPLj*%*p䡾bW-.gA{lvhW/Ub|e_0yKAQKtNz4~=˿& 9P͠m-d#W$h(  'Ȋ_Nͅ7З7NYg>ćm!<5-HAQXͶ'՟kswp.Ѫm[z7OE9qol6^UŒQw&&IXN\X_7W\Qwh!MC|(jVS;P?cɺ9߀!A%}4ҖG(Nq >dz3q#}DV2?&-/q7xYwn_4PkƦKch{"`Jkk 99݀F0:3Ր-r-Jt{ʟ?. XfX2 풽ԭy1k~Dyoj!3-PHޘr;_w:f7enzA8^bWf[PkEyQZ6 ;ћ8s:MY}"3\o>bQÆR aAgFM ͝ii5%ܼ+#FH O%/*QTq`{gLfyWZj)< ӧBrꊇǽ vp[møk@Z*fn}y\x] uqsGs37Y&cQ4}2wjw.:7_) zG KvߔNWzqZACItUOxQH:;a˪_h' Py;`[jbU^ 3*hY yi@* `i'plwvEsOH>f@\K$4_d3Z{q\٣Oc4"'!ؾ}6at51n~O\0 tKldzjWxl I*\N͞qhx={aA@ItD W k~ f`HBKYX"m:^i3S ] pCѡ=]xˋf=K7 :ibRq&=?!ĵ4~0ĚȖDJhyz =;r;)Ŕ HkI):-#a+vU33]S.]B]9G~ L YƢ}qALydq\cVӂYzrBGBw9#xO0F`V,` ~9*S0yRӎ ٞ`q뤧lAe\HQlH-J$3 ,F';%=G43ڕL=%@xl[}6k* uD#rN7ǬZ}՛hQݷ\tfï+t<ncx(/SF(\2gXX4MY&ِv9-"Q .RWb.jrZdog8oP:,pbʱ~TABHv:19Hv4W*H&GkNi2E1? U; &<̑8 3vy >ByK[B sj)%R4Ѵr R:^+LIzQ{T_Rʧ&0CGpB#@?-~bzz&љ*RJ3gtrfF Fj3vb9 &hg7t疾u1xʶ,˟taruJɟ4*60l;i-dX+T0Sxֆ|cyS+]*?>W%s_kB{3~ 8RE 'ۿݘİ(V OyldH#_d S:EW{67rIJ;،Wrm@ddLo6A\ (S` > s N;q4vflf~ .[@Z"tOPgG)J.z9Ai) LŎ@Vi#{qiYˑg)l:,ެ겲;sөy4d4ZjSÓD4Q]U -c LUd3~Y\͑a xnOUzAD)vBַ{9wBb]p~V*WU + @ġi h4N:߇P$ iD(eG;`Y{a־sڇCWؒfS*f!KWkrYm/JdaAhWQW疭 {lB@"˭N+3J9'ypvdSS" ӝzgЌUN( &WG5̝$+S6UQUx=lGa_C,;g£›GY*-"lშgG%D¨/\}a_C :O HH鸓>sJ( !*c5֏}J@k vh56䘫Ɵֹ] eP:~JppsL ) J .wNԳ<ÆZ,q<fH(o{҄ dT4M#pO* &pp/XiUw \WqTi_/N[20\Ϫ`>3'MGO[D!.!B3I%CjTjR9AI1ɲ +OHZ8wڅ ޤ^?t;=/4&*H|'Hz݀fۂ#H˻K- Ch6y8{>;;{4lڎQ}`+fdD2֩_ (~T3 A4W&1Kc( Ƭ'm(3s$9HO(uCx'J1{SWxq9"%ɉ t#ЍNepѐ-U ; ~'}] Gz>|NpȋS ]Rv*-甫.@;nOiTzL?0Ø:w~$B#L#>-Kŵ}Ah(Rϝك~L>UΜ"H|8rK-(8wU6[)gZ)3\q Ϭ!sqL1R׺_IݖH{}t AG@=*3F21`1=25q\mD  #j w2E4qiQio^ 良jNl(x*sr֦j-r!|[$[4%J 9b"a=.[hC ^k*i_+։XlL-AC׶F횸]Dq7!Wb &zm |2Mgڴlu|VlJX1'9Al,L.[\xfgFu{ܽku-=Xkiý4$>^Has1I$ySZv6&o8;!+{|_Η3~i@JP~󾀏_kw$_&$s|]z;iՏi%dDh[r{փmrf&^ɪr@4& UcˇxG$ji#*x 0%;SЮ32ݮ124:4B+-!$W祠*Mxr0U,vH1L".8ԂÞO1Sٹ\\T&7xICM=(je8Wsbu&k-NE퍽MF [>)Ü Ub'])\kԬ: Ý[?>nj134kFb vU{*5[b2*o.K̼zR=eEGWFs/jy< @"=ZdxI]sPE͖Wl 3 ) (n<f-8=\fY$ֲ\slnp|sd(qyL}iāL)qCƱ?S;j:#/AdH-,)CkD(7K(Hw$O,5O~/fwCN]G&ڛ.jKlj>CU7]Xiٵ9d'Y%ZVF zLU&TeW痭q$m<δ: !ڵ# ,ƛc]U>Nv/-kqyO?};p9 >)zVVF`zTV2[8VDBR6r>K6+<+&w06_ ¤ vQ:!)7DpoPf⮱'}='bGK/E$mnC^٦V(8_ka0>SmEʷQiC+"$aءL=>b@-d/Mt)cV3^FjFiߩG/h VIeU~z2aKf#ÝU)rW4}EATc|IP5Ǖ6. 9͙t;À|!*p#7dGo {#s?;ʹƅ"icGW*7%q|)J4AG_#oKKQ|V?Bb Ԣ?3۳V!2Rc " ro`w\ðN pk6456u5>D7t^ e±hHt=9 vj4<RHNJQ"N:Tͽr| T\g7ҩog%>dHm1+QݕCb,SG_qjk(hǺ{YdxVɥ2F3h`^vYs_bSw!o>Âpçt\F.EH)NEDh+VÅ{.ZwO;P!7ExL;,w RHcvЬĝKךd| ?}/Go3M jW2/0ciuVbkXROLHqq[,ή 4__ձn#|Oe0rHR8s^RWR*y̻$/[!z /?2gV媂 sIw/$őy1l2TbyY*9ݚ?r~R #3҈Ruz8+nFM{!ICpBA= ~/ʕ~88_O!D0DF6%eX'.`j`wQ]@^ұ`j@BgbĻo'q[G*Ԇr/L#}w>طkna7]̐$w L$42->ODOIlΈ?:lZ|Ë~P%JMos=̫LSC껉Y~+ɲ8nN do+<nu}^W IRNw'`Q]%1mi1jBׁIwnt_{s=a֫4pIK)RcpF2YKl?DShiC;<61EYx&^处JQr.;AɒNbY"H $ҀLlu'wS~܍hm|KT!C~k\.RZP.b3ʑM:^*;'UHO2Wr 8CXA)3A9 L ~\'t'<`$3Q5Nn6lRp^i$RVn5]C [8(»8G=ǟmn~Fi7]9C'`Tg'R6BkQ̲Ȏ/e֘\uAWy~L&J- .,J9U=Bz\]\ fE}܍@%v{&YEs>l8 q>h :bm社G?kcN#{P)0[=l3˗l@}l|32/Qs$ Q aDG]GNv2yZ"*MPK7jip4-pbB23ݵ&E+_ɣ,8& +Ai95\q/@*ܱ^;k@\S0RGGAkQI0,¢Yk*T~F&+:Sv#o!T֬f Bc%c=i/B%![*G0!KCL'Ȇ^9LO Y/qD(?d!o^)i3g SXXxc j i*H=\6jm!EZ0clD?yAUFsI p$#ܻnLȄk*JQK %x7y0ckE­tWȖsu@j'cdl |O[׬N).Q}iʟL<`Ļpr* yMȕ]#أ@97KôI'{>Ü{dOA-sŽ~":kI8zȆ&A$P;]Л&]e|;%V+ڥǿYv~f4]xr)WBP01eQdLUXOs׆{Ba噃™o08ENSa9Zp^PL%O ocews ܈_ ez>+Ql{ԭ2B?퉹MM eAGjYg㊚ERxahs};T iegx`7.3) %5͚] )SJ1 X*g9HeH7vesi(: f AMv>Rc}};:^:s+ `Msm˝j1Y23쬼P77#%|Vߟ7\>b«D)sdO׼_eX~L7P\:5!eJŖVgt?oΫ.:.t6fL8? y2ȼV5c0'3d6/xAK6Tx-|걖,O׫ L NݴFZ=*eL_#Xh]CIDpK4M*5<y3soUfY-O 'fY'< D-ĔD>07I/:S:]{685>@lj{4}KFEY0N9,@JJT}oΚR-T=OճJFw$>Bcj]Q^q !⒳%S#8]vN!lU|ELQ4޴~C$t|~ g2VIo,I_~͔YaaR;$+>ɼ;ئăc~I`~p Zy[riҀVPIj[%BnW$dw!lW )5HgC>M &{+J8_=SQ>!ju3a W$(~/v@^Ju(94TIOu^A[dG7hSw=dw~({V223> @FX:~J4%툅&&RDEWlЖRyX()JODO ]=p[7TԕT}E4b^SUI>N7;fy"DFQ?Rad9 F$ ThˬJFOƖ"uMGg"qk{ՙvo [T:}Hl%4*;C(^%Ň7"kV׏!^D*rC[d69!mٕoҷȃKwmm*:I(RE;=EȃX]` 1`~ va iAh r7QW󬅋''jQa_>HX;L}cKf0RyC ڭqڃl] sQ)Db>;Q?VR"e\u-s!&k~]= VX?|f ,37>g5x6͘7w:IٗܲWq.<"ˉSfW'1+FLT kZv_';Kx13.m~t9E'SkZhPsu+X_AF|(o$Wy8E$Y9 8dS?=N1BmqZc賰" N #BロE_Hdjho]G\>Ai?Px#Ϟ:iAExVǏMՙkq17Uor^"3?  XD"Hҧ7ըNNPH(9%v}ravoN"qн!Ni5tp@l{*`B6c>tWH#I6C3ٖ6;%A m1Ww=}ߚ7=n39(BS<8~vKܰ:Ӂd?ㅮ8 *c{fEa8C?gvpcbA_.rO8돋&Ř>dw obHͺs.ڛ"9>o:ůE >^ zA yS+dDIcvI~ր9O!l.9ׁ|6J2[!$@ӯO& %4 v mIy_mtT^T YW}9obqcb@/3xZS" I$gɬ ׸_\2e*<ߣ| !QߜkH@t]j s!JUu Vn@LeC@7گHeKאi-'_k5k6aŲ&wN%Z(x}@`Q&U )Pa(ob9×*,>?蓓H5ͮt^ڗC)Z3;|b*2~\vUk(]Y:鑇ܹ*"&^CSqv1)\d) 7u;0OzI5{*_IV4^K!G|iA I>ɡ ƞ%9gۉӇRJ;KaveE5kifs;R6?w3.J*⠣[LAɅȓFsRiwҬ|ʏ%ٔ&+i0b05 %cM 3ѥ51KKJw39\RW0df05]0uL󦞦7*2Ǥt1t4aHõ gsi}/uOE%b"H (drIxcb&:f`+0٨ra(ɫ-1Xؑr%jkk#1-V99ouJ 9:e tKSPβ*l1ᄀ՘$)L0LA#l +4Ipw`!x@ɂ譹icMی4JsLdkXCsY G,k?dF]xBxSh{:4*o$@9:JevN,=;]-zڷ`9LywU_zc7NmvBs 㝅a5|F_YqJED\SAWpgڣK*qKC%VK"wv љ/%ұ g-a~|׍z\Znh4pKH0p#Q&'[Ōjڲ6[xn }E!yAFEZ=!{:6cAtWAS7@y.ćv>W.hu9Jz湲~Ã͖xh85gŁ;YæۓY87 YW-!?Y"q\"/Kq' HY}kR. P. tPX ~Mt{f[G nD&clyBﻝsY7ƩG^Qm80*\E"E}1Q fj s 3RڄpHZE'I{ ^E]IaBIʇ 'I{5zsp7=}FrT_X.2qʮMd=czj "AP95ރ;PG1`ބ WWSy€וG3Z/XK| G'>[jK%(PZ9E^|!fql .]=z{tW(l?LcA^IRbhe8}qQ]J;)aoDk5$aټvԟ+!6Kw0Tĕe;G.GJ?8xjrKZb;KN2 ҬE_(nBd1DiuTT *\idZ}/7Vb~vQe;JH?ȾAl ;RCWꠞu$Ҳz]`ם "n? dغ QqEt;ZUn4c_[9!{ЦatdFޢXA | T A.J316:D߱% XʎCen  endstream endobj 123 0 obj << /Length1 1616 /Length2 9317 /Length3 0 /Length 10377 /Filter /FlateDecode >> stream xڍP.C ,wwwaf,k N Kp ={W[_|vt0jrHl@ 0([ #~͋Ȩ;c20ȿ d@G5Nx8x^xDxE1=uN rd d @k(@<v€`B09."\\^^^wN ; wAn [Пqb2uavp/k7Q P[p@XOv_p/߁?@ '6vv=[{Zm  ~,܁n`;;w\q\F:lVNF x  ;5,qU&إ$35,{$-͸?}䏆qRM#v0XѻKM K"\t]M{em?-7a2dg֝+tl'Y^gwEkRB{/Jea)1ҙ{0Nݸ7Aul9֣ :toĎ!AY,kʻ- {?ʃ8N~r. p[^7!=?]t'sݸ/*͢kAyݿ{J.߷Cs^$*9&=(5t7K, [ |R-U,_HcXˉ3߰xE*_'~V L_x}g=[$'=dx%>Q['R~ppg_DN]Rقv/\1piLBpK9LF!{Jo=D 5_ﻝAo}3P,FuW4yqt%lOHf 90&vo$9Kqͬ TŸ55^EB/i4]ύCW,Zc]g8?{.X(`ɫ.nH\5,G|;j\1mE:3=9(_KM!ŗq\׉F1?ҕ=ߤpYdi{#nf<ćeI(IO_| Xx<aڨȍ{Hm+UAmrxUaI-ំ"(V"F`T[<3dqA^+{y; ˚P-uNx@.؊OrU:^AI՜l>Ly~ӏ%9X$MB›{f? i98$oq:0%vA*.^W3 hMcHwF6jnQFs˅dj'["rOʠ  tTI:MFdE:sJI zevcMgaHaVɼKS.KGMILaNW_l|,jQ-B yv[ &6>>:Mpb/C-/g> =^ :ilH;SyB12k۴cH(V5TG!G $fK❆nPt':q(lϪHBsJܸmA9UI4ӠZx[?66/OYe&RW쉣n5&U@ф{&3 mXېlv}!@(jUA5Ees8>2#2,.òL^xU?!IcQ8`AIɖ {˘64Bd (2RYрv!]'3CE=!BVUp*Q*їqg|mBr[fZkƲ"Z _4rS/xJ{+tj]SINON!&֨1D!ϟ̥حĦuz? p/)5CNJ5d~~&!Sw+" aY>hSs+4G\s*#oK.  8;_83B=o^3u]YO_9Ҟ;}iF^%z~ Q]܏SqJ.W՚}W560L)Țt.v)ƻq4YUT bb}k;~mXzv61o1ZMɃSe8' j7;sDbFk Q1ߥ"r?]B|v:j;+ Ȗ,7B]LDZV4!kpg MWdOl:FMgCQ$c"RQد,ϙ +e ^٧a=>u&W`d5|U#NO{Qnß nK4J ]xn:k',u_rm2xʿ*!2^qVDۻKO XwxE=àR5tZ__TKFs~ FdtO.tP`yrQ,݋R~b @wMVAƋEhڤS ՙ{&651E7h} eV=N:}>c:X =バ|&Z3,:MV2  Pj=⾤/O&nسYlD'j>A D-?6v1!6N{/SZ-^6XP /HtXv'Q֊_ߡCHrVjNxbfA69Vemj=tpFKdIs%(#B -b3w-%奙FBfOfCn1ɠ^u$v f[Nq_ynFb-o9:Wm/bvoMץ*vPZݞ{A>xF:F@E~dNB+,_MFf~يӥo|k,+Pr3xK\WB$ [`FSr{Q~$. +њ++Z8KEi>ٹUj#/[&`!0JZE4]ӮBR(FD?8j*q=F7ؼ5ymJKE=h>nK; mbJ1w,cP"IVm: +OHaU;){ZDҥ(9[} un&;.݃o!{6&y\K,+ 鑨 1";Z%wٞO(|x^Oy+S,ӨFcE&aE uԆ&4I Q-: vʼnk:$nQbcBյ_Y/>J\9ya'oqهH ';40.p>DW7R6'sG`%)W^%iiwh Bqƪ8a"(% *n[D2/vL=E~2 -cjlɯ?P. yDvquJQn7eԀy$5WX:Cڍ N b*& v,I@Jrps\: y?&9PI3p|9{L+D C G. qa`a^;w!әO}KLȂwmK>Uu[9KlN_'I=;ٲ"d =*{:{u Y9 R2ܝ=,"dcu19=dA+6]lZ)¾[tR~@|o`4%PQy/Mgsei6'K 3.N8 =wLkK Vq u h蚻KwĒ[( e>Ģ0e!\["$(l`YLv$]C_5 ;~vJ[7PBw 4!|MvYҜf~X-/ ^.$a!a$b0g0sR*G>ȉ489ACABgeFu_j+,ZKwCO7޵EZaph+LǑ#t! Ӷ.,'S}o !fg_9.4esc#Zߠ APsH$R3[FqXuꓬy7.ۭ|kr3Uv{U?O0q]DP{t٪nxދʶ7dKu}#-mNΈVQ0 =關*LͭLO]JB97z0M2}#,,Ge2Nwbj3䫣NA(^ ?넋; g^Ur,G&[(" vb. y\JqwSR5+e"wT~_}cG =}K:n[ORÂ?#Ix:]9J~tU Kd\05Sf{A<`r-8,-|G/Ԑk$Y2#%7Lzi*϶|-|/6Zf'76'mEQA_xE}r|FaY:_WRYB&{6d+ĎV\PW MDhCŀYh.}R[n'CJ݂\ c#6 m-Cc+{\;{T28t$דpuKsHP='4_i8:Er(].[jSu2-#I;4yC/\:@C2HmhHfګs6z`"rmEo]9E*u6C>!.iMVaM4=O`*@(,ivsR4GP,My6+ F*s{nAINFx!ŨWT Xl$ãֈh5O|(es ӭ$DJ;t`Wfe9K18YfWV`S&!82ˤAۇ;-7MTtLOܰ]:jj8ߋL:0T6bjMoBX-fg0,;i9~q+uD"s6q:D?qgdvUf$⤴bG-dO6b^1Q"_TdԃSey7Gn+ɉJS1 T4Yy:ym-5_Nşo GkS%[,AIذwo82=7Fruz*vEoaU 8{$E$W6I:?Fcʉx n?=KF7*! ?[/l㼳qA#H'h^>% 20KfؖdL_ L MbŜ`N ,2uO # MNX]$ nk)rcr™%en~'1I;bn+e#X;Gf04u, kG9%B3i͘-_ % C/bA, 8ڟ@ ޽LQ N[Dp |O$ I̪VWc\L)-rbDbv⭛}z4m&]+A] ĤXt/݆ BK8T9tK3A w8͔+6պhϒ[?7UD]*n=NBis/H&zJH=;30-ͲÍҡwCL {}OϐIXd`A K+Fؒ z?@|W&_$̄m-ʢW5U-n$,#}-LUtHv7zg~~(Ɂiv 6u8MUƻ o%? 0!@hma14mTFTLH^*|mBBh 1U xDk度64J~rXzu.WA H0siN0lV"CfpU\\4=S懊9o2{Po\.; r=3z2ɡߒԈd.ag?F]h%AK]v#:S߂HZSin@?_Ǭl$ho+wUm"(f :hҿGG}iDUJfxzsM{LJ׸}"Άho,RQ9|؝3q󤴌҇.Hg4:fKLJ҅"qi.rc3F4;GG)v3DA9}G"x(O?|^4w\A\]',SVY>hNTؔ1ULp+{hLr j_ 0v%^+myǵRanC**[kg3ΤJw5$gq @jrh2Fݫ(~c*9ZLgNJZ‡~o5 xl.rr5OSG %"ͻQt؍#=݂l$]W$z+HRMbUd~JrFfWӷ*ii:)2h 9+)L1I%FE O|( &y|WcZ>op%zꞢPt^e]Q߆pdlȼJH/ib5n]a{.Q[Lr7:!Q/z?PAx6Mg- endstream endobj 125 0 obj << /Length1 2498 /Length2 16070 /Length3 0 /Length 17540 /Filter /FlateDecode >> stream xڌvuT[.ݍtm[Rn6n$KCRS@CCnyy{1gg͞뷨UԙL,l Eu v6' 5GDrGh I97[;'GW ?Dg;s QK88z9,\_=1;3hj{43;^eNQÃ΅R r]@sJ&vcAhX\V;Xz8`- h>foteʎ@ )s9&ffv&^ {KPV`qte؛&غ8ϛlML7HL9'C3g ẁ -eo.`gwuA$h?-w-@S1wsdմ9e%,n666>pcNo'^<|T~ ;;;d 0ZXc8<zl!d?;zhV5I5eEG-. a`0sprxy<<6bO :*ko;^p'f ߶ w}6n63/k[_ _ ;8vsx)7U +Anv[+j^ 1{Kh3p-H<* W3oﵳU\@3;ҁw|/JUqpLMC a/9y;,~*[7AV?*eJAV?*=A` ؃`U=A`j؃`4A`&ibsqrCc)&f6.&.VH9:msGc\C7؁-[bg'3j_ v+O~#'7F!C j[S,X-#S.pV^V@12п 8rAp_l18gSG.ܟv9?J`nv+Y SD>K,X:U\":ں+ O :9M+v_efg78b过|1kJ |Z95\]=ul_ { x =@翍]e ._E? 4CZs0 n#`Ѽyu#BbQ·Jĕw͗-2b%R G$a/%AxR'$r@7qK{ 5 sJ^vC-"1Or GDZVP?GϏKYP$uPX*\g=.r-vE;\8*L;$3S"Vo; "?]2 .*@'|vU pI*;hQ6paCIF<*h(gZ4k4Ør&W)J74O*LbA+r@&D6~G'ШQi6iu {= 23d^/]ȾܛRz|m-&I[f}Sg廕^u㊓_'I9 *^Q '`L,@>O+:0p@ G'(2~-b?`D*RˑEw`̗ii|8g<6ΦMum&&}i툅ח6dH 4o~w P×S?u#3Cy-ro?F7ڐD_(n$ ҈TW2q"֮l^Ҵ0He WY*hf]׬3YYgl]::2^NyIFïCvֽ-8O(,4{nj/wR+@s#V6?AOեxP5P@Zʋ+uAkEi#$WdA3S1^ĺ5HyQqjw42tCʈ䚊q'Mx})1ow%!J)ߗeփBzD^OI἟ŰSuI,O_OwO|}Tjoi#m|)F(vLsf̓Էha7:ms G8em][L0?IipR>> 6Abэj d1CSP'͆anC':W۵JIOіF*b-b"9-+)ycRjJx w7\&S`׺0BNy. $p\8X'YHS^œ =`adR[p TÂ{,z]1*kt1*dK%?䤹k8^cf+7 d?zN<6xs=+;b xK@T,|ByG4xDFXt$A7mW8}'"p.%r[OTQRMǨHt?#^@J172tv@v%{fݽkVxtIlF9y UaH @*7oZSʈ!Cı4FD#5Ni m='jX>#Mm5lOnR(^ݲŦK^ + Kފʦv\h5WX\M$Zg22*9j舌߮:pAd`l~~0;KGx̟Ќ( 8lI2a%BƗ1>Prd" NK5`y[RCvwV~Y?ChGVvEҚp%G)&ӂT Frڹ!1~vS,b rw}֞SXC]k"601KFH-~FAeEP.FkdrN(>~ Gc2W O(aᬧ$Z@?}ri6,hL$.2n~w5~VnxrF6P}-sJmZX+Z^)8R>򶁺+yB܊D\&ww9K5u-BAqF}:0r}mO{>|LФ7[qeDDcL1-keZ'2}ҳ24w5-+LiNI=4)uvJW5 lFD.żٟz졬ַe*S fȩE-8x9y8,w]DO KsE$MX!t /'v~,Hub:½,&DkJn'C򱜭0l v KЋWHdڏ&!q҇+ ցoUL3ax֊C'wxAA:s&b_dEǢ)WC!m̌ c~@ T J~+! K<"z,X:Ⱥ+EƝQ1Qǃ٢*îy:g#"Q땽c[2$ޛ` ?ow1bIvp3=-δʿ^pUMH^Ƴ}֧;[艱 TMdQ`XB9}D5BF^}~j\W:4[%|N=<IC'MCUKʜ mPjI' PhGFxq_v}2JlANKPw#mPt8$3ʶgcie3lhiiW!Hd?ۏG_*a2D|=AlBՇhCv@^9{{hqDOquN OДX UQIe])jEՆV4ҟni!Qv|ogt2.YyR9QDžh-#_9o_.cu0fXHG,TgB1n s)5L7ݖ]mi8s~defCnnc@ߜϱF^6$\49޹rfDjI%[ rl^MS;1f*c08⑶Nυ@O襑s.vO51g>;2/)8͜ˢudPvLuI }jXH3L˘}.6{ ^}_'ɟujZZ;KnnG[dKHBtR%(kUVk:!j)o![TOx.ۖb\:Wh v1>3h;]0xR:͓g-xU2Qugeqx3KQ 𾁨'p~˳pU‘ӇQ~ґp8t63,[÷vHx.sg'z)f^PFZD-#:5,SnS` Cb嵊xvcڴ隔0 c0'XuzߺnF6uDC|ll? d%̂Rv 3ANjFu?ߠ&$2ҷZ?k{ zx5]:L'9,\yN"ilɾӨmpxGHQ$>z~}|J3N<]N;EǾ0Duab::Q} +G% pOmq~ΤnP iDBJ.]FDt.`3/xΝH#' ajUTP@x)k\$4 fDFkqrBoH xLL=&oUm/((&8vw˧ZC>)e};dk X澿_ݍ@״H 䀋/SS}ϗÞJP OͤX5v҅Ad3&p&m+ql+Ap@+ό+`şLO^zZ3/Vgv+WUMJd(KCՈg5ZHPNiQ 8^niCyĩ"Wcw|'~\[GΩݫT'O^j náMc'9eGQlb3UȀJNlWcކ|4ǔ?ars^hqx+ ?wALZYx7)I!AEZ. 궈|gIc]G S|s6|ɷ0;;( WESG0-Vǟ7_q-(c]A"*q,ݢ{a ;mt UGeb(-j'w(AAF,HożEf^o#%D@2r֩&pX@}*S;gCrxxU|bZ~YrW້ ROeRéUmљq\G^IKvT ۶?:LԎu ct9 ВU xU|(o%lt>~`nI\k5 VP R}wbE0#rDzY香l*Vcx$M{#ّwo.Q٢&FOiZS)hecgVCyᩥK,|þ6%bˑv)|o[a#/+.[(› nwS,yT(>cn~F@ZQ,B gKy9$RXtLFuy}~)lhPM;1@%#7_)9 ݾ7;d)xB R#(ҕNeym817zۡȜJv҉zEU]-*l +xCYmD>Ľ0}mZzl`1; D bNVԱ!S'iTGKVb,MXN||+7R_KcZG P/@6s+slQZ(:Džqu E~}x 㹝Xnma%65[\m} x&C (2kUBc-YG7H^?1fXiL*0 EIe$6xRK԰\Bk|';Дj|6@:mi%7a>u"RqDo$\O)$'E#)QЍf,rF/-. LaZsoi̢ c9fm2n}r&~{H%hdc0JbK31q{}cdM=3{ &z}[3iO^j\M<8!;s2z{);'/o?ћ; z2y/I%3q!sql\hKå0 t&.zw,eyj5|3}~X>҇ɺNh>Q!)-.c)ꏾͯ-[MGL_Ohy&5z<dJ2}e}֫-~/%ECߋG ;Lehn uJ=K|+d ^VOyW40t{=bãYfgzX1ZX1@ :)[d/$r: DeP,|+o8W[kK?9*$nK#o$ՎA AH9bA1Ƃ7-I!MK󋪥 6 "@c ?fS_?N~BX2_l'UL\k_̞Zc'48 ZSŠe ey~,lV kiI d*"EXUY(LnQlAσk{ 1j<9guAS{F'",7/QA 󐲻3VF,;EE wɷK(i#䠅uͮI2)$z럜ss{I f fXm;k0#ӟxAܞ|X8ayuPK> Xny@TY"ɉ7gENtq:-n$%R35߱z {zOv1/\納 >jf~PbK #@lCm)hg+\CI3rǏrm&~T hNoVSEst.:|4wi5A} ōmmD6;*baf],vckh.)/[Il̚AݬpuAaYsydoRYmYP avdŅ׵Z R&lxB>(~'bY8"xh!2ŭHx:ͯ :mc&;oN=$Xf!V`.Fz9/D@ӉOR-͘I% ^*Ц*®D/$)f0:aKlMF_%La[7ڌ$^ez,wcxmh%;#|^aEWW?Fe.]y4>{"{Q}mvxu_5cJ3Ϫ+{1}%BڂmU56H#!NB=k(7|w0aG?G>lx}34uֳZ+mm5rf`A *cY/Q1_JQ -JTkH5okEG>oK{Oa3B$qC(uA:];$[ƴzMt~Ń>eMugH<'F1Czf:pr]^%%7. %s^wو*JŻK@]טTzP/iABçM^L_)Ip* Ғ[E#TͭN~V}Z˘qiS,PB2@BzMVNnړΑnEur%k-}]ۯcYv(KaRע'QKO >e<[]&Uetwm̩Oo]Vnѭ}8'm[JXza&qm4tTDn6Up!Q A}n,Zr'3C2\ 6/io(Yv6<ÑaKxmuRگkgKܺĬ9ò} a,XrkM#:g\&A+ M 7yVv+Gvh?"'cGd |D+Q9XDLR^ҀSGrR{6ʹ8e֚@MQaVUۨe}NJSF~=Ģb"}>jbgAݻ&3!jGÛ`Nt&I=@u)hbDo7$sБzs 13 qv1ꪄT%R9MvOPu,x9#'%;6_qOq߸ ӌuބCOix)b=){9;DB谧o.~~+F$ %Hd7omɘx&u.o2J6' .(@Zݗ^ [^Uw$<\[Dڼ2 oOv^/$E7ȬV()رq\dz j5b) !V/=yX!zajY8MhI(`e:G*/.M^i -Nn/y9UCyt˘ELD>IJ*bfp{[/ѭkTH1# olF\Iǖ)Ek*k%xm݄I$XW*^ -Ԩ(a@f֢z3ECĠP9Hi&R cfo.po 'IN>GaC賉sY q^p.VM+?z缶Nd8=t\:gJ~Uk'㬨Z#A>iQIwf%3~ĩm%aVG}l8x W9ߝB)U^n[qX3)؊2 S|MMWӚ}5ĨKПIKʮbݚu 7lǾ}PcUWW8U3GJ}QBX(ѡlg(֟!ܚU/:0fv,@ߧR`-ܚd[On1U [L=;!f\kPw3O&tnzT!|)}Y '{/!gNm>4#÷H̩.[ K? >:13%lxcⶠx~?)egLG7OLqu*t1/]\CJuu:]"]Ёh'wS0>Z*ufeXJ.--Qw]NjؽـA8ցP^w)8ݶ&iXXS!}"ewt&jձC>FDKuy8S{?gB8p5л1F*q%8(IJkŔds ƕ#G#janpМ-:Z9Am+󭔞 ?il=^̞||4yd 8f /"H!K-,81vxY!q{~5gKjHCMMRՂhGx7:;erܒOLqB;8#)㢫!DO_:TB.A .oo=|oؙ9b"ؿ{$`$=ַвJ#R0}] U\*U젪fGIqwםU@bs(݋pw؆a)C^>Х{c>c.Z܄ l9).9#AI$  rVFBB:^QѵerK :nzV% /{ºh( "53|N70BPӟ9ЄC6Bm15 ?H.-J,R'xGEGOYcsR:۠ҳNǽȳRdWq<ĉI {tZ!So X0ۦ(XNP֩d2Hdn(P~ UCprJ꣝:| y024ɾ= 0-4?>2X75y_jCJwoꡚ%@~*o$>{R6ǘ(9_>xro̷Qy},ҡt/y{xږY}tX"bC3΋cjSu4Ŝ&)s]<}WUz-;ZSjN7CV63)%2d+.=@/%Kr!DnK0wѠ,b&oY(:/'%g[GMK-D]q}dϏ|vRT%#d=ىTH*:F7Y1|T֫ (8W;m 0y{#I;kY_OإzNWhAqtgN31 ՜y}X4$ Cu ZP]IEz2c3Gߦm[/Uwz7m^ @_԰^.W{i; 2gFTdD~Y?v3<[ոYu(=o ^{ZbpKlDg2 z:R6䲞o_"ƭ/D_[DN$\U-S5ޒVQgז9JJ[ Ic+T#V8yihA?tp]C0ў c!9ݛ;%ى%sPOatQՕt9bTzS[ /2O#?!HDAg,# Day Z٧N[[9-EGdR43:A&7Qy+.PG*HBBMi Hx~y.W Vja4 v'; +A\u7 'NQ[>Qz9AwXyyBs“&oJX"Ok)L[SZT>V@r>U?ϝJW@ +%QPBg'߭Ooxsb()6_Yk29@{j6P+`eH0PS?+.ߜ `:һw9ycmP/jto'彍:c}3l!4\(m:>)}+LtӬ$itb3|/)o1'"~Ee?ߐ{(xAz CJ,@_dà Lھ+ U.b7BURl@ZgaJ} HhM:@ӕ ç$gaEq"8.0Q-'l ɰ2I>.i{C)M )yuٷ4QG+i(5;c*L@xr .c:}a`'TuײitkW4RwA hzD~=/lUĽ0bRTe$Ld}iBD7o/E]B٪;{-ԹB,V:4 =[lxK֐Ӿ"tWYV{RZ{ Tjj4]VΆP fWiVԹMJ, "M՘~=过 TUF8ږoW!+@=Vh6 bEkt2]@8 f!cGNwoho<%O kd4})MٴTEwNS9q(fzA;ԶO-<{cڗx1XG.K 3"b- g[5k`ef^mU_|Ak" +ꑁHCur޹ܮ;vn"zK^tB4~+,|KyW03d Fi4'(Dyn4zMÆ}$GoEtVT@Fuս-!QN& *j+ i<=~]=7eE#6mP(9.#n;4e#ej> stream xڍP-qww. 4ҍ4܃&$܂[3sWW]׶}R]Ub lBimN.7 -6VŐvZ@m2g  Ptwpr8888\!B\2 k @JC]Av:y0X18YH:]AV`\A ubgdprcڊ17+W3ԍ {Fi_,ZCP~'rZ=wo ـ6ǰvwf\܁ 2qM(lP/ zYٱ. <3`<d|Au@;prAVP%F'h'~>WY~ߟLf ;zCٵddo `prrpy9GW*m ?}~Oi/ 0 Υ yV.Ѝ9x98Gg ;swt'p9zxV;y T ϻ_UZܝ׫xIY9y8x@^@kuOio 0P}ו[A//YQ\_筴z!f;}<>X>~oӟH.7+*#.Hgzb[~n/ ` y2u 99  Ai6˻ >w|7 s?m&=W#BY kU-Iɺ=Ń}%cW#DZ朻0f^kf'f S>w!6,~8n\;J`k[Ϛӌ,.۳E>W^W#7  pKkPq;B&7ŻѿX Z$98@wVxw Gd>%Kdh fthz[9v74Q蒅X^c wJT y Lҷj;wMn,,L?e]6ϭ:$uJ>793Lߌ:l* JT}s eLVnLPڋَڎ,yۘRq.Erk[H ZSvnioo_kn?RC^v,/T 9 Ril L4P!RT Q!Yh|R!ubVRGL@ы2v@ыaBA+@ޯ@<1L(ABYhr. Z(tW*Rj~=;\r}=e/(@8B-G[]*}&)$(P0Bk3yzTjnFr+Mc}߷#ŕD޸Qf!mSqVиY&3kë!4֔-j8wE/vuT,dKqԑDmv0tE^)RC .ٺ`htB>!ngQ6u/OloRb z4qV.Z Pv5IK'ҏjd-&9isyȇi~5R.HVб~dL~2p"ȬdF׬)9/{nHS$U:dD8iע.\%;zjqb2Tf2M08 m9.zk#zJZ=~ӈC"֝x-Vȃ &r6ď(%PI/TYmXS{^P&E a&q¹F1E+; Nh 3F$c_]ND^|Wr2G$;i(dѹ5zPl{dX`th^И*4HmK|@z _hb۴f?ȟPnD|_#Fհ/5X-:P Zڰ8q$Sm0P!ss!;S:R іŊHrdM6L~[[rJD؁b`|ORK%7Њ'_k`&`/n1*幆cclϴL BFjv OPŐY?p!Rx9 Ajǡ*?rɄr͊M,^VmTkQ@yئw}[\)A 4z70vc0J57U']Ξ6aQD5`=.<@l HzDӯH 3 k4"v$o!`6UG-;sPz sf[GE\57%⼾˒e_GUg/RYBaթs" WŒ߶کsSFp~saTc"Giǿk[3fKgs+{[&(CPK7ք/kZ= p{A{E f{V=3+YKJ&VXie(~~_!Y;єԏP-3\]nvoєD'Z;/mg; $4)_J `IF^21Zwj *"Ė~喓+U{tfd9-EwsU@#M'*KznYRc/.e6=1L-!ΓmH#%|~q|x~ެ doi)TNhA'Bxރ:y`!rgAn3/fѿM9`\6Ģ]0jv 2Gm**?sp}=Z)T}EN7Z;zMsdt2{/ʠ(5} CL[Hߴ1͑勲8Ѻ"UΙ4g.TSQc{XٖG7Mc U'uFdݸj+f80\*j5Rh|쥴ɸkuV@[S5-? I HTMdYKM>j!R4Wo#P婞(xK;X-}e6PXЗDXGQ?s9S y'w[)@9pӝzA'+,8IVʼ>Rȧx[knQçEYo͝!|YKmJY-l&NmWl&BءhjI%lj̯S?3[ENz:nLh6|lJ$p&1b*n7{,V.DZYSw`ignz}[ Ae@#NI`)OkHt=`#$]p8>j`QjJ9b2}I[ ^pVX'aS*_xqiXé >Q!?ˆЭE)6xXf-#22tD1 ELM0/BS&/,piaBњa=imio2\t`&eIf1 MΦζxnZ]3deɊMB!/][`0ŏKL"Ρ[I 2 Yvo yN*>86꣯qzY ){B>4ʳf=olԟgƾ=1QJұǹԯv1>Kf㝃Dty-w AQW ,$<t:>p;娷+.}4GBHիuZw7bWB\sJ܂Y}VVpZYkXݒt; 6땚x++ǥۦ#Ƶej }طe<5N2!t#3<ۜǞL}jS}H,-mX_upnL IlXpK=35(LBydw/NOP:d {?DzB_; 1uj~spH^7(?0fAKPw ;a4"]5ʧ]{n^KZ5V-mյK8 avTK͸9Qj-M V6|1S0Ywq絳l+/i6asYfG1;`" $dD{]!e9’!HWd<y~s߫{Jo{vl\JZplv[m٤n3pX[)z~!T4 jB~l*ZHaB5ν"3[{\Ν6M*vBvM46,RNZ[Î ?8L:\",Ց+8&mDp[  4) ˏT`0 @t Tiʟ&+M&|!B=w5=4S+wNl}IO9܍apqdAGp'h`1/_f`}|ʂǖ0BWΑ$ޛG2IJm cM#%':IyHV2MبN[)Q#OI*U=M M(nS͑-Oh.Xq*? 2_# _3jXCh;Fp>qkǡ*<πX^e0óy/'U'D3$_ؿlٮLXQ̄OUEL[܀y n~k߸qd9cE{U PD"SOy(q -d'(\IsJSTq;_TTF8 wyY6 9IFYMH}a(_`& xT{`b{5_ a7Ǥ4:#P98~mUy+]8l|AT]kLX(W)1 ̑|%1lO* %x՘9\3+XUH O7PU54jumM[1AIO}iYѱ|u.Dho|N t?w}iuSPt)w⾫g W&6J$q[qr2 [t< |oPI}SZb>Q#5℃͍]ï ΃U>N崟;S$E>Vj{sqRFZ9>(9`ynM]0&@J_цĐ C8r^OX;w`HYaϔ袣nBsHhXg%gE)YCGt9=SA2_6>s%3r;%e6I'W3ܬh(FcX :%X2:mx^; ~VaCP>j~T_* /7SqY;%軲NtuH/t(`3f(d Y/9ӭω <Lv%iҜ{xy0s3D"+6BF d fn%cHSls`\[lǷci0& gP{ėbRn1-")ڝ%/Ñ+ fveZZ?ّm |!*D\2k4~]=˜n]Fk&l5SQ2# D9DLNyT-V~A顯[ VVruC+dFf?:En,Śz_= ,Y:'/ H+,8uk6׸bjCߔ攝b9F^tk:j^ "s![#/E% N#_\jN:mPXepn eil[=q~zK!{f{ſ\Kr91$BՔ{"y}}!D4fce\"]&lQ~^x0Y>u@KXʅސHU$|+jJ!7RfQ>]STMDҮ CS+h;2^.іN qyW<5Bb LX- /mk-ۇ^9Y")Xt ƭpiL-o{tՀB}S,7L5JrGzDZ&Cm!`x %U6@-`7M9(1/?u;PU^?I4ߘZ1A9'U{}tćZɭFjK-z 8A")wo~0FA>Qߧp?_/ɘܝ[zy);A&^qc'ՐluNU+FӇsi.Cȟ^GUKy=2꯾LJ 5XXtq׮N uy=#aD!DzSgW ckR\OrETgԧ&) YU"i,RNqM^a2xn`_)DQ}0Ӥfsݝ~- 0,>kz5=6Ys]6TK^i 0j&\^#!;K=&lm1u8>)_{~+x".ѭy$ ''3Y(۟zT[|ѹOhMIu[ooT_f j^yn6ƚ}hgZ f*)>V:ӫPڑc?4SLKNPq endstream endobj 129 0 obj << /Length1 2015 /Length2 7132 /Length3 0 /Length 8339 /Filter /FlateDecode >> stream xڍ46(jޛ]H"=jR*mFZTj+5o?}OIrϽsri`$li X!0Hkl @ 1NcCcHhA8G]$2`I ID p;0@ axT(o4W?,--yw8@C!.sUB\FH(O ~9G,%zzz C\1HO8`0;/=+ojd<cG8#=p8Bv04W`t[!r&;W"8w0 E o8`w5t^؛#C< p-wo~(b1p_Yatu!_0(n߽3vhعwp7wm}p&  @Ru*`썂^28(=n}b 0sῈ X- Ȏ3¸Gý Я׿߬p C"\/1PG[ߔ]TQAz|!Q1@BZ,]."o#鿚? {࿹827iW# w9!p=puf@\]Woc!YPF8p fgBk/_sG  g 7\PgI 7;"~ FC@8%|iy1(@bq!;=MH%@_$z@ $ $A5/(xIyptER,gt@ 4IrB.k /@? q4bQ gq8qq^@0?R. kuQ= אkm 7Z?T@a߲Eq0Iq0.:Uqb=dp{^@G7h= ' M"Ne KY'1]RgX*w'52ufCj{ g9("{ś~4'wfsQva ƛnߖG2ܒ)>*rܾW)LNqkRRʙ*onscv>}M`r#"1Yx>ƲN\WH]k`ֶ ,_;o |4c7ޭw B|-[{FdqPqpS2T"~YrRGn-REg g&P{x {^7>Ocwohŝ@9"yOo=+%=;yRҺU^Mbwvtz-ٚ9b={6h.PYpr8L5OvIYnjh.T}OrJ hx}VIJ]7#pUH%=S,~ۈ}tp7C<5E>Iʱ-Qk;7[M8'jS5En_ԡ:Z(͸Wwm-")6Dz\ս@bGHx_֞BV)oe+e|@26[ Ou8|f4+F)c~ȗQbttIww ;(NE?~RtuNl~5 g޳)31 /v97SMzuhQ<7n-qUG;P AIS4e?۾Q%UZzSC ݏ:yv e'_sVGW=%y1ÜMk/BzM0%Ӑ*%$J87 ]Gd(|5yMڰ GxeeveK@D cO+7aKy|Q]> 9-&%(%wK]_Zk~.nffˤ[)宄)TPC:ݜ$p5~oI,R' &iß>hf˪CY=/_d~?/w%dg6>F%ޚVջhs6i|Af{?Bye, f3e^1'&z,YIY3y^Kf˽.Tk:(o%ySNsUOP{ X* Z1u,vw:eT};G^6zm染ߛVC6wwjl&#ʴx Ybat,jU Fvݭr4C#ޒAFQ7όQ UӢ%` x|&xQdK=cEy@wYb'#(L|\ȤyCΟ(9Vah;( )Nw.0^`DlooeJQ *Wݒ$]} çcZsU,R̒yY_@Րª&wNj8'Խ }}.Xʷ=y6ڽRԊV;6 !&ͨQā!8} ,l)dF9H)zBm{S'Z^?h<', ,PsmpDDY ʾKEda)u5sQsI9`T,tז(T r5su4Pfnrl,lG!}(Egy7$^&JD0<4w;TzCj/YeHǥl&Ū(!+׌RTyZg2.SR5u1ZBH/#%~1A||ѐïF*^8* KHWh 3oSjdGs,bX0oIF} Z-7~4 rdmw%#"C ->b4lϪSw^gR訳}eaQ4;-CKwPm0 Ef4X|olVыQ^:g9IwJz{uV.􎼴YB9&ӱ<"fho|'F)gQܚ^ wKl2YKvs^! D0vP;20o3nnl#߯$g=xQ$r+t=gDBEh$nxM2f0׫a|ÞsNefƈ燑7AI̢S_"O;jJ<1 k#PޛZj> LS`W()NH/;馯oJ@4"ްb-RjXu'El+'h/92IgM\bR&7v'wrQ2D*<=/h]sd}"CiA)׉41<:O5ٔRz" eRdÆ5&'J^lػF7&KZƅ؎tX^ʖDz[`h}m+Ѡ[ +U6u5 dQf?z6y ,X]g<| 3 O+Ѽt~޳Iu)sq( sZڣv-N]ZNW+;: kJU1qWة{cu/Ndw*.^}[6I^vP[BF?AΓ&hmi6wUZ]UDY E?~%ѣ::7 2CqO⻾jߊ&LYeit`w]AR+ XX0!'bEV1ΉXܖ7[lªT.c1_uQ7LHUMfAK̝_bY"^_B|.9s'4~Akb߇|HW\OCepsWbD7n*[F)nŖJhSJ+F|}©$L>~\'q} z,ߋ5'8gYVNqnG~wj' Dm ;uD:6yF ED/_>jyM@e9D's_qLvbhҼUިHیMl:훂L ,BtnˆY1KwUK#\RPy/ѵ YH `܌J엟j087>tΏ:aY)2aS*O}:mk9U ~73-4*$PMS rU{-耫{?vԤ.7~.ȹu1r%U3+ ϥ;NFt*8&ij,yc#Z'jH6ycləRO ENt_ ԖJ? \b1s 0ҋZm%&<{%;O ҧk7GelfQ"|EZN/I-mm9S__;'RtJT`|B82&/ǁյ298 H>Sg Fa]9&֒~Y?7&ץ, o})>,4˨rfBϢ+d^1i^ROK43֥m/R']6.F&i*5}D[nzTf_ɇ If,?k:Pt9%=K0+"sٕ &ΝG(~걂uq擽vTS"-E;a_K9vS6XOI4e X;vTK?KFlQJ,m!E?X,PbԜ{͐eyTښꭍM|n{\x;:=|d897c{L04A1}BwA"+|fr] 9e'+\G<4R,;iR:Ɇ6[u}XH9OtM"ܛDC-rQ/k!1vnr 29]Ee'(UxnGVƮ&}nn$lVv\m|rg8f~o,bV}UKnCVCRcB׮p=^~,~gv-Łԝ*U3wj3__]^UƢZ25ՓU'ۓt əwH9HQ:/_ܓNrR]xh:,"dLǶ'FcXZ͏ KzlIvÍYߤ:;y݊"܈f[KQ.$^-cxe"R*ztU}Xlo1е He뼐cJRXbK@\GݱJ*ӊw@ռbԇ[߉u}j7VQU3R?ϺSH,z.@[Ɓ$+_h4 ^7hkez ŧ'ξqlޏ8si5 4 QގoIKzrZ.[ҡ8Q4?sTe) Mݩ\|#In+jxਦXUyza.n~~X&. `,jy = /i_o|gJn/jSz@Fw HLUɁp6 {Kb5w9\KrtVC^?{ -@ԱA)?{&->tnHIc TJ/SlJg6ħTd^cD_tՕD)pUy/QOȁɸhݧWɸ+`BbGX=RhNAGD@´^>11M{EO,ŅՆˀefk7(Z5\`WU{ZsnY83ߣIf.t35w駀2S?k&brU[?cfU$p[2F٭& 4\κữoJ 0©++_G]W s4~Ŝ8Ώ2ux_}\n[:뺑p:i|΅*Z9xmtv%WݹR8E07˷ voU /tdlErU=>=3@BŎ<$e^]lmnhmDp KŒ)wC⟁C+?PֈTI[}YZ`}[2|yU+gt>=u{SPÉZ7Bk endstream endobj 131 0 obj << /Length1 721 /Length2 5243 /Length3 0 /Length 5832 /Filter /FlateDecode >> stream xmrPZ5P*P+ł-RBBHRJqw(NZ] ]9{vs^Da\<ym5^>?3# G!e0Q> Ёx^f neZNPB\ր...R.N\\M:0m X0UA]CMgnP[N0 @,PH(/MN\ @,Qv59: ' #Uv$I pk:?0ACUA\( H|SBZ9{@algB`A;a5h̑/Jh{V;`PM8jKsĽEa wvBQH>y?r@-e9 GZt^;BK]4=wn7<\__d,+ D?r-aH߆o"`n0 )XM|͇BoѢou٧x1 5!wZCTD#~Ej(`8}E.cwVeEo=J; uCt{MA䴝Re|q2m-D?A*MX'VKZfH^,Oce/3SYgٯbYytdWzg}.VbC}DJ/aUֲR+}ծP[yW6JQ G8[am0 ]VoIYk 6}|nz#M H} j]AtLKHd_,~Rɍ?&gf N,1 Gbtwk#kuCWKU*1Ohh.;/v~Fpl`JԤ0 rĀ8;.,W4 \vuŽ8}l0r|5U͗|>yΓ䫗Sj\]W(/hǁ@x* (bA,n ңJcoN:VWRش@$tl=m0e]󌭉Nt) o&ƴ|n!3i4m] |)xZBfF?aov1 PH?/B3<S^56яt.;.zl1 Y?6?NGYB4d&d"kXkH ]SFh6co)糈j&%[SPn2^h9>g&<

=7op܉(ʎT/( Qv l(y .Ewr9_gR.U2.#Ot7)sZC˩/A3S¯PԶL/!ư T/TƐ-r[!+ԇe6>o;VBM)ii]f2t;}"4ږզey)XΒ`l+ɷwNyů\6+7 q 5m}:鍷w\x(g͔u20F+ o} I3(XmDYbD&#CL=61`uN6?NA[mԺإD-W.]%#,n{@vݣfh:ҍI^ŝZ\E.Y;qAzCfDlK*_.T_|'YiW0<cdlTr- ;qP%Ó =dpfeK;J̩<|p7':BjUݶ^/I$G6L֤?XaH?O__rH1*1) Ny~Ў9Gr`nYʨ(~7og/q.0C1wIqW-:|cgdy X`}JE wfp``egtB@Jg'*DCԭTNGni7>y=}0tx3sL|tڝ7#ke0˽` .1`A8hAR$1o'=Z3dT)?0}rUc %sjõ>wV"-8<>jPY,V!}zj.>CbN /\c, I2&iO\-b @U:u+?>8W61YNECHC'VPrȖBgq_$cFeO14Q@TU0}0վO}~/sN5)zJ c-I6cx?ek+\Xy`<Ϥ>'q$Մ͙H֖ܡii(3?tOP!cƮBGUQ~?_DOAfkK+$YW7D6uwNr4qͽD4u2A]ijOvWm#@@{P/^ Ips74`!mdX0lөiqX[9m鋋=jVzC8:e(DO1k|1k sRh<4`4)7LhHdR/wR+uK6 Cc fk4äe֨$)R0arإs{m+A=Qrb{+V?%i5+p}ߟbF(2I /_kL,ڻ^L$L/'G`2&|ln=B=" 2?,M^qS4}߼gW8D Ty>t T{0J(@Sd̒4k}`d-?̉O3XPg{eh/ Sy-ǔT2!@SJ=1 WŬIx.ɾܩ'%77\Ej2i\->] o>haHL586uϋbo>4 SDH>%j-Y |uQ8'-HmwjlJIա\xpC^ޮfeY4<;0}p7̎en۵ɭ$˭yXN TmڠZ@Ϋ[[} 6hKfc.V5r%_u=0aAJl#UPn3r2-~X`쓦vam c¶~h>v5y GP;p, 83s ]gfk BW Hq6j3 %yv,C86kWjY2QνYȶy\ ~N7BVyK2vIt8l%)EfCB`[-F>n Icc {׎K .gx[^ܔa3$Xvffle>'D=\|nD˅\hg ì\gem1+|q?{SټA ɚTk^#k2$l,xy)=\Z'4Q"S=P2|hj!Oha&ߨU,{%9(G ?ln5Diѻ9WBc0+SX5!esmHcy!%R9){/ASHKĭ}u6sb.=y0Ǿݬ(-$TP:}/anFgQmj\|r{B%("4RK-[Yeݷ)y$sn7&%K;&cQjj5xOslR7ϒM"<Fr ihw<~gzsJ`ETCTm ,f<Ւ3$%6tU-$hLc|^[e D9Gnj瀑R&KH5ge0d궩ͰqdeݶgQ!ː|-l%(}QTqy?SZkON7B{S1$b(GBcUv6@{(onq-r"=hÛArl '{F׍>ncJO%_1?x_ ~ͯ 7~-!Ѱ>k:W|'G|? OFPU+=6 " T[AhMA%܏(ng :b )h"4؟X1q77] D`& d2MR.(*DAWrܛޡL>AD bt1v9vDqEDU`pzw9U Mՠ]kvc|˳pQkಳOҷG=nVnu"Rd0E$˧217X ub$.p6qfכWsUToD"b\Q?prxFȼ%Xo؞dIQR.̛8 Иƫ`PLlQao/7Z6שBٺuoVE|?D-A_jB2H[`-?g7BWҏD1{+kzutm}Aa endstream endobj 145 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.18)/Keywords() /CreationDate (D:20180518110037-05'00') /ModDate (D:20180518110037-05'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017) kpathsea version 6.2.3) >> endobj 133 0 obj << /Type /ObjStm /N 24 /First 187 /Length 854 /Filter /FlateDecode >> stream xڕV]o0}ϯ4BnHX; x:S[[5)ϵ1'͆R\sc7@k@n!3e@Ҁ PHPҨ 9p ư3jdPf@$1iF0K?Ԍ<fhED$pFRށ TUn֫*Ig*@$}@dv<#KЕm[ /e5oA$=@~$ _?3G>"o:c$/.q:_z`CyO_]}z r6~h<:hXd+tޑb[GO~⦄"KP$ZPM@nl`e=L_hJZ>B9UʳBƪ뉎XT'@e4->ߺ`zͭc>SGDF2ь2Oݟʟ*{L:1M$Fj 4q+誳VGzX e[ m-v=qPN;Gzq0m5ݽ aNXEX/ʽv_ЬޔqqWT(V[[;(SD"6L@5 }9~Ec&B`%'W5K*0uIS2aE8E1ti9n x}fY(ݢ*֫)jEl Qgk演UB}/\đ8U@uMZ܌R%({IiN>_p括@G|[zTS"y߮I}R!!Vs,ƥf6n&xL endstream endobj 146 0 obj << /Type /XRef /Index [0 147] /Size 147 /W [1 3 1] /Root 144 0 R /Info 145 0 R /ID [<876293ACF501EDBEAE61C4FF772D16C2> <876293ACF501EDBEAE61C4FF772D16C2>] /Length 364 /Filter /FlateDecode >> stream x%ҷRBQ] PP1asBL(s,-,N3Vvꌕ`SXw9pYGT>>`kuV`ڠT7Vf/rzUzm00c0_1*sW``Z%l"̫$aIe+jeX ؄-8m؁]Sym*W;P Y:TY:RMX:VqJY:U}} 91Map c081 Leo"K6 endstream endobj startxref 116825 %%EOF RUnit/inst/doc/RUnit.Rnw0000644000176200001440000005041113277574216014605 0ustar liggesusers% -*- mode: noweb; noweb-default-code-mode: R-mode; -*- % % $Id: RUnit.Rnw,v 1.22 2009/11/25 15:12:11 burgerm Exp $ % % %\VignetteIndexEntry{RUnit primer} %\VignetteKeywords{Unit Testing, Code Inspection, Programming} %\VignetteDepends{methods, splines} %\VignettePackage{RUnit} \documentclass[12pt, a4paper]{article} %\usepackage{amsmath,pstricks} \usepackage{hyperref} %\usepackage[authoryear,round]{natbib} %\parskip=.3cm \oddsidemargin=.1in \evensidemargin=.1in \headheight=-.3in \newcommand{\scscst}{\scriptscriptstyle} \newcommand{\scst}{\scriptstyle} \newcommand{\Rfunction}[1]{{\texttt{#1}}} \newcommand{\Robject}[1]{{\texttt{#1}}} \newcommand{\Rpackage}[1]{{\textit{#1}}} %\makeindex % \begin{document} \title{RUnit - A Unit Test Framework for R} \author{Thomas K\"onig, Klaus J\"unemann, and Matthias Burger\\Epigenomics AG} \maketitle \tableofcontents \section*{Abstract} \label{section:abstract} Software development for production systems presents a challenge to the development team as the quality of the coded package(s) has to be constantly monitored and verified. We present a generic approach to software testing for the R language modelled after successful examples such as JUnit, CppUnit, and PerlUnit. The aim of our approach is to facilitate development of reliable software packages and provide a set of tools to analyse and report the software quality status. The presented framework is completely implemented within R and does not rely on external tools or other language systems. The basic principle is that every function or method is accompanied with a test case that queries many calling situations including incorrect invocations. A test case can be executed instantly without reinstalling the whole package - a feature that is necessary for parallel development of functionality and test cases. On a second level one or more packages can be tested in a single test run, the result of which is reported in an well structured test protocol. To verify the coverage of the test framework a code inspector is provided that monitors the code coverage of executed test cases. The result of individual test invocations as well as package wide evaluations can be compiled into a summary report exported to HTML. This report details the executed tests, their failure or success, as well as the code coverage. Taking it one step further and combining the build system with a development and release procedure with defined code status description this approach opens the way for a principled software quality monitoring and risk assessment of the developed application. For our code development we have utilised the described system with great benefit w.r.t.\ code reliability and maintenance efforts in a medium sized development team. \section{Introduction} The importance of software testing can hardly be overrated. This is all the more true for interpreted languages where not even a compiler checks the basic consistency of a program. Nonetheless, testing is often perceived more as a burden than a help by the programmer. Therefore it is necessary to provide tools that make the task of testing as simple and systematic as possible. The key goal of such a testing framework should be to promote the creation and execution of test cases to become an integral part of the software development process. Experience shows that such a permanently repeated code - test - simplify cycle leads to faster and more successful software development than the usually futile attempt to add test cases once the software is largely finished. This line of thought has been pushed furthest by the Extreme Programming \cite{xp} and Test-First paradigms where test cases are viewed as the essential guidelines for the development process. These considerations lead to various requirements that a useful testing framework should satisfy: \begin {itemize} \item {Tests should be easy to execute.} \item {The results should be accessible through a well structured test protocol.} \item{It should be possible to execute only small portions of the test cases during the development process.} \item{It should be possible to estimate the amount of code that is covered by some test case.} \end {itemize} %\paragraph{Background} %\label{paragraph:Background} Testing frameworks that address these aspects have been written in a variety of languages such as Smalltalk, Java, C++ and Python. In particular, the approach described in \cite{beck} has turned out to be very successful, leading -- among others -- to the popular JUnit library for Java \cite{junit}, which has been ported to many other languages (see \cite{xp} for an extensive list of testing frameworks for all kinds of languages). Accordingly, the RUnit package (available at sourceforge \cite{runit-sf}) is our version of porting JUnit to R, supplemented by additional functionality to inspect the test coverage of some function under question. %\paragraph{Motivation} %\label{paragraph:Motivation} One may wonder why R would need yet another testing framework even though the standard method, namely executing {\it R CMD check} on ones complete package at the shell prompt, is widely accepted and applied. We think, however, that the RUnit approach is more in line with the above listed requirements and can be seen as a complement to the existing process in that: \begin{itemize} \item{test cases are called and executed from the R prompt} \item{the programmer decides which result or functionality to put under testing, e.g.\ formating issues of textual output do not need to matter} \item{test and reference data files need not be maintained separately but are combined into one file} \item{test cases need not be limited to testing/using functionality from one package checked at a time} \end{itemize} Moreover, testing frameworks based on JUnit ports seem to have become a quasi standard in many programming languages. Therefore, programmers new to R but familiar with other languages might appreciate a familiar testing environment. And finally, offering more than one alternative in the important field of code testing is certainly not a bad idea and could turn out useful. Before explaining the components of the RUnit package in detail, we would like to list some of the lessons learned in the attempt of writing useful test suites for our software (a more complete collection of tips relating to a Test-First development approach can be found in \cite{tfg}): \begin{itemize} \item {Develop test cases parallel to implementing your functionality. Keep testing all the time (code - test - simplify cycle). Do not wait until the software is complete and attempt to add test cases at the very end. This typically leads to poor quality and incomplete test cases.} \item{Distinguish between unit and integration tests: Unit tests should be as small as possible and check one unit of functionality that cannot be further decomposed. Integration tests, on the other hand, run through a whole analysis workflow and check the interplay of various software components.} \item{Good test coverage enables refactoring, by which a reorganisation of the implementation is meant. Without regular testing the attitude {\it `I better do not touch this code anymore`} once some piece of software appears to be working is frequently encountered. It is very pleasing and time-saving just to run a test suite after some improvement or simplification of the implementation to see that all test cases are still passing (or possibly reveal some newly introduced bug). This refactoring ability is a key benefit of unit testing leading not only to better software quality but also to better design.} \item{Do not test internal functions but just the public interface of a library. Since R does not provide very much language support for this distinction, the first step here is to clarify which functions are meant to be called by a user of a package and which are not (namespaces in R provide a useful directive for making this distinction, if the export list is selected carefully and maintained). If internal functions are directly tested, the ability of refactoring gets lost because this typically involves reorganisation of the internal part of a library.} \item {Once a bug has been found, add a corresponding test case.} \item{We greatly benefitted from an automated test system: A shell script, running nightly, checks out and installs all relevant packages. After that all test suites are run and the resulting test protocol is stored in a central location. This provides an excellent overview over the current status of the system and the collection of nightly test protocols documents the development progress.} \end{itemize} \section{The RUnit package} \label{section:RUnitPackage} This section contains a detailed explanation of the RUnit package and examples how to use it. As has already been mentioned the package contains two independent components: a framework for test case execution and a tool that allows to inspect the flow of execution inside a function in order to analyse which portions of code are covered by some test case. Both components are now discussed in turn. \subsection{Test case execution} \label{subsection:Testcaseexecution} The basic idea of this component is to execute a set of test functions defined through naming conventions, store whether or not the test succeeded in a central logger object and finally write a test protocol that allows to precisely identify the problems. {\bf Note, that RUnit - by default - sets the version for normal, and all other RNGs to 'Kinderman-Ramage', and 'Marsaglia-Multicarry', respectively. If you like to change these defaults please see {\tt ?defineTestSuite} for argument 'rngNormalKind' and 'rngKind'.} As an example consider a function that converts centigrade to Fahrenheit: \begin{Sinput} c2f <- function(c) return(9/5 * c + 32) \end{Sinput} A corresponding test function could look like this: \begin{Sinput} test.c2f <- function() { checkEquals(c2f(0), 32) checkEquals(c2f(10), 50) checkException(c2f("xx")) } \end{Sinput} The default naming convention for test functions in the RUnit package is {\tt test...} as is standard in JUnit. To perform the actual checks that the function to be tested works correctly a set of functions called {\tt check ...} is provided. The purpose of these {\tt check} functions is two-fold: they make sure that a possible failure is reported to the central test logger so that it will appear properly in the final test protocol and they are supposed to make explicit the actual checks in a test case as opposed to other code used to set up the test scenario. Note that {\tt checkException} fails if the passed expression does not generate an error. This kind of test is useful to make sure that a function correctly recognises error situations instead of silently creating inappropriate results. These check functions are direct equivalents to the various {\tt assert} functions of the JUnit framework. More information can be found in the online help. Before running the test function it is necessary to create a test suite which is a collection of test functions and files relating to one topic. One could, for instance, create one test suite for one R package. A test suite is just a list containing a name, an array of absolute directories containing the locations of the test files, a regular expression identifying the test files and a regular expression identifying the test functions. In our example assume that the test function is located in a file {\tt runitc2f.r} located in a directory {\tt /foo/bar/}. To create the corresponding test suite we can use a helper function: \begin{Sinput} testsuite.c2f <- defineTestSuite("c2f", dirs = file.path(.path.package(package="RUnit"), "examples"), testFileRegexp = "^runit.+\\.r", testFuncRegexp = "^test.+", rngKind = "Marsaglia-Multicarry", rngNormalKind = "Kinderman-Ramage") \end{Sinput} All that remains is to run the test suite and print the test protocol: \begin{Sinput} testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult) \end{Sinput} The resulting test protocol should be self explanatory and can also be printed as HTML version. See the online help for further information. Note that for executing just one test file there is also a shortcut in order to make test case execution as easy as possible: \begin{Sinput} runTestFile(file.path(.path.package(package="RUnit"), "examples/runitc2f.r")) \end{Sinput} The creation and execution of test suites can be summarised by the following recipe: \begin{enumerate} \item{create as many test functions in as many test files as necessary } \item{create one or more test suites using the helper function {\tt defineTestSuite}} \item{run the test suites with {\tt runTestSuite}} \item{print the test protocol either with {\tt printTextProtocol} or with {\tt printHTMLProtocol} (or with a generic method like {\tt print} or {\tt summary})} \end{enumerate} We conclude this section with some further comments on various aspects of the test execution framework: \begin{itemize} \item{A test file can contain an arbitrary number of test functions. A test directory can contain an arbitrary number of test files, a test suite can contain an arbitrary number of test directories and the test runner can run an arbitrary number of test suites -- all resulting in one test protocol. The test function and file names of a test suite must, however, obey a naming convention expressible through regular expressions. As default test functions start with {\tt test} and files with {\tt runit}.} \item{RUnit makes a distinction between failure and error. A failure occurs if one of the check functions fail (e.g.~{\tt checkTrue(FALSE)} creates a failure). An error is reported if an ordinary R error (usually created by {\tt stop}) occurs.} \item{Since version 0.4.0 there is a function {\tt DEACTIVATED} which can be used to deactivate test cases temporarily. This might be useful in the case of a major refactoring. In particular, the deactivated test cases are reported in the test protocol so that they cannot fall into oblivion.} \item{The test runner tries hard to leave a clean R session behind. Therefore all objects created during test case execution will be deleted after a test file has been processed.} \item{In order to prevent mysterious errors the random number generator is reset to a standard setting before sourcing a test file. If a particular setting is needed to generate reproducible results it is fine to configure the random number generator at the beginning of a test file. This setting applies during the execution of all test functions of that test file but is reset before the next test file is sourced.} \item{In each source file one can define the parameterless functions {\tt .setUp()} and {\tt .tearDown()}. which are then executed directly before and after each test function. This can, for instance, be used to control global settings or create addition log information.} \end{itemize} \subsection{R Code Inspection} \label{subsection:RCodeInspection} The Code Inspector is an additional tool for checking detailed test case coverage and getting profiling information. It records how often a code line will be executed. We utilise this information for improving our test cases, because we can identify code lines not executed by the current test case code. The Code Inspector is able to handle S4 methods. During the development of the Code Inspector, we noticed, that the syntax of R is very flexible. Because our coding philosophy has an emphasis of maintenance and a clear style, we developed style guides for our R coding. Therefore, one goal for the Code Inspector was to handle our coding styles in a correct manner. This leads to the consequence that not all R expression can be handled correctly. In our implementation the Code Inspector has two main functional parts. The first part is responsible for parsing and modifying the code of the test function. The second part, called the Tracker, holds the result of the code tracking. The result of the tracking process allows further analysis of the executed code. \subsubsection{Usage} The usage of the Code Inspector and the Tracker object is very simple. The following code snippet is an example: <>= library(RUnit) ## define sample functions to be tested foo <- function(x) { x <- x*x x <- 2*x return(x) } test.foo <- function() { checkTrue(is.numeric(foo(1:10))) checkEquals(length(foo(1:10)), 10) checkEqualsNumeric(foo(1), 2) } bar <- function(x, y=NULL) { if (is.null(y)) { y <- x } if (all(y > 100)) { ## subtract 100 y <- y - 100 } res <- x^y return(res) } track <- tracker(); ## initialize a tracking "object" track$init(); ## initialize the tracker a <- 1:10 d <- seq(0,1,0.1) resFoo <- inspect(foo(a), track=track); ## execute the test function and track resBar <- inspect(bar(d), track=track); ## execute the test function and track resTrack <- track$getTrackInfo(); ## get the result of Code Inspector (a list) printHTML.trackInfo(resTrack) ; ## create HTML sites @ Note, that the tracking object is an global object and must have the name {\tt track}. The {\tt inspect} function awaits a function call as argument and executes and tracks the function. The results will be stored in the tracking object. The result of the function (not of the Tracker) will be returned as usual. The tracking results will received by tr\$getResult(). With {\tt printHTML} the result of the tracking process will be presented as HTML pages. \subsubsection{Technical Details} The general idea for the code tracking is to modify the source code of the function. Therefore, we use the {\tt parse} and {\tt deparse} functions and the capability of R to generate functions on runtime. To track the function we try to include a hook in every code line. That hook calls a function of the tracked object. The information of the tracking will be stored in the closure of the tracking object (actually a function). Because the R parser allows very nested expressions, we didn't try to modify every R expression. This is a task for the future. A simple example for the modifying process is as follow:\\ original: <>= foo <- function(x) { y <- 0 for(i in 1:x) { y <- y + x } return(y) } @ modified: <>= foo.mod <- function(x) { track$bp(1) ; y <- 0 track$bp(2); for(i in 1:x) { track$bp(4) ; y <- y +x } track$bp(6); return(y) } @ Problematic code lines are: <>= if(any(a==1)) { print("do TRUE") } else print ("do FALSE"); @ This must be modified to <>= if(any(a==1)) { track$bp(2); print("do TRUE") }else{ track$bp(3); print("do FALSE"); } @ The problem is the \textit{else} branch, that cannot be modified in the current version. \section{Future Development Ideas} Here we briefly list -- in an unordered manner -- some of the avenues for future development we or someone interested in this package could take: \begin{itemize} \item{extend the {\tt checkEquals} function to handle complex S4 class objects correctly in comparisons. To this end R core has modified check.equal to handle S4 objects.} \item{reimplement the internal structures storing the test suite as well as the test result data as S4 classes.} \item{record all warnings generated during the execution of a test function.} \item{add tools to create test cases automatically. This is a research project but -- given the importance of testing -- worth the effort. See \cite{junit} for various approaches in other languages.} \item{improve the export of test suite execution data e.g.~by adding XML data export support.} \item{add some evaluation methods to the code inspector e.g.~use software metrics to estimate standard measures of code quality, complexity, and performance.} \item{overcome the problem of nested calls to registered functions for code inspection.} \item{allow automatic registration of functions \& methods.} \end{itemize} \begin{thebibliography}{99} % \bibliographystyle{plainnat} \bibitem{xp} http://www.xprogramming.com \bibitem{beck} http://www.xprogramming.com/testfram.htm \bibitem{junit} http://www.junit.org/ \bibitem{tfg} http://www.xprogramming.com/xpmag/testFirstGuidelines.htm \bibitem{runit-sf} https://sourceforge.net/projects/runit/ \end{thebibliography} \end{document} RUnit/inst/unitTests/0000755000176200001440000000000013267374743014312 5ustar liggesusersRUnit/inst/unitTests/Makefile0000644000176200001440000000051313277574246015752 0ustar liggesusersTOP = ../../.. TEST_DIR = `pwd` INST_PATH = `dirname ${TEST_DIR}` PKG_PATH = `dirname ${INST_PATH}` ## Note that 'R' was not right here: there may be no 'R' on the path. all: install test install: cd ${TOP}; \ $(R_HOME)/bin/R CMD INSTALL --clean ${PKG_PATH} && \ cd ${TEST_DIR} test: $(R_HOME)/bin/R --slave < runalltests.R RUnit/inst/unitTests/runitSetUp.r0000644000176200001440000000731713267374743016627 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for '.setUp' function\n\n") ## defined for the life time of this environment warningsLengthDefault <- getOption("warnings.length") .setUp <- function() { ## define global variable if (exists("runitDummy", where=.GlobalEnv)) { stop(".setUp found 'runitDummy'.") } assign("runitDummy", "this is a test dummy variable", envir=.GlobalEnv) if( !exists("runitDummy", where=.GlobalEnv)) { stop(".setUp failed to assign 'runitDummy'.") } ## create temp file tempFile <- file.path(tempdir(), "runitDummyFile.txt") checkTrue( !file.exists(tempFile)) write.table(matrix(1:42, 6, 7), file=tempFile) if ( !file.exists(tempFile)) { stop(paste(".setUp failed to create file", tempFile)) } ## modify options ## current default: 1000 options(warnings.length=123) if ( !identical(TRUE, all.equal.numeric(getOption("warnings.length"), 123))) { stop(paste(".setUp failed to set options.")) } ## define S4 class checkTrue( !isClass("runitDummyS4Class", where=.GlobalEnv)) setClass("runitDummyS4Class", representation(x = "numeric", y = "numeric"), prototype(x = 1:10, y = 10:1), where=.GlobalEnv) if ( !isClass("runitDummyS4Class", where=.GlobalEnv)) { stop(paste(".setUp failed to define S4 class 'runitDummyS4Class'.")) } } testRUnit..setUp.Test1 <- function() { ##@bdescr ## testcase for function .setUp of class: none ## check existance of variables, modified options, class definitions ## defined in .setUp ## remove for subsequent check ##@edescr checkTrue( exists("runitDummy", where=.GlobalEnv)) ## remove global variable rm("runitDummy", envir=.GlobalEnv) tempFile <- file.path(tempdir(), "runitDummyFile.txt") checkTrue( file.exists(tempFile)) ## remove temp file unlink(tempFile) checkEqualsNumeric( getOption("warnings.length"), 123) ## reset options options(warnings.length=warningsLengthDefault) checkTrue( isClass("runitDummyS4Class", where=.GlobalEnv)) ## remove class checkTrue( removeClass("runitDummyS4Class", where=.GlobalEnv)) } testRUnit..setUp.Test2 <- function() { ##@bdescr ## testcase for function .setUp of class: none ## same as above, only reason is to check correct invocation of .setUp ## before *each* test case function ##@edescr checkTrue( exists("runitDummy", where=.GlobalEnv)) ## remove global variable rm("runitDummy", envir=.GlobalEnv) tempFile <- file.path(tempdir(), "runitDummyFile.txt") checkTrue( file.exists(tempFile)) ## remove temp file unlink(tempFile) checkEqualsNumeric( getOption("warnings.length"), 123) ## reset options options(warnings.length=warningsLengthDefault) checkTrue( isClass("runitDummyS4Class", where=.GlobalEnv)) ## remove class checkTrue( removeClass("runitDummyS4Class", where=.GlobalEnv)) } RUnit/inst/unitTests/runitHTMLProtocol.r0000644000176200001440000000463713267374743020057 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for 'HTMLProtocol' function\n\n") testRUnit.printHTMLProtocol <- function() { ## FIXME ## this is not safe, think about assigning .logger to new environment ## copy baseenv() .logger tmp <- get(".testLogger", envir = RUnitEnv) testCaseDir <- file.path(system.file(package="RUnit"), "examples") testSuiteInternal <- defineTestSuite("RUnit Self Test", testCaseDir, "correctTestCase.r") testData2 <- runTestSuite(testSuiteInternal, useOwnErrorHandler=FALSE) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") testProtocolFile <- file.path(tempdir(), paste(timeStamp, "test_printHTMLProtocol.html", sep="_")) ret <- printHTMLProtocol(testData2, fileName=testProtocolFile) assign(".testLogger", tmp, envir = RUnitEnv) checkTrue( file.exists(testProtocolFile)) ## input argument error handling ## missing 'testData' object checkException(printHTMLProtocol()) ## wrong class checkException(printHTMLProtocol("dummy")) ## fileName arg errors testData <- list() class(testData) <- "RUnitTestData" ## wrong type checkException(printHTMLProtocol(testData, fileName=numeric(1))) ## wrong length checkException(printHTMLProtocol(testData, fileName=character(0))) checkException(printHTMLProtocol(testData, fileName=character(2))) ## separateFailureList arg errors ## wrong type checkException(printHTMLProtocol(testData, separateFailureList=numeric(0))) ## wrong length checkException(printHTMLProtocol(testData, separateFailureList=logical(0))) checkException(printHTMLProtocol(testData, separateFailureList=logical(2))) } RUnit/inst/unitTests/runitTextProtocol.r0000644000176200001440000000513013267374743020224 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for 'textProtocol' function\n\n") testRUnit.printTextProtocol <- function() { ## copy baseenv() logger tmp <- get(".testLogger", envir = RUnitEnv) testCaseDir <- file.path(system.file(package="RUnit"), "examples") testSuiteInternal <- defineTestSuite("RUnit Self Test", testCaseDir, "correctTestCase.r") testData2 <- runTestSuite(testSuiteInternal, useOwnErrorHandler=FALSE) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") testProtocolFile <- file.path(tempdir(), paste(timeStamp, "test_printHTMLProtocol.txt", sep="_")) ret <- printTextProtocol(testData2, fileName=testProtocolFile) assign(".testLogger", tmp, envir = RUnitEnv) checkTrue( file.exists(testProtocolFile)) ## input argument error handling ## missing 'testData' object checkException(printTextProtocol()) ## wrong class checkException(printTextProtocol("dummy")) ## fileName arg errors testData <- list() class(testData) <- "RUnitTestData" ## wrong type checkException(printTextProtocol(testData, fileName=numeric(1))) ## wrong length checkException(printTextProtocol(testData, fileName=character(0))) checkException(printTextProtocol(testData, fileName=character(2))) ## separateFailureList arg errors ## wrong type checkException(printTextProtocol(testData, separateFailureList=numeric(0))) ## wrong length checkException(printTextProtocol(testData, separateFailureList=logical(0))) checkException(printTextProtocol(testData, separateFailureList=logical(2))) ## showDetails arg errors ## wrong type checkException(printTextProtocol(testData, showDetails=numeric(0))) ## wrong length checkException(printTextProtocol(testData, showDetails=logical(0))) checkException(printTextProtocol(testData, showDetails=logical(2))) } RUnit/inst/unitTests/runitJUnitProtocol.r0000644000176200001440000000401713267374743020334 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA cat("\n\nRUnit test cases for 'printJUnitProtocol' function\n\n") testRUnit.printJUnitProtocol <- function() { ## copy baseenv() logger tmp <- get(".testLogger", envir = RUnitEnv) testCaseDir <- file.path(system.file(package="RUnit"), "examples") testSuiteInternal <- defineTestSuite("RUnit Self Test", testCaseDir, "correctTestCase.r") testData2 <- runTestSuite(testSuiteInternal, useOwnErrorHandler=FALSE) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") testProtocolFile <- file.path(tempdir(), paste(timeStamp, "test_printJUnitProtocol.xml", sep="_")) ret <- printJUnitProtocol(testData2, fileName=testProtocolFile) assign(".testLogger", tmp, envir = RUnitEnv) checkTrue( file.exists(testProtocolFile)) ## input argument error handling ## missing 'testData' object checkException(printJUnitProtocol()) ## wrong class checkException(printJUnitProtocol("dummy")) ## fileName arg errors testData <- list() class(testData) <- "RUnitTestData" ## wrong type checkException(printJUnitProtocol(testData, fileName=numeric(1))) ## wrong length checkException(printJUnitProtocol(testData, fileName=character(0))) checkException(printJUnitProtocol(testData, fileName=character(2))) } RUnit/inst/unitTests/runitRUnit.r0000644000176200001440000005232713277572123016622 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for 'RUnit:check' functions\n\n") testRUnit.checkEquals <- function() { ##@bdescr ## test case for function checkEquals of class: none ##@edescr ## integer x <- 1:10 checkEquals(x, x) ## return value checkTrue( checkEquals(x, x)) namedInt <- 1:10 names(namedInt) <- letters[namedInt] checkEquals(namedInt, namedInt) checkEquals(namedInt, x, checkNames=FALSE) ## numeric checkEquals(pi, pi) y <- 1/0 checkEquals(Inf, y) checkEquals(y, Inf) y <- log(-1) checkEquals(NaN, y) checkEquals(rep(NaN, 23), rep(y, 23)) checkEquals(9, 9.0) checkEquals(NA, NA) checkEquals(rep(NA, 14), rep(NA, 14)) checkEquals( numeric(1), numeric(1)) checkEquals( 0.01, 0.02, tolerance=0.01) tmp <- c(0.01, NA, 0.02, Inf, -Inf, NaN, 1.0) checkEquals( tmp, tmp, tolerance=0.01) ## complex checkEquals(complex(0), complex(0)) checkEquals(complex(2), complex(2)) checkEquals(complex(2, imaginary=1), complex(2, imaginary=1)) ## character checkEquals( character(1), character(1)) checkEquals( letters, letters) ## matrix checkEquals( matrix(1, 3,5), matrix(1, 3,5)) checkEquals( matrix(1, 50000,5), matrix(1, 50000,5), "large matrix not identified as equal") ## language checkEquals( expression(2), expression(2)) checkEquals( call("mean", "median"), call("mean", "median")) ## formula simpleForm <- x ~ 1 checkEquals( simpleForm, simpleForm, "simple formula not identified as equal") compForm <- y ~ x + y + x*y + offset(x) checkEquals( compForm, compForm, "formula not identified as equal") ## factor alphaFac <- factor(letters) checkEquals( alphaFac, alphaFac, "factor not identified as equal") ## list checkEquals( list(100), list(100)) checkEquals( list(100), list(100), tolerance=1) alphaList <- seq_along(letters) names(alphaList) <- letters checkEquals( alphaList, alphaList) checkEquals( alphaList, alphaList, checkNames=FALSE) ## nested list with NA, NaN, Inf nl <- list(a=list(1), b=list(1:4), c=list(ab=1, bc=list(list(2), list(NA), list(NaN)) ), d=list(m1=matrix(NA, 2,3), m2=matrix(1+1i, 4,5)), e=list(e1=NaN, e2=list(Inf), e3=list(a=Inf, b=-Inf, c=NaN, d=-0/0))) checkEquals(nl, nl) ## example from ?glm counts <- c(18,17,15,20,10,20,25,13,12) outcome <- gl(3,1,9) treatment <- gl(3,3) lmFit <- glm(counts ~ outcome + treatment, family=poisson()) checkEquals( lmFit, lmFit, checkNames=FALSE) checkEquals( lmFit, lmFit) lmFitUnnamed <- lmFit names(lmFitUnnamed) <- NULL checkEquals( lmFit, lmFitUnnamed, checkNames=FALSE) ## POSIXct sysTime <- as.POSIXct(Sys.time()) checkEquals( sysTime, sysTime) ## raw checkEquals( raw(14), raw(14)) namedRaw <- as.raw(1:14) names(namedRaw) <- letters[1:14] checkEquals( namedRaw, namedRaw) ## formula a <- 1:10 f <- gl(2,5) checkEquals( a~f, a~f) ## S4 objects if (identical(TRUE, require(methods))) { setClass("track1", representation(x="numeric", y="numeric"), where=.GlobalEnv) on.exit(removeClass("track1", where=.GlobalEnv)) s4Obj <- try(new("track1")) s4Obj@x <- 1:10 s4Obj@y <- 10:1 checkEquals( s4Obj, s4Obj) ## S4 class containing S4 class slot setClass("trackPair", representation(trackx = "track1", tracky = "track1"), where=.GlobalEnv) on.exit(removeClass("trackPair", where=.GlobalEnv), add=TRUE) tPair <- new("trackPair") tPair@trackx <- s4Obj tPair@tracky <- s4Obj checkEquals( tPair, tPair) } ## detect differences checkException( checkEquals(1 , 1, tolerance=FALSE)) checkException( checkEquals(1 , 1, tolerance=numeric(0))) checkException( checkEquals(1 , 1, tolerance=numeric(2))) ## integer namedInt <- 1:9 names(namedInt) <- letters[namedInt] checkException( checkEquals( namedInt, 1:9)) ## numeric checkException( checkEquals( 8, 9)) checkException( checkEquals( 0.01, 0.02, tolerance=0.009)) checkException(checkEquals(NaN, NA)) checkException(checkEquals(NaN, Inf)) checkException(checkEquals(NaN, -Inf)) checkException(checkEquals(NA, Inf)) checkException(checkEquals(NA, -Inf)) checkException(checkEquals(numeric(2), numeric(3))) checkException(checkEquals(numeric(3), numeric(2))) ## complex checkException( checkEquals(complex(0), complex(1))) checkException( checkEquals(complex(2), complex(1))) checkException( checkEquals(complex(2, imaginary=1), complex(2, imaginary=0))) checkException( checkEquals(complex(2, real=1, imaginary=1), complex(2, real=1, imaginary=0))) checkException( checkEquals(complex(2, real=1, imaginary=1), complex(2, real=0, imaginary=1))) checkException( checkEquals(complex(2, real=1, imaginary=1), complex(2, real=0, imaginary=0))) ## character named <- character(1) names(named) <- "name" checkException( checkEquals( character(1), named)) checkException( checkEquals( letters, letters[-1])) ## formula checkException( checkEquals( lmFit, lmFitUnnamed)) lmFitInter <- glm(counts ~ outcome * treatment, family=poisson()) checkException( checkEquals( lmFitInter, lmFit)) ## factor alphaFacRecoded <- factor(alphaFac, labels=as.character(seq_along(levels(alphaFac)))) checkException( checkEquals(alphaFacRecoded, alphaFac)) ## list checkException( checkEquals( list(1), list("1"=1))) checkException( checkEquals( list(), list("1"=1))) checkException( checkEquals( list(list(), list(list()), list(list(list()))), list(list(), list(list()), list(list(list(), list()))))) ## POSIXct checkException( checkEquals(as.POSIXct(Sys.time()), as.POSIXct("2007-04-04 16:00:00"))) checkException( checkEquals(as.POSIXlt(Sys.time()), as.POSIXlt("2007-04-04 16:00:00"))) ## nested type sysTime <- as.POSIXct(Sys.time()) checkException( checkEquals( list(a=2, list(time=sysTime)), list(a=2, time=list(sysTime)))) ## raw checkException( checkEquals(raw(1), raw(2))) checkException( checkEquals(raw(1E5), raw(100001))) raw3 <- raw(3) raw3mod <- raw3 raw3mod[1] <- as.raw(3) checkException( checkEquals(raw3, raw3mod)) checkException( checkEquals(as.raw(1:1000), as.raw(c(1:99,-1,101:1000)) ) ) ## S4 objects if (identical(TRUE, require(methods))) { ## class defined above s4Obj <- new("track1") s4Obj@x <- 1:10 checkException( checkEquals( s4Obj, new("track1"))) tPair <- new("trackPair") tPair@trackx <- s4Obj checkException( checkEquals( tPair, new("trackPair"))) } } testRUnit.checkEqualsNumeric <- function() { ##@bdescr ## test case for function checkEqualsNumeric of class: none ##@edescr checkTrue( checkEqualsNumeric( 9,9)) checkTrue( checkEqualsNumeric( 9.1,9.2, tolerance=0.1)) x <- 1:10 attributes(x) <- list(dummy="nonsense") checkTrue( checkEqualsNumeric( x, x)) checkTrue( checkEqualsNumeric( 1:10, x, check.attributes=FALSE)) rvec <- rnorm(132) checkTrue( checkEqualsNumeric( matrix(rvec, 12, 11), matrix(rvec, 12, 11))) checkTrue( checkEqualsNumeric( rvec, rvec)) ## special constants checkEqualsNumeric( pi, pi) checkEqualsNumeric( NA, NA) checkEqualsNumeric( c(1, NA, 3), c(1, NA, 3)) checkEqualsNumeric( NaN, NaN) checkEqualsNumeric( c(1, NaN, 3), c(1, NaN, 3)) checkEqualsNumeric( Inf, Inf) checkEqualsNumeric( c(1, Inf, 3), c(1, Inf, 3)) checkEqualsNumeric( -Inf, -Inf) checkEqualsNumeric( c(1, -Inf, 3), c(1, -Inf, 3)) ## numeric difference checkException( checkEqualsNumeric( 9, 10)) checkException( checkEqualsNumeric( list(9), list(10))) checkException( checkEqualsNumeric( matrix(9), matrix(10))) rvec2 <- rnorm(132) checkException( checkEqualsNumeric( matrix(rvec, 12, 11), matrix(rvec2, 12, 11))) ## exception handling ## type not supported checkException( checkEqualsNumeric( list(rvec), list(rvec))) ## S4 objects if (identical(TRUE, require(methods))) { ## class defined above s4Obj <- new("track1") s4Obj@x <- 1:10 checkException( checkEqualsNumeric( s4Obj, s4Obj)) tPair <- new("trackPair") tPair@trackx <- s4Obj checkException( checkEqualsNumeric( tPair, tPair)) } } testRUnit.checkIdentical <- function() { ##@bdescr ## test case for function checkIdentical of class: none ##@edescr checkIdentical( TRUE, TRUE) ## return value checkTrue( checkIdentical( TRUE, TRUE)) checkIdentical( FALSE, FALSE) ## bit representation identical checkIdentical( NA, NA) checkIdentical( c(1, NA, 3), c(1, NA, 3)) checkIdentical( NaN, NaN) checkIdentical( c(1, NaN, 3), c(1, NaN, 3)) checkIdentical( Inf, Inf) checkIdentical( c(1, Inf, 3), c(1, Inf, 3)) checkIdentical( -Inf, -Inf) checkIdentical( c(1, -Inf, 3), c(1, -Inf, 3)) checkIdentical( as.integer(2), as.integer(2)) checkIdentical( as.character(2), as.character(2)) checkIdentical( as.complex(2), as.complex(2)) checkIdentical( as.numeric(2), as.numeric(2)) checkIdentical( as.expression("2+4"), as.expression("2+4")) checkIdentical( as.expression(2+4), as.expression(2+4)) checkIdentical( as.factor(letters), factor(letters)) ## nested list with NA, NaN, Inf nl <- list(a=list(1), b=list(1:4), c=list(ab=1, bc=list(list(2), list(NA), list(NaN)) ), d=list(m1=matrix(NA, 2,3), m2=matrix(1+1i, 4,5)), e=list(e1=NaN, e2=list(Inf), e3=list(a=Inf, b=-Inf, c=NaN, d=-0/0))) checkIdentical(nl, nl) ## POSIX sysTime <- as.POSIXlt(Sys.time()) checkIdentical( sysTime, sysTime) ## raw checkIdentical( raw(14), raw(14)) namedRaw <- as.raw(1:14) names(namedRaw) <- letters[1:14] checkIdentical( namedRaw, namedRaw) ## formula a <- 1:10 f <- gl(2,5) checkIdentical( a~f, a~f) ## call cl <- call("round", 10.5) checkIdentical( cl, cl) ## S3 objects (ie. lists with attributes) ## from ?lm Example ctl <- c(4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14) trt <- c(4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69) group <- gl(2,10,20, labels=c("Ctl","Trt")) weight <- c(ctl, trt) lm.D9 <- lm(weight ~ group) checkIdentical( lm.D9, lm(weight ~ group)) ## S4 objects if (identical(TRUE, require(methods))) { setClass("track1", representation(x="numeric", y="numeric"), where=.GlobalEnv) on.exit(removeClass("track1", where=.GlobalEnv)) s4Obj <- try(new("track1")) checkIdentical( s4Obj, new("track1")) rm(s4Obj) } ## exception handling ## type mismatches checkException( checkIdentical( as.integer(2), as.numeric(2))) checkException( checkIdentical( as.integer(2), as.character(2))) checkException( checkIdentical( as.integer(2), as.list(2))) checkException( checkIdentical( as.integer(2), as.complex(2))) checkException( checkIdentical( as.integer(2), as.expression(2))) ## value mismatches checkException( checkIdentical( as.integer(2), as.integer(3))) checkException( checkIdentical( as.character(2), as.character(3))) checkException( checkIdentical( as.complex(2), as.complex(3))) checkException( checkIdentical( as.numeric(2), as.numeric(3))) checkException( checkIdentical( as.expression("2+4"), as.expression("2+3"))) checkException( checkIdentical( as.factor(letters), factor(letters[-1]))) fac <- factor(letters) levels(fac) <- c("1", letters[-1]) checkException( checkIdentical( fac, as.factor(letters))) ## nested list with NA, NaN, Inf checkException( checkIdentical( )) ## POSIX sysTime <- as.POSIXlt(Sys.time()) checkException( checkIdentical( sysTime, as.POSIXlt(Sys.time(), tz="GMT"))) ## raw checkException(checkIdentical( raw(14), raw(13))) namedRaw <- as.raw(1:14) names(namedRaw) <- letters[1:14] checkException(checkIdentical( namedRaw, as.raw(1:14))) ## S3 objects (ie. lists with attributes) ## from ?lm Example lm.D9base <- lm(weight ~ group - 1) checkException( checkIdentical( lm.D9base, lm.D9)) ## S4 objects if (identical(TRUE, require(methods))) { setClass("track2", representation(x="numeric", y="numeric"), prototype(x=as.numeric(1:23), y=as.numeric(23:1)), where=.GlobalEnv) on.exit(removeClass("track2", where=.GlobalEnv), add=TRUE) s4Obj <- try(new("track2")) s4ObjDiff <- s4Obj s4ObjDiff@y <- s4ObjDiff@x checkException( checkIdentical( s4Obj, s4ObjDiff)) } } testRUnit.checkTrue <- function() { ##@bdescr ## test case for function checkTrue of class: none ##@edescr checkEquals( checkTrue( TRUE), TRUE) ## named arguments namedArg <- TRUE names(namedArg) <- "Yes" checkEquals( checkTrue( namedArg), TRUE) ## errorr handling namedArg <- FALSE names(namedArg) <- "No" checkException( checkTrue( namedArg)) checkException( checkTrue( FALSE)) ## incorrect length checkException( checkTrue( c(TRUE, TRUE))) checkException( checkTrue( c(FALSE, TRUE))) checkException( checkTrue( logical(0))) checkException( checkTrue( logical(2))) } testRUnit.checkException <- function() { ##@bdescr ## test case for function checkException of class: none ##@edescr checkException( checkTrue( FALSE)) checkException( checkTrue( )) checkException( checkEquals( )) checkException( checkEquals( 24)) checkException( checkEquals( 24, 24, tolerance="dummy")) checkException( checkEqualsNumeric( )) checkException( checkEqualsNumeric( 24)) checkException( checkEqualsNumeric( 24, 24, tolerance="dummy")) checkException( stop("with message"), silent=FALSE) checkException( stop("wo message"), silent=TRUE) ## R 2.5.0 devel example that failed ## minimal example provided by Seth Falcon ll = list() ll[[1]] = function(x) stop("died") checkException( do.call(ll[[1]], list(1))) ## S4 objects if (identical(TRUE, require(methods))) { setClass("track2", representation(x="numeric", y="numeric"), prototype(x=as.numeric(1:23), y=as.numeric(23:1)), where=.GlobalEnv) on.exit(removeClass("track2", where=.GlobalEnv)) s4Obj <- try(new("track2")) checkException( slot(s4Obj, "z")) checkException( slot(s4Obj, "z") <- 1:10) ## missing method argument ## coerce(from, to) checkException( coerce(s4Obj)) } } testRUnit.DEACTIVATED <- function() { ##@bdescr ## test case for function DEACTIVATED of class: none ##@edescr checkException( DEACTIVATED()) checkException( DEACTIVATED("some message")) ## compound text checkException( DEACTIVATED(c("some message", "some more", "and more"))) } testRUnit.defineTestSuite <- function() { ##@bdescr ## test case for function defineTestSuite of class: none ##@edescr ## correct working testSuite <- defineTestSuite("RUnit Example", system.file("examples", package="RUnit"), testFileRegexp="correctTestCase.r") ## this also works for S3 objects checkTrue( inherits(testSuite, "RUnitTestSuite")) checkTrue( is.list(testSuite)) checkTrue( all(c("name", "dirs", "testFileRegexp", "testFuncRegexp", "rngKind", "rngNormalKind") %in% names(testSuite))) checkTrue( isValidTestSuite(testSuite)) ## error handling ## missing 'dirs' argument checkException(defineTestSuite("RUnit Example", testFileRegexp="correctTestCase.r")) } testRUnit.isValidTestSuite <- function() { ##@bdescr ## test case for function isValidTestSuite of class: none ##@edescr ## correct working testSuite <- defineTestSuite("RUnit Example", system.file("examples", package="RUnit"), testFileRegexp="correctTestCase.r") checkTrue( isValidTestSuite(testSuite)) ## error handling ## has to be S3 class 'RUnitTestSuite' testSuiteFail <- testSuite class(testSuiteFail) <- "NotUnitTestSuite" checkTrue( !isValidTestSuite(testSuiteFail)) ## expecting list elements testSuiteFail <- testSuite testSuiteFail[["dirs"]] <- NULL checkTrue( !isValidTestSuite(testSuiteFail)) ## has to be character testSuiteFail <- testSuite testSuiteFail[["name"]] <- list() checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["dirs"]] <- list() checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["testFileRegexp"]] <- list() checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["testFuncRegexp"]] <- list() checkTrue( !isValidTestSuite(testSuiteFail)) ## length 1 required testSuiteFail <- testSuite testSuiteFail[["name"]] <- character(0) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["name"]] <- character(2) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["testFileRegexp"]] <- character(0) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["testFileRegexp"]] <- character(2) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["testFuncRegexp"]] <- character(0) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["testFuncRegexp"]] <- character(2) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["rngKind"]] <- character(0) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["rngKind"]] <- character(2) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["rngNormalKind"]] <- character(0) checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["rngNormalKind"]] <- character(2) checkTrue( !isValidTestSuite(testSuiteFail)) ## director has to exist testSuiteFail <- testSuite testSuiteFail[["dirs"]] <- "doesNotExist" checkTrue( !isValidTestSuite(testSuiteFail)) testSuiteFail <- testSuite testSuiteFail[["dirs"]] <- c(tempdir(), "doesNotExist", tempdir()) checkTrue( !isValidTestSuite(testSuiteFail)) ## same, '' has to return FALSE testSuiteFail <- testSuite testSuiteFail[["dirs"]] <- c(tempdir(), "", tempdir()) checkTrue( !isValidTestSuite(testSuiteFail)) } testRUnit.runTestFile <- function() { ##@bdescr ## test case for function runTestFile of class: none ##@edescr testFile <- file.path(system.file("examples", package="RUnit"), "correctTestCase.r") checkTrue( file.exists(testFile)) ## The issue: .testLogger is the hard coded logger object ## regenerated by each new run call in the global environment ## thereby overwriting the existing logger. ## With the current implementation there seems to be no way to ## test the test suite execution *within* a test suite run # tmpFile <- tempfile() # writeLines(text=" testFile <- file.path(system.file(\"examples\", package=\"RUnit\"), \"correctTestCase.r\");\n res <- runTestFile(testFile, useOwnErrorHandler=FALSE);\n", con=tmpFile) # # execEnv <- new.env(parent=.GlobalEnv) # sys.source(tmpFile, execEnv) # unlink(tmpFile) # checkTrue(exists("res", envir=execEnv)) # checkTrue(inherits(get("res", envir=execEnv), "RUnitTestData")) # rm(execEnv) ## error handling ## all argument checks delegated to runTestSuite so no need for comprehensive check here ## check if any argument check is reached/performed ## useOwnErrorHandler ## type logical checkException( runTestFile(testFile, useOwnErrorHandler=integer(1), gcBeforeTest=TRUE)) } testRUnit.runTestSuite <- function() { ##@bdescr ## test case for function runTestSuite of class: none ##@edescr testSuiteTest <- defineTestSuite("RUnit Example", system.file("examples", package="RUnit"), testFileRegexp="correctTestCase.r") checkTrue( isValidTestSuite(testSuiteTest)) ## The issue: same as above ##res <- runTestSuite(testSuiteTest) ## ## error handling ## ## useOwnErrorHandler ## type logical tS <- testSuiteTest checkException( runTestSuite(tS, useOwnErrorHandler=integer(1))) ## length 1 checkException( runTestSuite(tS, useOwnErrorHandler=logical(0))) checkException( runTestSuite(tS, useOwnErrorHandler=logical(2))) checkException( runTestSuite(tS, useOwnErrorHandler=as.logical(NA))) ## gcBeforeTest checkException( runTestSuite(tS, gcBeforeTest = "hello")) checkException( runTestSuite(tS, gcBeforeTest = c(TRUE, FALSE))) checkException( runTestSuite(tS, gcBeforeTest = as.logical(NA))) ## testSuite } RUnit/inst/unitTests/runitS4.r0000644000176200001440000000566113267374743016055 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for S4 class inheritance\n\n") testRUnit.S4classInheritance <- function() { ##@bdescr ## test case for noneof class: none ## test if S4 classes can be instantiated inside test code ## given that no where argument is specified ##@edescr className <- setClass("testVirtualClass", representation("VIRTUAL", x = "numeric", y = "numeric", xlab = "character", ylab = "character") ) checkEquals(className, "testVirtualClass") checkException( new(className)) derivedClassName <- setClass("testDerivedClass", representation(className, scale = "numeric", title = "character") ) ## Attention: ## invert inheritance order! on.exit(removeClass(derivedClassName)) on.exit(removeClass(className), add=TRUE) checkEquals(derivedClassName, "testDerivedClass") objD <- new(derivedClassName) checkTrue( is(objD, derivedClassName)) checkTrue( isS4(objD)) checkTrue(require(stats4)) ## instantiate S4 class from stats ## be sure to use a unique unused variable name here ## i.e. NOT className as this has been used before ## and the on.exit call will look up the name just *before* ## the test function exists classNameMLE <- "mle" obj <- new(classNameMLE) checkTrue( is(obj, classNameMLE)) checkTrue( isS4(obj)) derivedStats4ClassName <- setClass("mleChild", representation(classNameMLE, scale = "numeric", title = "character") ) on.exit(removeClass(derivedStats4ClassName), add=TRUE) checkEquals(derivedStats4ClassName, "mleChild") obj <- new("mleChild") checkTrue( is(obj, "mleChild")) checkTrue( isS4(obj)) } RUnit/inst/unitTests/runitPlotConnection.r0000644000176200001440000000540213267374743020516 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for 'RUnit:plotConnection' function\n\n") testRUnit.plotConnection <- function() { ##@bdescr ## test case for function plotConnection of class: none ##@edescr ## requires X server to be available for png device if (!interactive()) { DEACTIVATED("plotConnection uses png device which requires X sverer to be available.") } ## 1) no counts conMat <- matrix(0, nrow=5, ncol=5) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") tmpPlotFile <- file.path(tempdir(), paste(timeStamp, "connectionPlot.png", sep="_")) ret <- RUnit:::plotConnection.trackInfo(conMat, tmpPlotFile) checkTrue( is(ret, "NULL")) checkTrue( file.exists(tmpPlotFile)) ## clean up try(unlink(tmpPlotFile)) ## 2) num <- 5 conMat <- matrix(sample(1:3, num^2, replace=TRUE), nrow=num, ncol=num) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") tmpPlotFile <- file.path(tempdir(), paste(timeStamp, "connectionPlot2.png", sep="_")) ret <- RUnit:::plotConnection.trackInfo(conMat, tmpPlotFile) checkTrue( is(ret, "NULL")) checkTrue( file.exists(tmpPlotFile)) ## clean up try(unlink(tmpPlotFile)) num <- 25 colNum <- 3 conMat <- matrix(sample(1:3, num*colNum, replace=TRUE), nrow=num, ncol=colNum) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") tmpPlotFile <- file.path(tempdir(), paste(timeStamp, "connectionPlot3.png", sep="_")) ret <- RUnit:::plotConnection.trackInfo(conMat, tmpPlotFile) checkTrue( is(ret, "NULL")) checkTrue( file.exists(tmpPlotFile)) ## clean up try(unlink(tmpPlotFile)) num <- 25 colNum <- 3 conMat <- matrix(sample(0:3, num*colNum, replace=TRUE), nrow=num, ncol=colNum) timeStamp <- format(Sys.time(), "%y%m%d-%H%M") tmpPlotFile <- file.path(tempdir(), paste(timeStamp, "connectionPlot4.png", sep="_")) ret <- RUnit:::plotConnection.trackInfo(conMat, tmpPlotFile) checkTrue( is(ret, "NULL")) checkTrue( file.exists(tmpPlotFile)) ## clean up try(unlink(tmpPlotFile)) } RUnit/inst/unitTests/runitTestLogger.r0000644000176200001440000000553513267374743017646 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for '.testLogger' object\n\n") testRUnit..testLogger <- function() { ##@bdescr ## test case for .testLogger object of class: 'TestLogger' ##@edescr ## create new temp test logger object tlTmp <- RUnit:::.newTestLogger(TRUE) checkTrue( is(tlTmp, "TestLogger")) checkTrue( all( c("getTestData", "setCurrentTestSuite" , "setCurrentSourceFile", "addSuccess", "addError", "addFailure", "addDeactivated", "addCheckNum", "isFailure", "setFailure", "isDeactivated", "setDeactivated", "incrementCheckNum", "getCheckNum", "cleanup") %in% names(tlTmp))) testSuite <- defineTestSuite(name="Test Test", dirs=tempdir()) ## need to init 1) test suite name and 2) test case file name tlTmp$setCurrentTestSuite(testSuite) tlTmp$setCurrentSourceFile("Test File") tlTmp$addSuccess("first test case", system.time(1)) checkEquals(tlTmp$getTestData()[[1]]$nTestFunc, 1) tlTmp$incrementCheckNum() checkEquals(tlTmp$getCheckNum(), 1) checkTrue( !tlTmp$isFailure()) tlTmp$setFailure() checkTrue( tlTmp$isFailure()) checkTrue( !tlTmp$isDeactivated()) tlTmp$setDeactivated("This is a deactivated test case") checkTrue( tlTmp$isDeactivated()) tlTmp$cleanup() checkEquals(tlTmp$getCheckNum(), 0) checkTrue( !tlTmp$isFailure()) } testRUnit.getErrors <- function() { ##@bdescr ## test case for getErrors function ##@edescr ## create dummy test suite result object testData <- vector(mode="list", 3) for (i in seq_along(testData)) { testData[[i]]$nErr <- i testData[[i]]$nDeactivated <- i - 1 testData[[i]]$nTestFunc <- i*13 testData[[i]]$nFail <- i + 3 } class(testData) <- "RUnitTestData" res <- getErrors(testData) checkTrue (is.list(res)) checkEquals(length(res), 4) checkEquals(res$nErr, 6) checkEquals(res$nDeactivated, 3) checkEquals(res$nTestFunc, 78) checkEquals(res$nFail, 15) ## check exception handling checkException( getErrors( list())) } RUnit/inst/unitTests/runitInspect.r0000644000176200001440000001105413267374743017165 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for 'RUnit:inspect' functions\n\n") .tearDown <- function() { if (exists("track", envir=.GlobalEnv)) { rm(track, envir=.GlobalEnv) } } bar <- function(x) { y <- 0 for(i in 1:100) { y <- y + i } return(y) } foo <- function(x) { if (length(x)) n <- length(x) len <- if(n==0) "zero" else if (n==1) "one" else if (n==2) "two" else if (n==3) "three" else "many" cat("object contains",len, "elements") return(n) } foo2 <- function(x) { if (length(x)) n <- length(x) ## should not confuse tracker len <- ifelse(n==0, "zero", "more") cat("object contains",len, "elements") } testRUnit.inspect <- function() { foo <- function(x) { y <- 0 for(i in 1:100) { y <- y + i } return(y) } ## the name track is necessary track <<- tracker() ## initialize the tracker track$init() ## inspect the function ## res will collect the result of calling foo res <- inspect(foo(10), track=track) checkEquals( res, 5050) } testRUnit.inspect.extended <- function() { ## the name track is necessary track <<- tracker() ## initialize the tracker track$init() # from ? svd hilbert <- function(n) { i <- 1:n; 1 / outer(i - 1, i, "+") } X <- hilbert(9)[,1:6] s <- svd(X) res <- inspect(svd(X), track=track) checkEquals( res, s) } testRUnit.getTrackInfo <- function() { ## the name track is necessary track <<- tracker() ## initialize the tracker track$init() ## inspect the function checkTrue( exists("bar")) ## res will collect the result of calling foo res <- inspect(bar(10), track=track) checkEquals( res, 5050) res <- inspect(foo(10), track=track) checkEquals( res, 1) res <- inspect(foo2(10), track=track) ## get the tracked function call info resTrack <- track$getTrackInfo() checkTrue( is.list(resTrack)) checkTrue( c("R/foo2") %in% names(resTrack)) checkEquals( names(resTrack$"R/foo2"), c("src", "run", "time", "graph", "nrRuns", "funcCall")) } testRUnit.printHTML <- function() { ## the name track is necessary track <<- tracker() ## initialize the tracker track$init() ## inspect the function checkTrue( exists("bar")) ## res will collect the result of calling foo res <- inspect(bar(10), track=track) checkEquals( res, 5050) ## get the tracked function call info resTrack <- track$getTrackInfo() outDir <- tempdir() ##checkTrue( is.null(printHTML(resTrack, baseDir=outDir))) checkTrue( is.null(printHTML.trackInfo(resTrack, baseDir=outDir))) checkTrue( "index.html" %in% dir(file.path(outDir, "results"))) inspect(foo(1:3), track=track) resTrack <- track$getTrackInfo() checkTrue( is.null(printHTML.trackInfo(resTrack, baseDir=outDir))) ## error handling checkException(printHTML("notCorrectClass")) ## baseDir checkException(printHTML(resTrack, baseDir=logical(1))) checkException(printHTML(resTrack, baseDir=character(0))) checkException(printHTML(resTrack, baseDir=character(2))) checkException(printHTML(resTrack, baseDir=as.character(NA))) } testRUnit.printHTML.extended <- function() { ## the name track is necessary track <<- tracker() ## initialize the tracker track$init() # from ? svd hilbert <- function(n) { i <- 1:n; 1 / outer(i - 1, i, "+") } X <- hilbert(9)[ ,1:6] s <- svd(X) res <- inspect(svd(X, LINPACK=TRUE), track=track) res <- inspect(svd(X), track=track) res <- inspect(svd(X, nu=nrow(X)), track=track) res <- inspect(svd(X, nv=ncol(X)), track=track) res <- inspect(svd(X, nv=0L), track=track) resTrack <- track$getTrackInfo() outDir <- tempdir() checkTrue( is.null(printHTML.trackInfo(resTrack, baseDir=outDir))) } RUnit/inst/unitTests/runitTearDown.r0000644000176200001440000000625113267374743017306 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## $Id$ cat("\n\nRUnit test cases for '.tearDown' function\n\n") ## defined for the life time of this environment warningsLengthDefault <- getOption("warnings.length") .tearDown <- function() { ## remove global variable if (exists("runitDummy", envir=.GlobalEnv)) { try(rm("runitDummy", envir=.GlobalEnv)) } ## remove temp file tempFile <- file.path(tempdir(), "runitDummyFile.txt") if (file.exists(tempFile)) { try(unlink(tempFile)) } ## reset options options(warnings.length=warningsLengthDefault) ## remove class if (length(findClass("runitDummyS4Class")) > 0) { try(removeClass("runitDummyS4Class", where=.GlobalEnv)) } } testRUnit..tearDown.Init <- function() { ##@bdescr ## testcase for function .tearDown of class: none ## setup vriables to be removed by .tearDown after test case execution ## check in subsequent test case that this operation chain succeeded ##@edescr ## define global variable checkTrue( !exists("runitDummy", where=.GlobalEnv)) assign("runitDummy", "this is a test dummy variable", envir=.GlobalEnv) checkTrue( exists("runitDummy", where=.GlobalEnv)) ## create temp file tempFile <- file.path(tempdir(), "runitDummyFile.txt") checkTrue( !file.exists(tempFile)) write.table(matrix(1:42, 6, 7), file=tempFile) checkTrue( file.exists(tempFile)) ## modify options ## current default: 1000 options(warnings.length=123) checkEqualsNumeric( getOption("warnings.length"), 123) ## define S4 class checkTrue( !isClass("runitDummyS4Class", where=.GlobalEnv)) setClass("runitDummyS4Class", representation(x = "numeric", y = "numeric"), prototype(x = 1:10, y = 10:1), where=.GlobalEnv) checkTrue( isClass("runitDummyS4Class", where=.GlobalEnv)) } testRUnit..tearDown.Test <- function() { ##@bdescr ## testcase for function .tearDown of class: none ## test that all modifications resulting from the previous test case ## have been removed as defined in .tearDown ##@edescr checkTrue( !exists("runitDummy", where=.GlobalEnv)) tempFile <- file.path(tempdir(), "runitDummyFile.txt") checkTrue( !file.exists(tempFile)) checkEqualsNumeric( getOption("warnings.length"), warningsLengthDefault) checkTrue( !isClass("runitDummyS4Class", where=.GlobalEnv)) } RUnit/inst/unitTests/runalltests.R0000644000176200001440000000056413267374743017022 0ustar liggesuserslibrary("RUnit") options(warn=1) testSuite <- defineTestSuite(name="RUnit", dirs=".", testFileRegexp="runit.*\\.r$", rngKind="default", rngNormalKind="default") testData <- runTestSuite(testSuite, verbose=0L) printTextProtocol(testData, showDetails=FALSE) RUnit/tests/0000755000176200001440000000000013267374743012475 5ustar liggesusersRUnit/tests/README0000644000176200001440000000012113267374743013347 0ustar liggesusers unit tests have been moved to inst/unitTests in the source package structure RUnit/NAMESPACE0000644000176200001440000000204413267374743012552 0ustar liggesusers###################################################################### ## ## RUnit ## ===================================== ## ## inst/NAMESPACE ## ===================================== ## initialization of classes, namespace, ... ## ## ## ## ## Version ## ===================================== ## $Id$ ## ## ###################################################################### import(utils) import(methods) importFrom("graphics", "arrows", "legend", "lines", "plot", "text") export(".setUp", ".tearDown", "checkTrue", "checkEquals", "checkEqualsNumeric", "checkException", "checkIdentical", "DEACTIVATED", "defineTestSuite", "getErrors", "inspect", "isValidTestSuite", # "print.RUnitTestData", "printTextProtocol", "printHTMLProtocol", "printJUnitProtocol", "printHTML.trackInfo", "runTestSuite", "runTestFile", # "summary.RUnitTestData", "tracker") S3method(print, RUnitTestData) S3method(summary, RUnitTestData) RUnit/NEWS0000644000176200001440000001540113277574003012023 0ustar liggesusers Dear Emacs, please make this -*-Text-*- mode! ************************************************** * * * RUnit * * * ************************************************** Changes in RUnit 0.4.32 o runTestSuite and runTestFile takes additional gcBeforeTest parameter (default FALSE). This disables running garbage collector before timing the test. This setting speeds up the test suite at the cost of making individual test timing less reliable. When trying to optimize tests for speed, set gcBeforeTest to TRUE for more reliable timing information. o added .Rinstignore to cut down on warnings when building the package o removed Biobase specific tests, replaced with direct S4 class creation o fixed CRAN URLs in README.md Changes in RUnit 0.4.31 o checkEquals and others output optional message on separate line Changes in RUnit 0.4.30 o printJUnitProtocol added for JUnit-style output Changes in RUnit 0.4.29 o changed maintainer to Roman Zenka o .testLogger global variable now stored in package environment RUnitEnv o added imports of graphics package to NAMESPACE Changes in RUnit 0.4.26 o isValidTestSuite: fixed insufficient if expression handling, reported by Rich Calaway; extended validity checks Changes in RUnit 0.4.25 o enable redirection of error and log messages to file, controlled via new global option 'outfile', (following a suggestion by Seth Falcon) Changes in RUnit 0.4.24 o added RUnit specific options 'verbose' and 'silent' to global options list to allow control of test log output o moved unit tests from tests/ to inst/unitTests o added Makefile for executing unit tests (using R wiki example) Changes in RUnit 0.4.23 o vignette: fixed function name in example code, reported by Blair Christian o init .testLogger to avoid R CMD check NOTE messages o allow verbosity of console output log to be controlled: added 'verbose' argument to runTestFile and runTestSuite (following a suggestion by Seth Falcon) o test logger object declared as S3 class 'TestLogger' Changes in RUnit 0.4.22 o clarified applicable license: GPL 2 o defineTestSuite: gained some argument checks o isValidTestSuite: check for empty name o includeTracker: fix `<- if` handling Changes in RUnit 0.4.21 o documentation issues corrected, identified by new R 2.9.0 devel Rd parser Changes in RUnit 0.4.20 o test protocol generation on Mac OS X failed due to incorrect code to identify 'gcc' version o Rd documentation updated Changes in RUnit 0.4.19 o test protocol now states check number per test case o changed check for object class to is() to allow derived class objects to pass (suggested by Philippe Grosjean) o removed start up message Changes in RUnit 0.4.18 o seq_along introduced instead of seq( ) for efficiency and R version dependency set to 2.4.0 when seq_along was introduced o some small changes to avoid warnings with options(warnPartialMatchArgs=TRUE) Changes in RUnit 0.4.17 o corrected documentation example code Changes in RUnit 0.4.16 o changed the environment test code files are evaluated, now a new environment outside the RUnit namespace is utilized, allowing e.g. setClass calls without specifying where=.GlobalEnv o updated documentation to use encoding latin1 o use LazyLoad: yes instead of SaveImage:yes (to be deprecated for R 2.6.0) o internal error handler rewritten to be more failure robust o added new test cases for .setUp and .tearDown, extended tests to cover S4 class and method behaviour in check* functions o example on S4 virtual class testing added o utility function to compare to RUnitTestResult objects added: concept idea for comparing and optimizing test suite performance (share/R/checkCode.r) Changes in RUnit 0.4.15 o compatibility to R 1.9.0 as declared in DESCRIPTION: removed calls to isTRUE as this was introduced only in R 2.1.0, replaced where needed by identical(TRUE, x) o fixed printHTMLProtocol: on Windows Makeconf does not exist so CC compiler used by R cannot be queried this way (reported by Tobias Verbeke) Changes in RUnit 0.4.14 o stated all package dependencies in DESCRIPTION, required for R 2.4.0 compatibility Changes in RUnit 0.4.13 o allow the RNG to be set by the user via new arguments 'rngKind' and 'rngNormalKind' to functions defineTestSuite and runTestFile (patch by Seth Falcon) o fixed exit status: RNG kind was left changed after runTestSuite execution in user workspace o email contact address modified Changes in RUnit 0.4.12 o allow *.R test case file extension (suggested by Gregor Gorjanc) o fixed code typo in example vignette (spotted by Gregor Gorjanc) Changes in RUnit 0.4.11 o checkException: argument silent added to allow to suppress error messages emitted by the failing function o inspect: added argument track, which _has_ to be provided at each invocation to avoid recursive default argument reference call errors (experimental: subject to change if this leads to new incompatibilities) Changes in RUnit 0.4.9 o checkEquals has new compatibility argument checkNames Changes in RUnit 0.4.8 o checkIdentical added, to allow to check for exact identity Changes in RUnit 0.4.7 o update for checkEqualsNumeric to be compatible with R 2.3.0 Changes in RUnit 0.4.5 o improvements to error detection in runTestSuite Changes in RUnit 0.4.4 o changed maintainer Changes in RUnit 0.4.2 o checkTrue: corrected handling of named logical arguments Changes in RUnit 0.4.1 o printHTMLProtocol has new parameter 'testFileToLinkMap' to allow to provide a function to add dynamically generated URLs for each test case file, e.g. for use with CVSweb Changes in RUnit 0.4.0 o New 'error' category DEACTIVATED introduced: If the function DEACTIVATED is inserted into a test function the function stops at that point and is reported as deactivated in the test protocol. o New function 'getErrors' which takes a list of type 'RUnitTestData' and returns some very basic error information of a test run. o The name of the currently executed test function is written to standard out. o 'printHTMLProtocol' fixed such that it does not produce a warning anymore. o Dependency on package 'splines' removed. o Various small fixes of the documentation. RUnit/R/0000755000176200001440000000000013274053413011516 5ustar liggesusersRUnit/R/checkFuncs.r0000644000176200001440000001714313267374743014001 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ checkEquals <- function(target, current, msg="", tolerance = .Machine$double.eps^0.5, checkNames=TRUE, ...) { ##@bdescr ## checks if two objects are equal, thin wrapper around 'all.equal' ## with tolerance one can adjust to and allow for numerical imprecision ##@edescr ##@in target : [ANY] one thing to be compared ##@in current : [ANY] second object to be compared ##@in msg : [character] an optional message to further identify and document the call ##@in tolerance : [numeric] directly passed to 'all.equal', see there for further documentation ##@in checkNames: [logical] iff TRUE do not strip names attributes from current and target prior to the comparison ##@ret : [logical] TRUE iff check was correct ## ##@codestatus : testing if (missing(current)) { stop("argument 'current' is missing") } if(!is.numeric(tolerance)) { stop("'tolerance' has to be a numeric value") } if (length(tolerance) != 1) { stop("'tolerance' has to be a scalar") } if(!is.logical(checkNames)) { stop("'checkNames' has to be a logical value") } if (length(checkNames) != 1) { stop("'checkNames' has to be a scalar") } if(.existsTestLogger()) { RUnitEnv$.testLogger$incrementCheckNum() } if (!identical(TRUE, checkNames)) { names(target) <- NULL names(current) <- NULL } result <- all.equal(target, current, tolerance=tolerance, ...) if (!identical(result, TRUE)) { if(.existsTestLogger()) { RUnitEnv$.testLogger$setFailure() } stop(paste(result, collapse="\n"), "\n", msg) } else { return(TRUE) } } checkEqualsNumeric <- function(target, current, msg="", tolerance = .Machine$double.eps^0.5, ...) { ##@bdescr ## checks if two objects are equal, thin wrapper around 'all.equal.numeric' ## with tolerance one can adjust to and allow for numerical imprecision. ## current and target are converted via as.vector() thereby stripping all attributes. ##@edescr ##@in target : [ANY] one thing to be compared ##@in current : [ANY] second object to be compared ##@in tolerance : [numeric] directly passed to 'all.equal.numeric', see there for further documentation ##@in msg : [character] an optional message to further identify and document the call ## ##@ret : [logical] TRUE, if objects 'target' and 'current' are equal w.r.t. specified numerical tolerance, else a stop signal is issued ## ##@codestatus : testing if (missing(current)) { stop("argument 'current' is missing") } if(!is.numeric(tolerance)) { stop("'tolerance' has to be a numeric value") } if (length(tolerance) != 1) { stop("'tolerance' has to be a scalar") } if(.existsTestLogger()) { RUnitEnv$.testLogger$incrementCheckNum() } ## R 2.3.0: changed behaviour of all.equal ## strip attributes before comparing current and target result <- all.equal.numeric(as.vector(target), as.vector(current), tolerance=tolerance, ...) if (!identical(result, TRUE)) { if(.existsTestLogger()) { RUnitEnv$.testLogger$setFailure() } stop(paste(result, collapse="\n"), "\n", msg) } else { return(TRUE) } } checkIdentical <- function(target, current, msg="") { ##@bdescr ## checks if two objects are exactly identical, thin convenience wrapper around 'identical' ## ##@edescr ##@in target : [ANY] one object to be compared ##@in current : [ANY] second object to be compared ##@in msg : [character] an optional message to further identify and document the call ## ##@ret : [logical] TRUE, if objects 'target' and 'current' are identical ## ##@codestatus : testing if (missing(current)) { stop("argument 'current' is missing") } if(.existsTestLogger()) { RUnitEnv$.testLogger$incrementCheckNum() } result <- identical(target, current) if (!identical(TRUE, result)) { if(.existsTestLogger()) { RUnitEnv$.testLogger$setFailure() } stop(paste(paste(result, collapse="\n"), "\n", msg)) } else { return(TRUE) } } checkTrue <- function(expr, msg="") { ##@bdescr ## checks whether or not something is true ##@edescr ##@in expr : [expression] the logical expression to be checked to be TRUE ##@in msg : [character] optional message to further identify and document the call ## ##@ret : [logical] TRUE, if the expression in a evaluates to TRUE, else a stop signal is issued ## ##@codestatus : testing if (missing(expr)) { stop("'expr' is missing") } if(.existsTestLogger()) { RUnitEnv$.testLogger$incrementCheckNum() } ## allow named logical argument 'expr' result <- eval(expr) names(result) <- NULL if (!identical(result, TRUE)) { if(.existsTestLogger()) { RUnitEnv$.testLogger$setFailure() } stop("Test not TRUE\n", msg) } else { return(TRUE) } } checkException <- function(expr, msg="", silent=getOption("RUnit")$silent) { ##@bdescr ## checks if a function call creates an error. The passed function must be parameterless. ## If you want to check a function with arguments, call it like this: ## 'checkException(function() func(args...))' ## ## adding argument silent was suggested by Seth Falcon ## who provided a patch. ##@edescr ##@in expr : [parameterless function] the function to be checked ##@in msg : [character] an optional message to further identify and document the call ##@in silent : [logical] passed on to try, iff TRUE error messages will be suppressed ## ##@ret : [logical] TRUE, if evaluation of the expression results in a 'try-error', else a stop signal is issued ## ##@codestatus : testing if (missing(expr)) { stop("'expr' is missing") } if(is.null(silent)) { silent <- FALSE warning("'silent' has to be of type 'logical'. Was NULL. Set to FALSE.") } if(.existsTestLogger()) { RUnitEnv$.testLogger$incrementCheckNum() } if (!inherits(try(eval(expr, envir=parent.frame()), silent=silent), "try-error")) { if(.existsTestLogger()) { RUnitEnv$.testLogger$setFailure() } stop("Error not generated as expected\n", msg) } else { return(TRUE) } } DEACTIVATED <- function(msg="") { ##@bdescr ## Convenience function, for maintaining test suites. ## If placed in an existing test case call ## the test will be executed normally until occurrence of the call ## after which execution will leave the test case (so all code will ## be checked and errors or failures reported as usual). ## An entry for a separate table in the log will be added ## for this test case. ## ##@edescr ##@in msg : [character] optional message to further identify and document the call ## ##@codestatus : testing if(.existsTestLogger()) { RUnitEnv$.testLogger$setDeactivated(paste(msg, "\n", sep="")) } stop(msg) } RUnit/R/inspector.r0000644000176200001440000003531213267374743013731 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ includeTracker <- function(fbody, track=track) { ##@bdescr ## Internal function ## ##@edescr ## ##@in fbody : [character] vector of code lines of function to track ##@in track : [trackInfo] list ##@ret : [list] with elements list(modFunc=c(sig,newBody),newSource = newCode) ## ##@codestatus : internal ## get the signature sig <- fbody[1] ## get the block structure (important for if, for, while, else with one line) block <- sapply(fbody[-1],function(x) regexpr("[^ ]",x)[1], USE.NAMES=FALSE) ## vector of keywords kwOpen <- c("for","while","repeat","if","else") ## keyword at begin kwGrep <- paste("(",paste(kwOpen,sep="",collapse="|"),")",sep="") oneLiner <- function(code) { ##@bdescr ## utility ## search a character vector ie. the vector of lines of a function body ## for block structures e.g. for|while|repeat|if|else { } code block ##@edescr ## ##@in code : [character] vector of function body code lines ##@ret : [logical] vector of length of code, indication which are one line control blocks ## ##@codestatus : internal return(sapply(code, function(line) { opBr <- grep(paste("^[ ]*",kwGrep,".*[ ]+$",sep=""), line) ## special case if combined with assignment or math operators opBr2 <- grep(paste("(<-|=|\\+|\\-|\\*|\\/)[ ]*if[ ]*\\(",sep=""), line) if(length(opBr) > 0 || length(opBr2) > 0) { return(TRUE) } return(FALSE) }, USE.NAMES=FALSE)) } ## set Brackets setBrackets <- function(potLine,block,env) { ##@bdescr ## ##@edescr ## ##@in potLine : [logical] mask vector which line contains a one-line control construct ##@in block : [integer] vector ##@in env : [logical] mask vector: which line already contains a opening brace ##@ret : [list] with matching element vectors: openBr & clodeBr ## ##@codestatus : internal oBr <- character(length(potLine)) clBr <- character(length(potLine)) lineIdx <- 1L while(lineIdx < length(potLine)) { if(potLine[lineIdx] && !(potLine[lineIdx+1])) { oBr[lineIdx] <- "{" if (!env[lineIdx+1]) { clBr[lineIdx+2] <- paste(clBr[lineIdx+2],"}") } else { bbl <- block[lineIdx] endBlockIdx <- min(which((bbl >= block) & (seq_along(block) > lineIdx))) clBr[endBlockIdx] <- paste(clBr[endBlockIdx],"}") } } else if(potLine[lineIdx] && (potLine[lineIdx+1]) ) { oBr[lineIdx] <- "{" bbl <- block[lineIdx] endBlockIdx <- min(which((bbl >= block) & (seq_along(block) > lineIdx))) clBr[endBlockIdx] <- paste(clBr[endBlockIdx],"}") } lineIdx <- lineIdx + 1L } return(list(openBr=oBr, closeBr=clBr)) } ## check for new environments env <- sapply(fbody[-1], function(code) { envIdx <- grep("\\{$",code) if(length(envIdx) > 0) { return(TRUE) } return(FALSE) },USE.NAMES=FALSE) ## check the block structure block <- sapply(fbody[-1], function(x) regexpr("[^ ]",x)[1], USE.NAMES=FALSE) ## is 4 a convention or a rule? block <- (block %/% 4) + 1 ## check for if's, while's, etc. ol <- oneLiner(fbody[-1]) ## create brackets for control structures without new environments br <- setBrackets(ol,block,env) ## create new Code newCode <- paste(as.vector(rbind(br$closeBr,fbody[-1],br$openBr))) newCode <- newCode[newCode != ""] ## include the breakpoint function bpVec <- sapply(newCode, function(line) { nobp <- grep("^[ ]*(else |\\{|\\})",line) if(length(nobp) == 0) { return("track$bp();") } return("") },USE.NAMES=FALSE) for(i in seq_along(bpVec)) { bpVec[i] <- gsub("\\(\\)",paste("(",i,")",sep=""),bpVec[i]) } ## create the mainpulated body of the function newBody <- paste(bpVec,newCode) ## return signature and body return(list(modFunc=c(sig,newBody), newSource=newCode)) } tracker <- function() { ##@bdescr ## initialization of the central tracking object ## which stores all information related to inspection results and code execution structure ## defines accessor functions ## - addFunc (fId,src,callExpr): add specified function to the track list ## - getSource(nr): get the source code (character) for function nr on track list ## - init ## - bp ## - getTrackInfo ## - isValidTrackInfo ##@edescr ## ##@ret : [list] OO object with functions addFunc, getSourcee, init, bp, getTrackInfo ## ##@codestatus : testing ## object for information trackInfo <- list() class(trackInfo) <- "trackInfo" ## current function index fIdx <- 0 ## old time oldTime <- NULL ## old src line oldSrcLine <- 0 addFunc <- function(fId,src,callExpr) { ##@bdescr ## ## accessor function ##@edescr ## ##@in fId : [character] function name ##@in src : [character] source code character vector ##@in callExpr : [character] function call ##@ret : [NULL] returns invisible, used for its side effects ## ## codestatus : internal ## preconditions if( length(fId) != 1) { stop("fId must be one character string: function name") } isThere <- which(fId == names(trackInfo)) if(length(isThere) == 1) { ## already in tracking list fIdx <<- isThere } else { fIdx <<- fIdx + 1 newFuncInfo <- list(src=src, run=integer(length(src)), time=numeric(length(src)), graph=matrix(0,nrow=length(src),ncol=length(src)), nrRuns=as.integer(0), funcCall=callExpr) ## append strips class attribute trackInfo <- append(trackInfo,list(newFuncInfo)) names(trackInfo)[fIdx] <- fId class(trackInfo) <- "trackInfo" ## update global state trackInfo <<- trackInfo } ## increment run number trackInfo[[fIdx]]$nrRuns <<- trackInfo[[fIdx]]$nrRuns + 1 ## initialize local variables oldSrcLine <<- 0 oldTime <<- NULL return(invisible()) } getTrackInfo <- function() { ##@bdescr ## ## accessor function ## returns the main inspection result list with ## elements ## - src ## - run ## - time ## - graph ## - nrRuns ## - funCall ##@edescr ## ##@ret : [trackInfo] S3 class list (see description above) ## ## codestatus : internal return(trackInfo) } init <- function() { ##@bdescr ## ## initalisation function ## sets/resets variables run and fIdx ##@edescr ## ##@ret : [NULL] returns invisible, used for its side effects ## ## codestatus : internal trackInfoInit <- list() class(trackInfoInit) <- "trackInfo" trackInfo <<- trackInfoInit fIdx <<- 0L return(invisible()) } bp <- function(nr) { ##@bdescr ## ## accessor function ##@edescr ## ##@in : [integer] index, function run number ##@ret : [NULL] returns invisible, used for its side effects ## ## codestatus : internal ## preconditions if (length(nr) != 1) { stop("argument 'nr' has to be of length 1.") } if (is.na(nr)) { stop("argument 'nr' may not contain missing value (NA).") } trackInfo[[fIdx]]$run[nr] <<- trackInfo[[fIdx]]$run[nr] + 1 ## cumulative processing time if(!is.null(oldTime)) { dtime <- proc.time()[1] - oldTime trackInfo[[fIdx]]$time[nr] <<- trackInfo[[fIdx]]$time[nr] + dtime } oldTime <<- proc.time()[1] ## graph if(oldSrcLine != 0) { trackInfo[[fIdx]]$graph[oldSrcLine,nr] <<- trackInfo[[fIdx]]$graph[oldSrcLine,nr] + 1 } ## store the old line oldSrcLine <<- nr return(invisible()) } getSource <- function(nr) { ##@bdescr ## ## accessor function ## returns the source code as character string ##@edescr ## ##@in : [integer] index, function run number ##@ret : [character] string, source code ## ##@codestatus : untested ## preconditions if (length(nr) != 1) { stop("argument 'nr' has to be of length 1.") } if (is.na(nr)) { stop("argument 'nr' may not contain missing value (NA).") } return(trackInfo[[nr]]$src) } isValidTrackInfo <- function(trackInfo) { ##@bdescr ## test function ## returns TRUE iff trackInfo object fullfils S3 class definition constraints ## - S3 class 'trackInfo' ## - with elements ## - src [character] vector of function source code lines ## - run [integer] vector of no. of times this function was called ## - time [numeric] vector of function execution times in seconds per call ## - graph [matrix] connection matrix (# code linbes x # of execution calls) ## - nrRuns [integer] ## - funcCall [character] function call ##@edescr ## ##@in : [trackInfo] S3 class object ##@ret : [logical] TRUE, iff object fullfils class definition constraints ## ##@codestatus : untested if (!is(trackInfo,"trackInfo")) { return(FALSE) } checkElements <- function(x) { if (!all(c("src", "run", "time", "graph", "nrRuns", "funcCall") %in% names(x))) { return(FALSE) } if (length(x[["run"]]) < 1 || any(is.na(x[["run"]])) || any(x[["run"]] < 0)) { return(FALSE) } if (length(x[["time"]]) < 1 || any(is.na(x[["time"]])) || any(x[["time"]] < 0)) { return(FALSE) } ## TODO: graph if (length(x[["nrRuns"]]) != 1 || is.na(x[["nrRuns"]]) || x[["nrRuns"]] < 0) { return(FALSE) } if (length(x[["funcCall"]]) != 1 || is.na(x[["funcCall"]])) { return(FALSE) } } ok <- sapply(trackInfo, checkElements) if (!all(ok)) { return(FALSE) } return(TRUE) } return(list(addFunc=addFunc, getSource=getSource, init=init, bp=bp, getTrackInfo=getTrackInfo, isValid=isValidTrackInfo)) } inspect <- function(expr, track=track) { ##@bdescr ## inspector function ## an attempt is made to parse the expression or function ## insert track info statements to be used for subsequent ## code execution structure displays ## ## can handle functions aswell as generics ##@edescr ## ##@in expr : [call] ##@in track : [list] tracker object ##@ret : [expression|ANY] either the unevaluated expression of the function or the result of the function call ## ##@codestatus : testing ## get the call and its parameters fCall <- as.character(substitute(expr)) ## get the original call callExpr <- deparse(substitute(expr)) ## get the name of the function fname <- fCall[1] ## check for generic function if(isGeneric(fname)) { ## get type of arguments selType <- sapply(fCall[-1], function(x) { if(exists(x, envir=sys.parent(sys.parent()))) { varSig <- is(get(x,envir=sys.parent(sys.parent())))[1] } else { varSig <- is(eval(parse(text=x)))[1] } return(varSig) },USE.NAMES=FALSE) ## we have to check for missing arguments formalArg <- names(formals(getGeneric(fCall[1]))) ## number of missing arguments nrMissing <- length(formalArg) - length(selType) if(nrMissing > 0) { ## check for ... ellipseIdx <- which(formalArg == "...") if(length(ellipseIdx) != 0) { selType <- c(selType,rep("missing",nrMissing -1 )) } else { selType <- c(selType,rep("missing",nrMissing)) } } ## select function selFunc <- selectMethod(fname, selType) ## deparse the function fbody <- deparse(selFunc@.Data, width.cutoff=500) ## create an identifier for the generic function fNameId <- paste("S4",fname,paste(selFunc@defined@.Data, collapse="/"), sep="/") } else { ## deparse the function fbody <- try(deparse(get(fname), width.cutoff=500), silent=TRUE) if (inherits(fbody, "try-error")) { ## in case the function is defined ## in the test case file fbody <- try(deparse(get(fname, envir=sys.parent()), width.cutoff=500)) if (inherits(fbody, "try-error")) { stop("function not found.") } } ## create an identifier for the generic function fNameId <- paste("R/",fname,sep="") } ## generate the new body of the function newFunc <- includeTracker(fbody, track=track) track$addFunc(fNameId, newFunc$newSource, callExpr) ## build the test function eval(parse(text=c("testFunc <- ",newFunc$modFunc)),envir=sys.frame()) ## create function call newFunCall <- paste("testFunc(",paste(fCall[-1], collapse=","), ")",sep="") parsedFunc <- try(parse(text=newFunCall)) ## check for an error if(!inherits(parsedFunc,"try-error")) { ## call the new function res <- eval(parsedFunc, envir=parent.frame()) } else { ## no parsing possible ## simple call without tracking res <- expr } ## do here some error checking return(res) } RUnit/R/junitProtocol.r0000644000176200001440000001013313267374743014570 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA printJUnitProtocol <- function(testData, fileName = "") { ##@bdescr ## Report generator ## Extracts the log information stored in the 'RUnitTestData' test run object ## and generates a JUnit-style formated XML output. ##@edescr ## ##@in testData : [RUnitTestData] S3 class object ##@in fileName : [character] string, full path + file name to be written to ##@ret : [logical] TRUE if execution completed without error ## ##@codestatus : testing ## preconditions if (!is(testData, "RUnitTestData")) { stop("Argument 'testData' must be of class 'RUnitTestData'.") } if (!is.character(fileName)) { stop("Argument 'fileName' has to be of type character.") } if (length(fileName) != 1) { stop("Argument 'fileName' must contain exactly one element.") } errInfo <- getErrors(testData) # Create entry fro all test suites xml.testsuites <- XML::newXMLNode("testsuites", attrs = c( errors=errInfo$nErr, failures=errInfo$nFail, tests=errInfo$nTestFunc) ) for (tsName in names(testData)) { # Create entry for test suite xml.testsuite <- XML::newXMLNode("testsuite", attrs = c( errors = testData[[tsName]]$nErr, failures = testData[[tsName]]$nFail, name = tsName, tests = testData[[tsName]]$nTestFunc )) XML::addChildren(xml.testsuites, kids=c(xml.testsuite)) if (testData[[tsName]]$nErr + testData[[tsName]]$nFail >= 0) { srcFileRes <- testData[[tsName]][["sourceFileResults"]] for (i in seq_along(srcFileRes)) { testFuncNames <- names(srcFileRes[[i]]) for (j in seq_along(testFuncNames)) { funcList <- srcFileRes[[i]][[testFuncNames[j]]] # Each tested function gets a testcase xml.testcase <- XML::newXMLNode("testcase", attrs=c(name=testFuncNames[j], time=funcList$time[['elapsed']])) XML::addChildren(xml.testsuite, kids=c(xml.testcase)) if (funcList$kind == "success") { } else if (funcList$kind == "error") { xml.error <- XML::newXMLNode("error", attrs=c( "message"=funcList$msg, "type"="ERROR")) XML::addChildren(xml.testcase, kids=c(xml.error)) } else if (funcList$kind == "failure") { xml.error <- XML::newXMLNode("failure", attrs=c( "message"=funcList$msg, "type"="FAILURE")) XML::addChildren(xml.testcase, kids=c(xml.error)) } else if (funcList$kind == "deactivated") { xml.skipped <- XML::newXMLNode("skipped") XML::addChildren(xml.testcase, kids=c(xml.skipped)) } } } } } xml <- XML::saveXML(xml.testsuites) if(fileName=="") { write(xml, stdout()) } else { dir.create(dirname(fileName), showWarnings=FALSE, recursive=TRUE) fileConn <- file(fileName) write(xml, fileConn) close(fileConn) } return(invisible(TRUE)) } RUnit/R/textProtocol.r0000644000176200001440000002110113267374743014420 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ printTextProtocol <- function(testData, fileName = "", separateFailureList = TRUE, showDetails = TRUE, traceBackCutOff=9) { ##@bdescr ## Report generator ## Extracts the log information stored in the 'RUnitTestData' test run object ## and generates a well formated protocol output. ##@edescr ## ##@in testData : [RUnitTestData] S3 class object ##@in fileName : [character] string, full path + file name to be written to ##@in separateFailureList : [logical] if TRUE (default) add a failure list ##@in showDetails : [logical] if TRUE (default) add detailed traceback for each error incurred ##@in traceBackCutOff : [integer] number of steps back in the trace back stack to display ##@ret : [logical] TRUE if execution completed without error ## ##@codestatus : testing ## preconditions if (!is(testData, "RUnitTestData")) { stop("Argument 'testData' must be of class 'RUnitTestData'.") } if (!is.character(fileName)) { stop("Argument 'fileName' has to be of type character.") } if (length(fileName) != 1) { stop("Argument 'fileName' must contain exactly one element.") } if (!is.logical(separateFailureList)) { stop("Argument 'separateFailureList' has to be of type logical.") } if (length(separateFailureList) != 1) { stop("Argument 'separateFailureList' must contain exactly one element.") } if (!is.logical(showDetails)) { stop("Argument 'showDetails' has to be of type logical.") } if (length(showDetails) != 1) { stop("Argument 'showDetails' must contain exactly one element.") } if (!is.numeric(traceBackCutOff)) { stop("Argument 'traceBackCutOff' has to be of type logical.") } if (length(traceBackCutOff) != 1) { stop("Argument 'traceBackCutOff' must contain exactly one element.") } if (traceBackCutOff < 0 || traceBackCutOff > 100) { stop("Argument 'traceBackCutOff' out of valid range [0, 100].") } ## just a convenience function pr <- function(..., sep=" ", nl=TRUE) { if(nl) { cat(... , "\n", file = fileName, append=TRUE, sep=sep) } else { cat(... , file = fileName, append=TRUE, sep=sep) } } ## get singular or plural right sop <- function(number, word, plext="s") { ifelse(number == 1, paste(number, word), paste(number, paste(word, plext, sep=""))) } ## header part cat("RUNIT TEST PROTOCOL --", date(), "\n", file = fileName) pr("***********************************************") if(length(testData) == 0) { pr("no test cases :-(") return(invisible(TRUE)) } errInfo <- getErrors(testData) pr("Number of test functions:", errInfo$nTestFunc) if(errInfo$nDeactivated > 0) { pr("Number of deactivated test functions:", errInfo$nDeactivated) } pr("Number of errors:", errInfo$nErr) pr("Number of failures:", errInfo$nFail, "\n\n") ## summary of test suites pr(sop(length(testData), "Test Suite"), ":") for(tsName in names(testData)) { pr(tsName, " - ", sop(testData[[tsName]]$nTestFunc, "test function"), ", ", sop(testData[[tsName]]$nErr, "error"), ", ", sop(testData[[tsName]]$nFail, "failure"), sep="") if(separateFailureList && (testData[[tsName]]$nErr + testData[[tsName]]$nFail > 0)) { srcFileRes <- testData[[tsName]][["sourceFileResults"]] for(i in seq_along(srcFileRes)) { testFuncNames <- names(srcFileRes[[i]]) for(j in seq_along(testFuncNames)) { funcList <- srcFileRes[[i]][[testFuncNames[j]]] if(funcList$kind == "error") { pr("ERROR in ", testFuncNames[j], ": ", funcList$msg, nl=FALSE, sep="") } else if(funcList$kind == "failure") { pr("FAILURE in ", testFuncNames[j], ": ", funcList$msg, sep="", nl=FALSE) } else if(funcList$kind == "deactivated") { pr("DEACTIVATED ", testFuncNames[j], ": ", funcList$msg, sep="", nl=FALSE) } } } } } ## if no details are required, we are done. if(!showDetails) { return(invisible(TRUE)) } pr("\n\n\nDetails") ## loop over all test suites for(tsName in names(testData)) { tsList <- testData[[tsName]] pr("***************************") pr("Test Suite:", tsName) pr("Test function regexp:", tsList$testFuncRegexp) pr("Test file regexp:", tsList$testFileRegexp) if(length(tsList$dirs) == 0) { pr("No directories !") } else { if(length(tsList$dirs) == 1) { pr("Involved directory:") } else { pr("Involved directories:") } for(dir in tsList$dirs) { pr(dir) } res <- tsList$sourceFileResults testFileNames <- names(res) if(length(res) == 0) { pr("no test files") } else { ## loop over all source files for(testFileName in testFileNames) { testFuncNames <- names(res[[testFileName]]) if(length(testFuncNames) > 0) { pr("---------------------------") pr("Test file:", testFileName) ## loop over all test functions in the test file for(testFuncName in testFuncNames) { testFuncInfo <- res[[testFileName]][[testFuncName]] if(testFuncInfo$kind == "success") { pr(testFuncName, ": (",testFuncInfo$checkNum, " checks) ... OK (", testFuncInfo$time, " seconds)", sep="") } else { if(testFuncInfo$kind == "error") { pr(testFuncName, ": ERROR !! ", sep="") } else if (testFuncInfo$kind == "failure") { pr(testFuncName, ": FAILURE !! (check number ", testFuncInfo$checkNum, ")", sep="") } else if (testFuncInfo$kind == "deactivated") { pr(testFuncName, ": DEACTIVATED, ", nl=FALSE) } else { pr(testFuncName, ": unknown error kind", sep="") } pr(testFuncInfo$msg, nl=FALSE) if(length(testFuncInfo$traceBack) > 0) { pr(" Call Stack:") if(traceBackCutOff > length(testFuncInfo$traceBack)) { pr(" (traceBackCutOff argument larger than length of ", "trace back: full trace back printed)") for(i in 1:length(testFuncInfo$traceBack)) { pr(" ", i, ": ", testFuncInfo$traceBack[i], sep="") } } else { for(i in traceBackCutOff:length(testFuncInfo$traceBack)) { pr(" ", 1+i-traceBackCutOff, ": ", testFuncInfo$traceBack[i], sep="") } } } } } } } } } } ## return type return(invisible(TRUE)) } print.RUnitTestData <- function(x, ...) { ##@bdescr ## Generic print method ##@edescr ## ##@in x : [RUnitTestData] S3 class object ##@in ... : [ANY] currently ignored ##@ret : [NULL] ## ##@codestatus : untested errInfo <- getErrors(x) cat("Number of test functions:", errInfo$nTestFunc, "\n") if(errInfo$nDeactivated > 0) { cat("Number of deactivated test functions:", errInfo$nDeactivated, "\n") } cat("Number of errors:", errInfo$nErr, "\n") cat("Number of failures:", errInfo$nFail, "\n") } summary.RUnitTestData <- function(object, ...) { ##@bdescr ## Generic summary method ##@edescr ## ##@in object : [RUnitTestData] S3 class object ##@in ... : [ANY] ##@ret : [logical] return valof from printTextProtocol ## ##@codestatus : untested printTextProtocol(object, ...) } RUnit/R/html.r0000644000176200001440000003104613267374743012667 0ustar liggesusers## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ writeRaw <- function(htmlStr,htmlFile,append=TRUE) { ##@bdescr ## private function ## write raw text in a html file ##@bdescr ##@in htmlStr : [character] text ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal cat(htmlStr,file=htmlFile,append=append) invisible(TRUE) } writeRawCR <- function(htmlStr,htmlFile,append=TRUE) { ##@bdescr ## private function ## write raw text in a html file with a cr at end ##@bdescr ##@in htmlStr : [character] text ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw(htmlStr,htmlFile,append) cat("\n",file=htmlFile,append=TRUE) invisible(TRUE) } writeTitle <- function(htmlStr,htmlFile,append=TRUE) { ##@bdescr ## private function ## write title tags and title text ##@bdescr ##@in htmlStr : [character] title ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("",htmlFile,append) writeRaw(htmlStr,htmlFile) writeRaw("\n",htmlFile) } writeBeginHead <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("",htmlFile,append) } writeEndHead <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("\n",htmlFile,append) } writeBeginHtml <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("",htmlFile,append) } writeEndHtml <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("\n",htmlFile,append) } writeBeginBody <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("",htmlFile,append) } writeEndBody <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw("\n",htmlFile,append) } writeBeginTag <- function(htmlTag,htmlFile,para="",append=TRUE) { ##@bdescr ## private function ## write begin of a tag, with parameters ##@bdescr ##@in htmlTag : [character] name of the tag ##@in htmlFile : [character] name of the html file ##@in para : [character] parameters as string ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal if(all(para =="")) { writeRaw(paste("<",htmlTag,">",sep=""),htmlFile,append) } else { writeRaw(paste("<",htmlTag," ",para,">",sep=""),htmlFile,append) } } writeEndTag <- function(htmlTag,htmlFile,append=TRUE) { ##@bdescr ## private function ## write end of tag ##@bdescr ##@in htmlTag : [character] name of the tag ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRaw(paste("",sep=""),htmlFile,append) } writeCR <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## write CR in html file for better formatting of the html source ##@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal cat("\n",file=htmlFile,append=append) invisible(TRUE) } writeBeginTable <- function(header,htmlFile,border=1, width="100%",append=TRUE, columnWidth=NULL) { ##@bdescr ## private function ## write begin of a table ##@bdescr ##@in header : [character] title for columns ##@in htmlFile : [character] name of the html file ##@in border : [integer] border of table ##@in width : [character] width of table ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal tablePara <- paste("border=\"",border,"\" width=\"",width,"\"",sep="") writeRawCR(paste("",sep=""),htmlFile,append) ## if header is provided if (length(header) > 0) { writeBeginTag("tr",htmlFile) for(i in seq_along(header)) { para <- "" if(!is.null(columnWidth)) { if (length(columnWidth) == length(header)) { para = paste("width=\"", columnWidth[i], "\"", sep="") } else { ## recycle first para = paste("width=\"", columnWidth[1], "\"", sep="") } } writeBeginTag("th",htmlFile, para=para) writeRaw(header[i],htmlFile) writeEndTag("th",htmlFile) writeCR(htmlFile) } writeEndTag("tr",htmlFile,append) } writeCR(htmlFile) } writeTableRow <- function(row,htmlFile,append=TRUE,bgcolor="") { ##@bdescr ## private function ## write a table row ##@bdescr ##@in row : [character] data for table cells in row ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@in bgcolor : [character] color for table cells ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeBeginTag("tr",htmlFile) if(length(bgcolor) == 1) { bgcolor <- rep(bgcolor,length(row)) } for(i in seq_along(row)) { if(bgcolor[i] == "") { writeBeginTag("td",htmlFile) } else { writeBeginTag("td",htmlFile,para=paste("bgcolor=\"",bgcolor[i],"\"",sep="")) } writeRaw(row[i],htmlFile) writeEndTag("td",htmlFile) writeCR(htmlFile) } writeEndTag("tr",htmlFile,append) writeCR(htmlFile) } writeLink <- function(target,name,htmlFile,append=TRUE) { ##@bdescr ## private function ## write a link ##@bdescr ##@in target : [character] target of the link ##@in name : [character] name of the target ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeBeginTag("a",htmlFile,paste("href=\"",target,"\"",sep=""),append=append) writeRaw(name,htmlFile,append=TRUE) writeEndTag("a",htmlFile,append=TRUE) } writeEndTable <- function(htmlFile,append=TRUE) { ##@bdescr ## private function ## close an
enviroment by adding
#@bdescr ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeEndTag("table",htmlFile,append) writeCR(htmlFile) } writeHtmlHeader <- function(header,htmlFile) { ##@bdescr ## private function ## write a HTML file header ## - DOCTYPE ## - ## - </head> ## - <body> ## ## should be finished by writeHtmlEnd ##@bdescr ##@in header : [character] title of the document ##@in htmlFile : [character] name of the link ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRawCR("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"", htmlFile,FALSE) writeRawCR("\"http://www.w3.org/TR/html4/transitional.dtd\">",htmlFile) writeBeginHtml(htmlFile) writeBeginHead(htmlFile) writeTitle(header,htmlFile) writeEndHead(htmlFile) writeBeginBody(htmlFile) } writeHtmlEnd <- function(htmlFile) { ##@bdescr ## private function ## write end of html code ##@bdescr ##@in htmlFile : [character] name of the html file ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeEndBody(htmlFile) writeEndHtml(htmlFile) } writeHtmlSep <- function(htmlFile) { ##@bdescr ## private function ## write horizontal seperator ##@bdescr ##@in htmlFile : [character] name of the html file ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeRawCR("<hr>",htmlFile) } writeImage <- function(img,htmlFile,append=TRUE) { ##@bdescr ## private function ## write image tags ##@bdescr ##@in img : [character] name of the image file ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal writeBeginTag("img",htmlFile,para=paste("src=\"",img,"\"",sep=""),append) writeEndTag("img",htmlFile) } writeHtmlSection <- function(title,sec,htmlFile,append=TRUE) { ##@bdescr ## private function ## write titles for section ##@bdescr ##@in title : [character] title of the section ##@in sec : [integer] size of title (between 1-6) ##@in htmlFile : [character] name of the html file ##@in append : [logical] append the html code ##@ret : [logical] TRUE if execution completes ## ##@codestatus : internal secTag <- paste("h",sec,sep="") writeBeginTag(secTag,htmlFile,append) writeRaw(title,htmlFile,append) writeEndTag(secTag,htmlFile,append) writeCR(htmlFile,append) } writeHtmlTable <- function(dataFrame, htmlFile, border=1, width="100%", append=TRUE) { ##@bdescr ## private function ## write a data frame to a HTML table ##@bdescr ## ##@in dataFrame : [data frame] size of title (between 1-6) ##@in htmlFile : [character] name of the html file ##@in border : [integer] 1 (default) table borders will be shown ##@in width : [character] width of table ##@in append : [logical] if TRUE append the tabel to an existing HTML file ##@ret : [logical] TRUE if execution completed ## ##@codestatus : internal header <- NULL colNames <- colnames(dataFrame) if (!is.null(colNames)) { if (length(colNames) == dim(dataFrame)[2]) { header <- colNames } else { ## don't write column names header <- NULL } } rowNames <- rownames(dataFrame) if (!is.null(rowNames)) { header <- c("Name", header) dataFrame <- cbind(rowNames, dataFrame) } writeBeginTable(header, htmlFile, border=border, width=width, append=append, columnWidth=NULL) for (ti in 1:dim(dataFrame)[1]) { writeTableRow(dataFrame[ti, ], htmlFile, append=TRUE, bgcolor="") } writeEndTable(htmlFile,append=TRUE) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/R/00Init.r������������������������������������������������������������������������������������0000644�0001762�0000144�00000002524�13267374743�012765� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������###################################################################### ## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ RUnitEnv <- new.env() .onLoad <- function(libname, pkgname) { ##@bdescr ## Internal Function. ## Not to be called by users. ##@edescr ## runitVersion <- packageDescription("RUnit", lib.loc=libname, fields="Version") assign(".testLogger", NULL, envir=RUnitEnv) ## add options to R's global options list .buildRUnitOptions() } .onUnload <- function(libpath) { ## drop RUnit specific options from global options list options("RUnit"=NULL) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/R/htmlProtocol.r������������������������������������������������������������������������������0000644�0001762�0000144�00000041413�13267374743�014410� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ printHTMLProtocol <- function(testData, fileName = "", separateFailureList = TRUE, traceBackCutOff=9, testFileToLinkMap=function(x) x) { ##@bdescr ## Report generator ## Extracts the log information stored in the 'RUnitTestData' test run object ## and generates a well formated HTML output. ##@edescr ## ##@in testData : [RUnitTestData] S3 class object ##@in fileName : [character] ##@in separateFailureList : [logical] if TRUE (default) add a list of all failures ##@in traceBackCutOff : [integer] number of steps back in the trace back stack to be displayed ##@in testFileToLinkMap : [function] a function transforming the full name of the test file to a link location ##@ret : [logical] TRUE if execution completed w/o error ## ##@codestatus : testing ## -------------------------------- ## CHECK OF INPUT DATA ## -------------------------------- if (!is(testData, "RUnitTestData")) { stop("Argument 'testData' must be of class 'RUnitTestData'.") } if (!is.character(fileName)) { stop("Argument 'fileName' has to be of type character.") } if (length(fileName) != 1) { stop("Argument 'fileName' must contain exactly one element.") } if (!is.logical(separateFailureList)) { stop("Argument 'separateFailureList' has to be of type logical.") } if (length(separateFailureList) != 1) { stop("Argument 'separateFailureList' must contain exactly one element.") } if (!is.numeric(traceBackCutOff)) { stop("Argument 'traceBackCutOff' has to be of type logical.") } if (length(traceBackCutOff) != 1) { stop("Argument 'traceBackCutOff' must contain exactly one element.") } if (traceBackCutOff < 0 || traceBackCutOff > 100) { stop("Argument 'traceBackCutOff' out of valid range [0, 100].") } ## -------------------------------- ## HELPER FUNCTIONS ## -------------------------------- ## get singular or plural right sop <- function(number, word, plext="s") { ifelse(number == 1, paste(number, word), paste(number, paste(word, plext, sep=""))) } pr <- function(...) { writeRaw(paste(...), htmlFile=fileName) writeRaw("<br/>", htmlFile=fileName) } writeP <- function(string, para="") { writeBeginTag("p", para=para, htmlFile=fileName) writeRaw(string, htmlFile=fileName) writeEndTag("p", htmlFile=fileName) writeCR(htmlFile=fileName) } writeLi <- function(..., para="") { writeBeginTag("li", para=para, htmlFile=fileName) writeRaw(paste(...), htmlFile=fileName) writeEndTag("li", htmlFile=fileName) } createTestFuncRef <- function(testSuite, srcFileName, testFuncName, asAnchor=FALSE) { tmp <- paste(testSuite, srcFileName, testFuncName, sep="_") if(asAnchor) { return(paste("#", gsub("/", "_", tmp), sep="")) } else { return(gsub("/", "_", tmp)) } } printTraceBack <- function(traceBack) { if(length(traceBack) > 0) { writeRaw("Call Stack:<br/>", htmlFile=fileName) if(traceBackCutOff > length(testFuncInfo$traceBack)) { writeRaw("(traceBackCutOff argument larger than length of trace back: full trace back printed)<br/>", htmlFile=fileName) writeBeginTag("ol", htmlFile=fileName) for(i in seq_along(traceBack)) { writeBeginTag("li", htmlFile=fileName) writeRaw(traceBack[i], htmlFile=fileName) writeEndTag("li", htmlFile=fileName) } } else { writeBeginTag("ol", htmlFile=fileName) for(i in traceBackCutOff:length(traceBack)) { writeBeginTag("li", htmlFile=fileName) writeRaw(traceBack[i], htmlFile=fileName) writeEndTag("li", htmlFile=fileName) } } writeEndTag("ol", htmlFile=fileName) } } errorStyle <- "color:red" deactivatedStyle <- "color:black" ## -------------------------------------------- ## PART 1: TITLE AND BASIC ERROR INFORMATION ## -------------------------------------------- ## title title <- paste("RUNIT TEST PROTOCOL", date(), sep="--") writeHtmlHeader(title, htmlFile=fileName) writeHtmlSection(title, 1, htmlFile=fileName) if(length(testData) == 0) { writeP(" no test cases :-(") return(invisible(TRUE)) } ## basic Info errInfo <- getErrors(testData) writeP(paste("Number of test functions:", errInfo$nTestFunc)) if(errInfo$nDeactivated > 0) { writeP(paste("Number of deactivated test functions:", errInfo$nDeactivated), para=ifelse(errInfo$nDeactivated == 0, "", paste("style", deactivatedStyle, sep="="))) } writeP(paste("Number of errors:", errInfo$nErr), para=ifelse(errInfo$nErr == 0, "", paste("style", errorStyle, sep="="))) writeP(paste("Number of failures:", errInfo$nFail), para=ifelse(errInfo$nFail == 0, "", paste("style", errorStyle, sep="="))) writeHtmlSep(htmlFile=fileName) ## -------------------------------- ## PART 2: TABLE OF TEST SUITES ## -------------------------------- ## summary of test suites writeHtmlSection(sop(length(testData), "Test suite"), 3, htmlFile=fileName) ## table of test suites if(errInfo$nDeactivated > 0) { writeBeginTable(c("Name", "Test functions", "Deactivated", "Errors", "Failures"), width="80%", htmlFile=fileName, columnWidth=c("20%", "20%", "20%", "20%", "20%")) for(tsName in names(testData)) { rowString <- c(paste("<a href=\"#", tsName, "\">", tsName, "</a>", sep=""), testData[[tsName]]$nTestFunc, testData[[tsName]]$nDeactivated, testData[[tsName]]$nErr, testData[[tsName]]$nFail) rowCols <- c("", "", ifelse(testData[[tsName]]$nDeactivated==0, "", "yellow"), ifelse(testData[[tsName]]$nErr==0, "", "red"), ifelse(testData[[tsName]]$nFail==0, "", "red")) writeTableRow(row=rowString, bgcolor=rowCols, htmlFile=fileName) } writeEndTable(htmlFile=fileName) } else { ## skip 'deactivated' column if no function has been deactivated writeBeginTable(c("Name", "Test functions", "Errors", "Failures"), width="60%", htmlFile=fileName, columnWidth=c("30%", "30%", "20%", "20%")) for(tsName in names(testData)) { rowString <- c(paste("<a href=\"#", tsName, "\">", tsName, "</a>", sep=""), testData[[tsName]]$nTestFunc, testData[[tsName]]$nErr, testData[[tsName]]$nFail) rowCols <- c("", "", ifelse(testData[[tsName]]$nErr==0, "", "red"), ifelse(testData[[tsName]]$nFail==0, "", "red")) writeTableRow(row=rowString, bgcolor=rowCols, htmlFile=fileName) } writeEndTable(htmlFile=fileName) } writeHtmlSep(htmlFile=fileName) ## ------------------------------------------------ ## PART 3: ERROR, FAILURE AND DEACTIVATED TABLES ## ------------------------------------------------- ## error table if(separateFailureList && (errInfo$nErr > 0)) { writeHtmlSection("Errors", 3, htmlFile=fileName) writeBeginTable(c("Test suite : test function", "message"), htmlFile=fileName, columnWidth=c("30%", "70%")) for(tsName in names(testData)) { if(testData[[tsName]]$nErr > 0) { srcFileRes <- testData[[tsName]]$sourceFileResults srcFileNames <- names(srcFileRes) for(i in seq_along(srcFileRes)) { testFuncNames <- names(srcFileRes[[i]]) for(j in seq_along(testFuncNames)) { funcList <- srcFileRes[[i]][[testFuncNames[j]]] if(funcList$kind == "error") { lnk <- paste("<a href=\"", createTestFuncRef(tsName, srcFileNames[i], testFuncNames[j], asAnchor=TRUE), "\">", paste(tsName, testFuncNames[j], sep=" : "), "</a>", sep="") writeTableRow(row=c(lnk, funcList$msg), htmlFile=fileName) } } } } } writeEndTable(htmlFile=fileName) writeHtmlSep(htmlFile=fileName) } ## failure table if(separateFailureList && (errInfo$nFail > 0)) { writeHtmlSection("Failures", 3, htmlFile=fileName) writeBeginTable(c("Test suite : test function", "message"), htmlFile=fileName, columnWidth=c("30%", "70%")) for(tsName in names(testData)) { if(testData[[tsName]]$nFail > 0) { srcFileRes <- testData[[tsName]]$sourceFileResults srcFileNames <- names(srcFileRes) for(i in seq_along(srcFileRes)) { testFuncNames <- names(srcFileRes[[i]]) for(j in seq_along(testFuncNames)) { funcList <- srcFileRes[[i]][[testFuncNames[j]]] if(funcList$kind == "failure") { lnk <- paste("<a href=\"", createTestFuncRef(tsName, srcFileNames[i], testFuncNames[j], asAnchor=TRUE), "\">", paste(tsName, testFuncNames[j], sep=" : "), "</a>", sep="") writeTableRow(row=c(lnk, funcList$msg), htmlFile=fileName) } } } } } writeEndTable(htmlFile=fileName) writeHtmlSep(htmlFile=fileName) } ## deactivated table if(separateFailureList && (errInfo$nDeactivated > 0)) { writeHtmlSection("Deactivated", 3, htmlFile=fileName) writeBeginTable(c("Test suite : test function", "message"), htmlFile=fileName, columnWidth=c("30%", "70%")) for(tsName in names(testData)) { if(testData[[tsName]]$nDeactivated > 0) { srcFileRes <- testData[[tsName]]$sourceFileResults srcFileNames <- names(srcFileRes) for(i in seq_along(srcFileNames)) { testFuncNames <- names(srcFileRes[[i]]) for(j in seq_along(testFuncNames)) { funcList <- srcFileRes[[i]][[testFuncNames[j]]] if(funcList$kind == "deactivated") { lnk <- paste("<a href=\"", createTestFuncRef(tsName, srcFileNames[i], testFuncNames[j], asAnchor=TRUE), "\">", paste(tsName, testFuncNames[j], sep=" : "), "</a>", sep="") writeTableRow(row=c(lnk, funcList$msg), htmlFile=fileName) } } } } } writeEndTable(htmlFile=fileName) writeHtmlSep(htmlFile=fileName) } ## -------------------------------- ## PART 4: DETAILS ## -------------------------------- writeHtmlSection("Details", 3, htmlFile=fileName) ## loop over all test suites for(tsName in names(testData)) { tsList <- testData[[tsName]] writeBeginTag("p", htmlFile=fileName) writeBeginTag("a", para=paste("name=\"", tsName, "\"", sep=""), htmlFile=fileName) writeHtmlSection(paste("Test Suite:", tsName), 5, htmlFile=fileName) writeEndTag("a", htmlFile=fileName) pr("Test function regexp:", tsList$testFuncRegexp) pr("Test file regexp:", tsList$testFileRegexp) if(length(tsList$dirs) == 0) { pr("No directories !") } else { if(length(tsList$dirs) == 1) { pr("Involved directory:") } else { pr("Involved directories:") } for(dir in tsList$dirs) { pr(dir) } res <- tsList$sourceFileResults testFileNames <- names(res) if(length(res) == 0) { pr(" no test files") } else { ## loop over all source files writeBeginTag("ul", htmlFile=fileName) for(testFileName in testFileNames) { testFuncNames <- names(res[[testFileName]]) if(length(testFuncNames) > 0) { writeBeginTag("li", htmlFile=fileName) writeLink(target=testFileToLinkMap(testFileName), name=paste("Test file:", basename(testFileName)), htmlFile=fileName) ## loop over all test functions in the test file writeBeginTag("ul", htmlFile=fileName) for(testFuncName in testFuncNames) { writeBeginTag("li", htmlFile=fileName) testFuncInfo <- res[[testFileName]][[testFuncName]] anchorName <- createTestFuncRef(tsName, testFileName, testFuncName) writeBeginTag("a", para=paste("name=\"", anchorName, "\"", sep=""), htmlFile=fileName) if(testFuncInfo$kind == "success") { pr(paste(testFuncName, ": (",testFuncInfo$checkNum, " checks) ... OK (", testFuncInfo$time, " seconds)", sep="")) writeEndTag("a", htmlFile=fileName) } else { if(testFuncInfo$kind == "error") { writeBeginTag("u", para=paste("style", errorStyle, sep="="), htmlFile=fileName) writeRaw(paste(testFuncName, ": ERROR !! ", sep=""), htmlFile=fileName) writeEndTag("u", htmlFile=fileName) writeEndTag("a", htmlFile=fileName) } else if (testFuncInfo$kind == "failure") { writeBeginTag("u", para=paste("style", errorStyle, sep="="), htmlFile=fileName) writeRaw(paste(testFuncName, ": FAILURE !! (check number ", testFuncInfo$checkNum, ") ", sep=""), htmlFile=fileName) writeEndTag("u", htmlFile=fileName) writeEndTag("a", htmlFile=fileName) } else if (testFuncInfo$kind == "deactivated") { writeBeginTag("u", para=paste("style", deactivatedStyle, sep="="), htmlFile=fileName) writeRaw(paste(testFuncName, ": DEACTIVATED, ", sep=""), htmlFile=fileName) writeEndTag("a", htmlFile=fileName) } else { writeLi(paste(testFuncName, ": unknown error kind", sep="")) writeEndTag("a", htmlFile=fileName) } pr(testFuncInfo$msg) printTraceBack(testFuncInfo$traceBack) } writeEndTag("li", htmlFile=fileName) } writeEndTag("ul", htmlFile=fileName) } writeEndTag("li", htmlFile=fileName) } writeEndTag("ul", htmlFile=fileName) } } writeHtmlSep(htmlFile=fileName) } ver <- cbind(unlist(version)) ## add host name ver <- rbind(ver, Sys.info()["nodename"]) rownames(ver)[dim(ver)[1]] <- "host" colnames(ver) <- "Value" ## compiler used (under *nix) rhome <- Sys.getenv("R_HOME") ## on Windows Makeconf does not exist ## other than that we have no indication which compiler ## would be used for R CMD INSTALL so we report NA gccVersion <- as.character(NA) makeconfFile <- file.path(rhome, "etc", "Makeconf") if (file.exists(makeconfFile) && identical(.Platform$OS.type, "unix")) { gccVersion <- system(paste("cat ", makeconfFile," | grep \"^CXX =\" "), intern=TRUE) gccVersion <- sub("^CXX[ ]* =[ ]*", "", gccVersion) } ver <- rbind(ver, gccVersion) rownames(ver)[dim(ver)[1]] <- "compiler" writeHtmlTable(ver, htmlFile=fileName, border=0, width="80%", append=TRUE) ## finish html document writeHtmlEnd(htmlFile=fileName) return(invisible(TRUE)) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/R/testLogger.r��������������������������������������������������������������������������������0000644�0001762�0000144�00000025654�13267374743�014052� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ .newTestLogger <- function(useOwnErrorHandler) { ##@bdescr ## creates a new, empty TestLogger 'object'. ## TestLogger is an object based on the 'closure trick'. It has the task ## to store, administrate and print the test protocol. ##@edescr ##@in useOwnErrorHandler : [logical] ##@ret : [list] ## ##@codestatus : internal ## private data: ## ----------------------- .testData <- list() class(.testData) <- "RUnitTestData" .currentTestSuiteName <- NULL .currentSourceFileName <- NULL ## book keeping variables for individual test functions ## can be reset by function cleanup .currentTraceBack <- NULL .failure <- FALSE .deactivationMsg <- NULL ## if non-NULL test function is deactivated .checkNum <- 0 ## verbosity level: 0: silent .verbosity <- 1L ## define own error handler ## ----------------------- errorHandler <- function() { ##@bdescr ## used as default error handler during test case execution iff ## the user specified 'useOwnErrorHandler' as TRUE (default). ## called in case an error condition, typically stop() has been signalled. ## tries to create a traceback object, currently only used by addError(). ## ## not provided via testLogger but used by R's error handler. ##@edescr ## ##@ret : [NULL] used for it's side effect ## ##@codestatus : internal res <- try(dump.frames()) if (inherits(res, "try-error")) { .currentTraceBack <<- "traceback not available (dump.frames failed)." } else { .currentTraceBack <<- names(last.dump)[-length(last.dump)] } } if(useOwnErrorHandler) { options(error=errorHandler) } ## public methods: ## ----------------------- .getTestData <- function() { ##@bdescr ## return the protocol data collected during the test runs ##@edescr return(.testData) } .setCurrentTestSuite <- function(testSuite) { ##@bdescr ## record the test suite that is currently executed. ##@edescr ##@in testSuite : [testSuite - list] the current testSuite if(is.null(testSuite)) { .currentTestSuiteName <<- NULL } else { if(is.element(testSuite$name, names(.testData))) { stop(paste("Duplicate test suite:", testSuite$name)) } .currentTestSuiteName <<- testSuite$name .testData[[testSuite$name]] <<- list(nTestFunc = 0L, nDeactivated = 0L, nErr = 0, nFail = 0, dirs = testSuite[["dirs"]], testFileRegexp = testSuite[["testFileRegexp"]], testFuncRegexp = testSuite[["testFuncRegexp"]], sourceFileResults = list()) } } .setCurrentSourceFile <- function(sourceFileName) { ##@bdescr ## record the source file whose test functions are currently executed ##@edescr ##@in sourceFileName : [character] name of current source file if(is.null(sourceFileName)) { .currentSourceFileName <<- NULL } else { .currentSourceFileName <<- sourceFileName .testData[[.currentTestSuiteName]]$sourceFileResults[[sourceFileName]] <<- list() } } .addSuccess <- function(testFuncName, secs) { ##@bdescr ## add a successful test function run. ##@edescr ##@in testFuncName : [character] name of test function ##@in secs : [numeric] time in seconds needed by the test function to complete .testData[[.currentTestSuiteName]]$nTestFunc <<- 1 + .testData[[.currentTestSuiteName]]$nTestFunc .testData[[.currentTestSuiteName]]$sourceFileResults[[.currentSourceFileName]][[testFuncName]] <<- list(kind="success", checkNum=.checkNum, time=secs) } .addError <- function(testFuncName, errorMsg) { ##@bdescr ## add a test function that generated an error. ##@edescr ##@in testFuncName : [character] name of test function ##@in errorMsg : [character] the error message .testData[[.currentTestSuiteName]]$nTestFunc <<- 1 + .testData[[.currentTestSuiteName]]$nTestFunc .testData[[.currentTestSuiteName]]$nErr <<- 1 + .testData[[.currentTestSuiteName]]$nErr .testData[[.currentTestSuiteName]]$sourceFileResults[[.currentSourceFileName]][[testFuncName]] <<- list(kind="error", msg=errorMsg, checkNum=.checkNum, traceBack=.currentTraceBack) } .addFailure <- function(testFuncName, failureMsg) { ##@bdescr ## add a test function that generated an error. ##@edescr ##@in testFuncName : [character] name of test function ##@in failureMsg : [character] the failure message .testData[[.currentTestSuiteName]]$nTestFunc <<- 1 + .testData[[.currentTestSuiteName]]$nTestFunc .testData[[.currentTestSuiteName]]$nFail <<- 1 + .testData[[.currentTestSuiteName]]$nFail .testData[[.currentTestSuiteName]]$sourceFileResults[[.currentSourceFileName]][[testFuncName]] <<- list(kind="failure", msg=failureMsg, checkNum=.checkNum, traceBack=NULL) ## traceBack is useless in this case } .addDeactivated <- function(testFuncName) { ##@bdescr ## add a deactivated test function that generated an error. ##@edescr ##@in testFuncName : [character] name of test function .testData[[.currentTestSuiteName]]$nDeactivated <<- 1 + .testData[[.currentTestSuiteName]]$nDeactivated .testData[[.currentTestSuiteName]]$sourceFileResults[[.currentSourceFileName]][[testFuncName]] <<- list(kind="deactivated", msg=.deactivationMsg, checkNum=.checkNum) } .addCheckNum <- function(testFuncName) { ##@bdescr ## add total number of checks performed ##@edescr ##@in testFuncName : [character] name of test function .testData[[.currentTestSuiteName]]$sourceFileResults[[.currentSourceFileName]][[testFuncName]]$checkNum <<- .checkNum } .cleanup <- function() { ##@bdescr ## reset book keeping variables like .failure, ... ## should be called before each test function execution ##@edescr .currentTraceBack <<- NULL .failure <<- FALSE .deactivationMsg <<- NULL .checkNum <<- 0 } .isFailure <- function() { ##@bdescr ## return current failure status ##@edescr return(.failure) } .setFailure <- function() { ##@bdescr ## set failure status to TRUE ##@edescr .failure <<- TRUE } .isDeactivated <- function() { ##@bdescr ## return current deactivation message ##@edescr ##@ret : [logical] TRUE if deactivation msg is not NULL return(!is.null(.deactivationMsg)) } .setDeactivated <- function(msg) { ##@bdescr ## set deactivation message variable, indicating a deactivated test case ##@edescr ##@in msg : [character] message string if (length(msg) > 1) { msg <- paste(msg, collapse=" ") } .deactivationMsg <<- msg } .incrementCheckNum <- function() { ##@bdescr ## increment internal counter of total num of test cases ##@edescr .checkNum <<- 1 + .checkNum } .getCheckNum <- function() { ##@bdescr ## return counter value for total num of test cases ##@edescr return(.checkNum) } .getVerbosity <- function() { ##@bdescr ## return verbosity level for output log messages ##@edescr return(.verbosity) } .setVerbosity <- function(level) { ##@bdescr ## set verbosity level for output log messages ##@edescr ##@in level : [integer] 0: omit output log messages, 1 >= : write begin/end comments for each test case if (length(level) > 1) { level <- level[1] } .verbosity <<- level } tl <- list(getTestData = .getTestData, setCurrentTestSuite = .setCurrentTestSuite, setCurrentSourceFile = .setCurrentSourceFile, addSuccess = function(testFuncName, secs) .addSuccess(testFuncName, secs), addError = function(testFuncName, errorMsg) .addError(testFuncName, errorMsg), addFailure = function(testFuncName, failureMsg) .addFailure(testFuncName, failureMsg), addDeactivated = function(testFuncName) .addDeactivated(testFuncName), addCheckNum = function(testFuncName) .addCheckNum(testFuncName), isFailure = .isFailure, setFailure = .setFailure, isDeactivated = .isDeactivated, setDeactivated = function(msg) .setDeactivated(msg), incrementCheckNum = .incrementCheckNum, getCheckNum = .getCheckNum, getVerbosity = .getVerbosity, setVerbosity = .setVerbosity, cleanup = .cleanup) class(tl) <- "TestLogger" return(invisible(tl)) } getErrors <- function(testData) { ##@bdescr ## return a brief summary of the test case execution result, ## computed from the testData listOfListsOfLists ## ##@edescr ## ##@in testData : [list] S3 RUnitTestData class object ##@ret : [list] containing no of errors, deactivated, failed, and total test functions ## ##@codestatus : testing if(class(testData) != "RUnitTestData") { stop("getErrors needs an object of class 'RUnitTestData' as argument.") } ret <- list(nErr=0, nDeactivated=0, nFail=0, nTestFunc=0) for(i in seq_along(testData)) { ret$nErr <- ret$nErr + testData[[i]]$nErr ret$nDeactivated <- ret$nDeactivated + testData[[i]]$nDeactivated ret$nFail <- ret$nFail + testData[[i]]$nFail ret$nTestFunc <- ret$nTestFunc + testData[[i]]$nTestFunc } return(ret) } .existsTestLogger <- function(envir=RUnitEnv) { ##@bdescr ## Internal Function ## checks if .testLogger object is available in specified environment ## and if present if this object is of class 'TestLogger' ## ##@edescr ## ##@in envir : [environment] to search within ##@ret : [logical] TRUE iff .testLogger list object is found in specified environment ## ##@codestatus : internal exists(".testLogger", envir=envir) && inherits(get(".testLogger", envir=envir), "TestLogger") } ������������������������������������������������������������������������������������RUnit/R/exportHTML.r��������������������������������������������������������������������������������0000644�0001762�0000144�00000023117�13267374743�013731� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2010 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ plotConnection.trackInfo <- function(con, pngfile, ...) { ##@bdescr ## create a plot displaying the execution flow as a graph ##@edescr ## ##@in con : [matrix] counts of execution calls for previous functions ##@in pngfile : [character] string specifying the full path & file name of the ## plot file (PNG) to be generate ##@ret : [NULL] used for its side effect ## ##@codestatus : testing #stopifnot(require(graphics)) ## experimental 2nd order connections ## color for arrows color <- c("black","lightgreen","green","lightblue","blue","orangered","red") ## create nothing if if(all(con==0)) { ## open png device grDevices::png(filename=pngfile,width=1024,height=960) graphics::plot(1:10,axes=FALSE,xlab="",ylab="",main="",type="n") graphics::text(5,5,labels="No connection graph available") grDevices::dev.off() return(invisible()) } ## overall connections allCon <- sum(con) ## connections with percent con <- ceiling(con/sum(con)*100) ## normalize for colors con <- (con + 14) %/% 15 ## open png device grDevices::png(filename=pngfile,width=1024,height=960) ## basic plot plot(x=1:nrow(con), y=1:nrow(con), type="n",axes=FALSE,ylab="# line",xlab="", ylim=c(nrow(con),1)) ## draw text lines text(x=1, y=1:nrow(con), labels=1:nrow(con)) ## offset, to avoid complete overlay offset <- rep(3,length.out=nrow(con)) ## minimal x xmin <- 2 ## check all connections for(i in 1:nrow(con)) { for(j in 1:ncol(con)) { ## check for an existing connection if(con[i,j] != 0) { colDraw <- color[con[i,j]] from <- j to <- i ## circular if(from == to) { top <- from + 0.5 bot <- from - 0.5 middle <- (xmin+offset[from])/2 ## top spline splTop <- stats::spline(c(xmin,middle,offset[from]), c(from + 0.2,top,from)) ## bottom spline splBot <- stats::spline(c(xmin,middle,offset[from]), c(from - 0.2,bot,from)) lines(splTop$x, splTop$y, col=colDraw) lines(splBot$x, splBot$y, col=colDraw) l <- length(splTop$y) ## draw arrow tips arrows(splTop$x[l-1], splTop$y[l-1], splTop$x[l], splTop$y[l], length=0.04, col=colDraw) offset[from] <- offset[from] + 1 } else { ## "regular" case middle <- (i+j)/2; splxy <- stats::spline(c(from - 0.2, middle, to + 0.2), c(xmin - 0.2, offset[from], xmin + 0.2)) lines(splxy$y, splxy$x, col=colDraw) if(i < j) { l <- length(splxy$y) ## draw an arrow tip arrows(splxy$y[l-1], splxy$x[l-1], splxy$y[l], splxy$x[l], length=0.06, col=colDraw) } else { ## draw "inverse" arrow tip arrows(splxy$y[2], splxy$x[2], splxy$y[1], splxy$x[1], length=0.06, col=colDraw) } ## set offset higher offset[from] <- offset[from] + 1 } } } } legposx <- nrow(con) leg.txt <- c("0-15%","15-30%","30-45%","45-60%","60-75%","75-90%","90-100%") legend(x=legposx,y=1,legend=leg.txt,lty=1,xjust=1,col=color) grDevices::dev.off() return(invisible()) } printHTML <- function(object, ...) UseMethod("printHTML") printHTML.default <- function(object, ...) NextMethod("printHTML") printHTML.trackInfo <- function(object, baseDir=".") { ##@bdescr ## create a HTML representation of the TrackInfo object data ##@edescr ## ##@in object : [list] trackInfo object ##@in baseDir : [character] string specifying the full path to the root directory to hold the HTML pages ## ## ##@codestatus : untested ## preconditions if (!is(object, "trackInfo")) { stop("argument 'object' has to be a list of class 'trackInfo'.") } if (!is.character(baseDir)) { stop("argument 'baseDir' has to be of type 'character'.") } if (length(baseDir) != 1) { stop("argument 'baseDir' has to contain exactly one element.") } if (is.na(baseDir)) { stop("argument 'baseDir' may not be missing value.") } path <- file.path(baseDir,"results") if (!file.exists(path)) { ok <- dir.create(path) if(!ok) { stop(paste("could not create", path) ) } } htmlFile <- file.path(path,"index.html") footerString <- paste("RUnit ", packageDescription("RUnit", fields="Version"), as.character(Sys.time())) ## create index.html writeHtmlHeader("RUnit Code Inspection - Overview",htmlFile) writeHtmlSection("Overview",2,htmlFile) writeBeginTable(c("Categ.","Name","Signature"),htmlFile) for(i in seq_along(object)) { funcID <- strsplit(names(object)[i],"/")[[1]] funcCat <- funcID[1] funcName <- funcID[2] if(length(funcID) > 2) { sig <- funcID[3:length(funcID)] funcSig <- paste(funcName,"(",paste(sig,collapse=", "),")",sep="") } else { funcSig <- "" } writeBeginTag("tr",htmlFile) writeCR(htmlFile) ## write function category writeBeginTag("td",htmlFile) writeRaw(funcCat,htmlFile) writeEndTag("td",htmlFile) writeCR(htmlFile) ## write function name writeBeginTag("td",htmlFile) writeLink(file.path(".", paste("result",i,".html",sep="")), funcName, htmlFile) writeEndTag("td",htmlFile) writeCR(htmlFile) ## write function signature writeBeginTag("td",htmlFile) writeRaw(funcSig,htmlFile) writeEndTag("td",htmlFile) writeCR(htmlFile) writeEndTag("tr",htmlFile) } writeEndTable(htmlFile) writeRaw(footerString, htmlFile) writeHtmlEnd(htmlFile) writeLinkRef <- function(htmlFile,leftLink,leftName,rightLink,rightName) { writeBeginTable(c("",""),htmlFile,border=0,width="100%") writeBeginTag("tr",htmlFile) writeCR(htmlFile) writeBeginTag("td",htmlFile) writeLink(leftLink,leftName,htmlFile) writeEndTag("td",htmlFile) writeCR(htmlFile) writeBeginTag("td",htmlFile,"align=\"right\""); writeLink(rightLink,rightName,htmlFile) writeEndTag("td",htmlFile) writeEndTag("tr",htmlFile) writeCR(htmlFile) writeEndTable(htmlFile) } ## create result pages for(i in seq_along(object)) { absGraphImg <- file.path(path, paste("con",i,".png",sep="")) absGraphFile <- file.path(path, paste("con",i,".html",sep="")) relGraphImg <- file.path(".", paste("con",i,".png",sep="")) relGraphFile <- file.path(".", paste("con",i,".html",sep="")) relHTMLFile <- file.path(".", paste("result",i,".html",sep="")) htmlFile <- file.path(path, paste("result",i,".html",sep="")) ## begin result page writeHtmlHeader("RUnit Code Inspection - Result",htmlFile) writeLinkRef(htmlFile,"index.html","index",relGraphFile,"graph") writeHtmlSep(htmlFile) writeHtmlSection("Result",2,htmlFile) funcName <- strsplit(names(object)[i],"/")[[1]][2] writeRaw("Function: ",htmlFile) writeBeginTag("b",htmlFile) writeRaw(funcName,htmlFile) writeEndTag("b",htmlFile) writeCR(htmlFile) writeRaw("Runs: ",htmlFile) writeBeginTag("b",htmlFile) writeRaw(object[[i]]$nrRuns,htmlFile) writeEndTag("b",htmlFile) writeCR(htmlFile) writeCR(htmlFile) writeBeginTable(c("line","code","calls","time"),htmlFile) for(j in seq_along(object[[i]]$src)) { srcLine <- object[[i]]$src[j] leadingSpaceNr <- attr(regexpr("^( )*",srcLine),"match.length") if(leadingSpaceNr > 0) { srcLine <- gsub("^( )*","",srcLine) srcLine <- paste(paste(rep(" ",leadingSpaceNr),collapse=""), srcLine,collapse="",sep="") } if(object[[i]]$run[j] > 0) { bgcolor <- "#00D000" } else { bgcolor <- "#D00000" } writeTableRow(c(j,srcLine,object[[i]]$run[j],round(object[[i]]$time[j],2)), htmlFile,bgcolor=bgcolor) } writeEndTable(htmlFile) writeHtmlSep(htmlFile) writeLinkRef(htmlFile,"index.html","index",relGraphFile,"graph") writeHtmlSep(htmlFile) writeRaw(footerString, htmlFile) writeHtmlEnd(htmlFile) ## Conncetion plot plotConnection.trackInfo(object[[i]]$graph, absGraphImg) writeHtmlHeader("RUnit Code Inspection - Connection Graph",absGraphFile) writeLinkRef(absGraphFile,"index.html","index",relHTMLFile,"Function") writeHtmlSep(absGraphFile) writeHtmlSection("Connection Graph",2,absGraphFile) writeImage(relGraphImg,absGraphFile) writeCR(absGraphFile) writeHtmlSep(absGraphFile) writeLinkRef(absGraphFile,"index.html","index",relHTMLFile,"Function") writeRaw(footerString, absGraphFile) writeHtmlEnd(absGraphFile) } return(invisible()) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/R/runit.r�������������������������������������������������������������������������������������0000644�0001762�0000144�00000036427�13274053750�013062� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ defineTestSuite <- function(name, dirs, testFileRegexp="^runit.+\\.[rR]$", testFuncRegexp="^test.+", rngKind="Marsaglia-Multicarry", rngNormalKind="Kinderman-Ramage") { ##@bdescr ## Convenience functions to handle test suites ##@edescr ## ##@in name : [character] test suite title used in protocol ##@in dirs : [character] vector of paths to search for test case files ##@in testFileRegexp : [character] regular expression string to match file names ##@in testFuncRegexp : [character] (vector) regular expression string(s) to match test case functions within all test case files ##@in rngKind : [character] name of the RNG version, see RNGversion() ##@in rngNormalKind : [character] name of the RNG version for the rnorm, see RNGversion() ##@ret : [RUnitTestSuite] S3 class (list) object, ready for test runner ## ##@codestatus : testing if (missing(dirs)) { stop("argument 'dirs' is missing without a default.") } if (missing(name)) { warning("argument 'name' is missing. using basename(dirs)[1] instead.") name <- basename(dirs)[1] } ret <- list(name=name, dirs=dirs, testFileRegexp=testFileRegexp, testFuncRegexp=testFuncRegexp, rngKind=rngKind, rngNormalKind=rngNormalKind) class(ret) <- "RUnitTestSuite" return(invisible(ret)) } isValidTestSuite <- function(testSuite) { ##@bdescr ## Helper function ## checks 'RUnitTestSuite' class object features ##@edescr ## ##@in testSuite : [RUnitTestSuite] S3 class (list) object, input object for test runner ##@ret : [logical] TRUE if testSuite is valid ## ##@codestatus : testing if(!is(testSuite, "RUnitTestSuite")) { warning(paste("'testSuite' object is not of class 'RUnitTestSuite'.")) return(FALSE) } ## check required elements, irrespective of order, allow for additional elements requiredNames <- c("name", "dirs", "testFileRegexp", "testFuncRegexp", "rngKind", "rngNormalKind") if(!all(requiredNames %in% names(testSuite))) { warning("'testSuite' object does not conform to S3 class definition. Not all list elements present.") return(FALSE) } for(i in seq_along(testSuite)) { if(!is.character(testSuite[[i]])) { warning(paste("'testSuite' object does not conform to S3 class definition.\n", "'", names(testSuite)[i],"' element has to be of type 'character'.", sep="")) return(FALSE) } if(any(testSuite[[i]] == "")) { warning(paste("'testSuite' object does not conform to S3 class definition.\n", "'",names(testSuite)[i],"' element may not contain empty string.", sep="")) return(FALSE) } } notFound <- !file.exists(testSuite[["dirs"]]) if (any(notFound)) { warning(paste("specified directory", paste(testSuite[["dirs"]][notFound], collapse=", "), "not found.")) return(FALSE) } if (length(testSuite[["name"]]) != 1) { warning(paste("'name' element may only contain exactly one name.")) return(FALSE) } if (length(testSuite[["testFileRegexp"]]) != 1) { warning(paste("'testFileRegexp' element may only contain exactly one string.")) return(FALSE) } if (length(testSuite[["testFuncRegexp"]]) != 1) { warning(paste("'testFuncRegexp' element may only contain exactly one string.")) return(FALSE) } ## RNGkind has an internal list of valid names which cannot be accessed ## programmatically. Furthermore, users can define their own RNG and select that one ## so we have to leave it to RNGkind() to check if the arguments are valid. if (length(testSuite[["rngKind"]]) != 1) { warning(paste("'rngKind' element may only contain exactly one name.")) return(FALSE) } if (length(testSuite[["rngNormalKind"]]) != 1) { warning(paste("'rngNormalKind' element may only contain exactly one name.")) return(FALSE) } return(TRUE) } .setUp <- function() { ##@bdescr ## Internal Function. ## Default function to be executed once for each test case before the test case gets executed. ## This function can be adopted to specific package requirements for a given project. ## Need to replace this default with a new function definition. ## Function cannot take arguments and does not have a return value. ##@edescr ## ##@codestatus : internal return(invisible()) } .tearDown <- function() { ##@bdescr ## Internal Function. ## Default function to be executed once for each test case after the test case got executed. ## This function can be adopted to specific package requirements for a given project. ## Need to replace this default with a new function definition. ## Function cannot take arguments and does not have a return value. ##@edescr ## ##@codestatus : internal return(invisible()) } .executeTestCase <- function(funcName, envir, setUpFunc, tearDownFunc) { ##@bdescr ## Internal Function. ## Execute individual test case, record logs and change state of global TestLogger object. ##@edescr ## ##@in funcName : [character] name of test case function ##@in envir : [environment] ##@in setUpFunc : [function] ##@in tearDownFunc : [function] ##@ret : [NULL] ## ##@codestatus : internal ## write to stdout for logging func <- get(funcName, envir=envir) ## anything else than a function is ignored. if(mode(func) != "function") { return(invisible()) } if (RUnitEnv$.testLogger$getVerbosity() > 0) { cat("\n\nExecuting test function", funcName, " ... ") } ## safe execution of setup function res <- try(setUpFunc()) if (inherits(res, "try-error")) { message <- paste("Error executing .setUp before",funcName, ":", geterrmessage()) RUnitEnv$.testLogger$addError(testFuncName=paste(".setUp (before ", funcName, ")", sep=""), errorMsg=message) return(invisible()) } ## reset book keeping variables in RUnitEnv$.testLogger RUnitEnv$.testLogger$cleanup() ## ordinary test function execution: timing <- try(system.time(func(), gcFirst=RUnitEnv$.gcBeforeTest)) if (inherits(timing, "try-error")) { if(RUnitEnv$.testLogger$isFailure()) { RUnitEnv$.testLogger$addFailure(testFuncName=funcName, failureMsg=geterrmessage()) } else if(RUnitEnv$.testLogger$isDeactivated()) { RUnitEnv$.testLogger$addDeactivated(testFuncName=funcName) } else { RUnitEnv$.testLogger$addError(testFuncName=funcName, errorMsg=geterrmessage()) } } else { RUnitEnv$.testLogger$addSuccess(testFuncName=funcName, secs=round(timing[3], 2)) } ## add number of check function calls within test case RUnitEnv$.testLogger$addCheckNum(testFuncName=funcName) ## safe execution of tearDown function res <- try(tearDownFunc()) if (inherits(res, "try-error")) { message <- paste("Error executing .tearDown after",funcName, ":", geterrmessage()) RUnitEnv$.testLogger$addError(testFuncName=paste(".tearDown (after ", funcName, ")", sep=""), errorMsg=message) return(invisible()) } if (RUnitEnv$.testLogger$getVerbosity() > 0) { cat(" done successfully.\n\n") } return(invisible()) } .sourceTestFile <- function(absTestFileName, testFuncRegexp) { ##@bdescr ## This function sources a file, finds all the test functions in it, executes them ## and reports the results to the TestLogger. ## No return value, called for its side effects on TestLogger object ##@edescr ## ##@in absTestFileName : [character] absolute path name of the file to test ##@in testFuncRegexp : [character] a regular expression identifying the names of test functions ##@ret : [NULL] ## ##@codestatus : internal RUnitEnv$.testLogger$setCurrentSourceFile(absTestFileName) if (!file.exists(absTestFileName)) { msgText <- paste("Test case file ", absTestFileName," not found.") RUnitEnv$.testLogger$addError(testFuncName=absTestFileName, errorMsg=msgText) return(invisible()) } sandbox <- new.env(parent=.GlobalEnv) ## will be destroyed after function closure is left ## catch syntax errors in test case file res <- try(sys.source(absTestFileName, envir=sandbox)) if (inherits(res, "try-error")) { message <- paste("Error while sourcing ",absTestFileName,":",geterrmessage()) RUnitEnv$.testLogger$addError(testFuncName=absTestFileName, errorMsg=message) return(invisible()) } ## test file provides definition of .setUp/.tearDown if (exists(".setUp", envir=sandbox, inherits=FALSE)) { .setUp <- get(".setUp", envir=sandbox) } if (exists(".tearDown", envir=sandbox, inherits=FALSE)) { .tearDown <- get(".tearDown", envir=sandbox) } testFunctions <- ls(pattern=testFuncRegexp, envir=sandbox) for (funcName in testFunctions) { .executeTestCase(funcName, envir=sandbox, setUpFunc=.setUp, tearDownFunc=.tearDown) } } runTestSuite <- function(testSuites, useOwnErrorHandler=TRUE, verbose=getOption("RUnit")$verbose, gcBeforeTest=FALSE) { ##@bdescr ## This is the main function of the RUnit framework. It identifies all specified ## test files and triggers all required actions. At the end it creates a test ## protocol data object. ## IMPORTANT to note, the random number generator is (re-)set to the default ## methods specified in defineTestSuite() before each new test case *file* is sourced. ## This guarantees that each new test case set defined together in on file can rely ## on the default, even if the random number generator version is being reconfigured in some ## previous test case file(s). ##@edescr ## ##@in testSuites : [list] list of test suite lists ##@in useOwnErrorHandler : [logical] TRUE (default) : use the RUnit error handler ##@in verbose : [integer] >= 1: (default) write begin/end comments for each test case, 0: omit begin/end comment ##@in gcBeforeTest : [logical] FALSE (default) : garbage collect before timing each test ##@ret : [list] 'RUnitTestData' S3 class object ## ##@codestatus : testing ## preconditions if (!is.logical(useOwnErrorHandler)) { stop("argument 'useOwnErrorHandler' has to be of type logical.") } if (length(useOwnErrorHandler) != 1) { stop("argument 'useOwnErrorHandler' has to be of length 1.") } if (is.na(useOwnErrorHandler)) { stop("argument 'useOwnErrorHandler' may not contain NA.") } if (!is.logical(gcBeforeTest)) { stop("argument 'gcBeforeTest' has to be of type logical.") } if (length(gcBeforeTest) != 1) { stop("argument 'gcBeforeTest' has to be of length 1.") } if (is.na(gcBeforeTest)) { stop("argument 'gcBeforeTest' may not contain NA.") } oFile <- getOption("RUnit")$outfile if (!is.null(oFile)) { if(is.character(oFile)) { ## connection has to be open when handed on to sink oFile <- file(oFile, "w") } else if(!inherits(oFile, "connection")) { stop("'outfile' must be a connection or a character string.") } sink(file=oFile) sink(file=oFile, type="message") resetStream <- function() { sink(type="message") sink() flush(oFile) close(oFile) ##close(oFile) } on.exit(resetStream()) } ## record RNGkind and reinstantiate on exit rngDefault <- RNGkind() on.exit(RNGkind(kind=rngDefault[1], normal.kind=rngDefault[2]), add=TRUE) oldErrorHandler <- getOption("error") ## reinstall error handler on.exit(options(error=oldErrorHandler), add=TRUE) ## initialize TestLogger assign(".testLogger", .newTestLogger(useOwnErrorHandler), envir=RUnitEnv) RUnitEnv$.testLogger$setVerbosity(verbose) ## store the information about GC before test assign(".gcBeforeTest", gcBeforeTest, envir=RUnitEnv) ## main loop if (isValidTestSuite(testSuites)) { testSuites <- list(testSuites) } else if (isValidTestSuite(testSuites[[1]])) { ## do nothing } else { stop("invalid test suite supplied.") } for (i in seq_along(testSuites)) { testSuite <- testSuites[[i]] if(!isValidTestSuite(testSuite)) { errMsg <- paste("Invalid test suite",testSuite$name,". Test run aborted.") stop(errMsg) } RUnitEnv$.testLogger$setCurrentTestSuite(testSuite) testFiles <- list.files(testSuite$dirs, pattern = testSuite$testFileRegexp, full.names=TRUE) for(testFile in testFiles) { ## set a standard random number generator. RNGkind(kind=testSuite$rngKind, normal.kind=testSuite$rngNormalKind) .sourceTestFile(testFile, testSuite$testFuncRegexp) } } ret <- RUnitEnv$.testLogger$getTestData() return(ret) } runTestFile <- function(absFileName, useOwnErrorHandler=TRUE, testFuncRegexp="^test.+", rngKind="Marsaglia-Multicarry", rngNormalKind="Kinderman-Ramage", verbose=getOption("RUnit")$verbose, gcBeforeTest=FALSE) { ##@bdescr ## Convenience function. ##@edescr ## ##@in absFileName : [character] complete file name of test cases code file ##@in useOwnErrorHandler : [logical] if TRUE RUnits error handler will be used ##@in testFuncRegexp : [character] ##@in rngKind : [character] name of the RNG, see RNGkind for avialbale options ##@in rngNormalKind : [character] name of the RNG for rnorm, see RNGkind for avialbale options ##@in verbose : [integer] >= 1: (default) write begin/end comments for each test case, 0: ommit begin/end comment (passed on to function runTestSuite) ##@in gcBeforeTest : [logical] FALSE (default) : garbage collect before timing each test ##@ret : [list] 'RUnitTestData' S3 class object ## ##@codestatus : testing ## preconditions ## all error checking and handling is delegated to function runTestSuite fn <- basename(absFileName) nn <- strsplit(fn, "\\.")[[1]][1] dn <- dirname(absFileName) ts <- defineTestSuite(name=nn, dirs=dn, testFileRegexp=paste("^", fn, "$", sep=""), testFuncRegexp=testFuncRegexp, rngKind=rngKind, rngNormalKind=rngNormalKind) return(runTestSuite(ts, useOwnErrorHandler=useOwnErrorHandler, verbose=verbose, gcBeforeTest=gcBeforeTest)) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/R/options.r�����������������������������������������������������������������������������������0000644�0001762�0000144�00000002741�13267374743�013416� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������## RUnit : A unit test framework for the R programming language ## Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; version 2 of the License. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## $Id$ .buildRUnitOptions <- function() { ##@bdescr ## Internal function ## adds an entry to R's default global option list ## modelled after version in package Biobase (BioC) ##@edescr ## ##@ret : [list] extended options() list ## ##@codestatus : internal RUnit <- getOption("RUnit") if (is.null(RUnit)) { RUnit <- list() class(RUnit) <- "RUnitOptions" } if (is.null( RUnit$verbose)) { ## integer: == 0: silent, >= 1: add comments to test case log RUnit$verbose <- 1L } if (is.null(RUnit$silent)) { RUnit$silent <- FALSE } if (is.null(RUnit$outfile)) { RUnit$outfile <- NULL } options("RUnit"=RUnit) } �������������������������������RUnit/vignettes/������������������������������������������������������������������������������������0000755�0001762�0000144�00000000000�13277574246�013344� 5����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/vignettes/RUnit.Rnw���������������������������������������������������������������������������0000644�0001762�0000144�00000050411�13277574216�015073� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% -*- mode: noweb; noweb-default-code-mode: R-mode; -*- % % $Id: RUnit.Rnw,v 1.22 2009/11/25 15:12:11 burgerm Exp $ % % %\VignetteIndexEntry{RUnit primer} %\VignetteKeywords{Unit Testing, Code Inspection, Programming} %\VignetteDepends{methods, splines} %\VignettePackage{RUnit} \documentclass[12pt, a4paper]{article} %\usepackage{amsmath,pstricks} \usepackage{hyperref} %\usepackage[authoryear,round]{natbib} %\parskip=.3cm \oddsidemargin=.1in \evensidemargin=.1in \headheight=-.3in \newcommand{\scscst}{\scriptscriptstyle} \newcommand{\scst}{\scriptstyle} \newcommand{\Rfunction}[1]{{\texttt{#1}}} \newcommand{\Robject}[1]{{\texttt{#1}}} \newcommand{\Rpackage}[1]{{\textit{#1}}} %\makeindex % \begin{document} \title{RUnit - A Unit Test Framework for R} \author{Thomas K\"onig, Klaus J\"unemann, and Matthias Burger\\Epigenomics AG} \maketitle \tableofcontents \section*{Abstract} \label{section:abstract} Software development for production systems presents a challenge to the development team as the quality of the coded package(s) has to be constantly monitored and verified. We present a generic approach to software testing for the R language modelled after successful examples such as JUnit, CppUnit, and PerlUnit. The aim of our approach is to facilitate development of reliable software packages and provide a set of tools to analyse and report the software quality status. The presented framework is completely implemented within R and does not rely on external tools or other language systems. The basic principle is that every function or method is accompanied with a test case that queries many calling situations including incorrect invocations. A test case can be executed instantly without reinstalling the whole package - a feature that is necessary for parallel development of functionality and test cases. On a second level one or more packages can be tested in a single test run, the result of which is reported in an well structured test protocol. To verify the coverage of the test framework a code inspector is provided that monitors the code coverage of executed test cases. The result of individual test invocations as well as package wide evaluations can be compiled into a summary report exported to HTML. This report details the executed tests, their failure or success, as well as the code coverage. Taking it one step further and combining the build system with a development and release procedure with defined code status description this approach opens the way for a principled software quality monitoring and risk assessment of the developed application. For our code development we have utilised the described system with great benefit w.r.t.\ code reliability and maintenance efforts in a medium sized development team. \section{Introduction} The importance of software testing can hardly be overrated. This is all the more true for interpreted languages where not even a compiler checks the basic consistency of a program. Nonetheless, testing is often perceived more as a burden than a help by the programmer. Therefore it is necessary to provide tools that make the task of testing as simple and systematic as possible. The key goal of such a testing framework should be to promote the creation and execution of test cases to become an integral part of the software development process. Experience shows that such a permanently repeated code - test - simplify cycle leads to faster and more successful software development than the usually futile attempt to add test cases once the software is largely finished. This line of thought has been pushed furthest by the Extreme Programming \cite{xp} and Test-First paradigms where test cases are viewed as the essential guidelines for the development process. These considerations lead to various requirements that a useful testing framework should satisfy: \begin {itemize} \item {Tests should be easy to execute.} \item {The results should be accessible through a well structured test protocol.} \item{It should be possible to execute only small portions of the test cases during the development process.} \item{It should be possible to estimate the amount of code that is covered by some test case.} \end {itemize} %\paragraph{Background} %\label{paragraph:Background} Testing frameworks that address these aspects have been written in a variety of languages such as Smalltalk, Java, C++ and Python. In particular, the approach described in \cite{beck} has turned out to be very successful, leading -- among others -- to the popular JUnit library for Java \cite{junit}, which has been ported to many other languages (see \cite{xp} for an extensive list of testing frameworks for all kinds of languages). Accordingly, the RUnit package (available at sourceforge \cite{runit-sf}) is our version of porting JUnit to R, supplemented by additional functionality to inspect the test coverage of some function under question. %\paragraph{Motivation} %\label{paragraph:Motivation} One may wonder why R would need yet another testing framework even though the standard method, namely executing {\it R CMD check} on ones complete package at the shell prompt, is widely accepted and applied. We think, however, that the RUnit approach is more in line with the above listed requirements and can be seen as a complement to the existing process in that: \begin{itemize} \item{test cases are called and executed from the R prompt} \item{the programmer decides which result or functionality to put under testing, e.g.\ formating issues of textual output do not need to matter} \item{test and reference data files need not be maintained separately but are combined into one file} \item{test cases need not be limited to testing/using functionality from one package checked at a time} \end{itemize} Moreover, testing frameworks based on JUnit ports seem to have become a quasi standard in many programming languages. Therefore, programmers new to R but familiar with other languages might appreciate a familiar testing environment. And finally, offering more than one alternative in the important field of code testing is certainly not a bad idea and could turn out useful. Before explaining the components of the RUnit package in detail, we would like to list some of the lessons learned in the attempt of writing useful test suites for our software (a more complete collection of tips relating to a Test-First development approach can be found in \cite{tfg}): \begin{itemize} \item {Develop test cases parallel to implementing your functionality. Keep testing all the time (code - test - simplify cycle). Do not wait until the software is complete and attempt to add test cases at the very end. This typically leads to poor quality and incomplete test cases.} \item{Distinguish between unit and integration tests: Unit tests should be as small as possible and check one unit of functionality that cannot be further decomposed. Integration tests, on the other hand, run through a whole analysis workflow and check the interplay of various software components.} \item{Good test coverage enables refactoring, by which a reorganisation of the implementation is meant. Without regular testing the attitude {\it `I better do not touch this code anymore`} once some piece of software appears to be working is frequently encountered. It is very pleasing and time-saving just to run a test suite after some improvement or simplification of the implementation to see that all test cases are still passing (or possibly reveal some newly introduced bug). This refactoring ability is a key benefit of unit testing leading not only to better software quality but also to better design.} \item{Do not test internal functions but just the public interface of a library. Since R does not provide very much language support for this distinction, the first step here is to clarify which functions are meant to be called by a user of a package and which are not (namespaces in R provide a useful directive for making this distinction, if the export list is selected carefully and maintained). If internal functions are directly tested, the ability of refactoring gets lost because this typically involves reorganisation of the internal part of a library.} \item {Once a bug has been found, add a corresponding test case.} \item{We greatly benefitted from an automated test system: A shell script, running nightly, checks out and installs all relevant packages. After that all test suites are run and the resulting test protocol is stored in a central location. This provides an excellent overview over the current status of the system and the collection of nightly test protocols documents the development progress.} \end{itemize} \section{The RUnit package} \label{section:RUnitPackage} This section contains a detailed explanation of the RUnit package and examples how to use it. As has already been mentioned the package contains two independent components: a framework for test case execution and a tool that allows to inspect the flow of execution inside a function in order to analyse which portions of code are covered by some test case. Both components are now discussed in turn. \subsection{Test case execution} \label{subsection:Testcaseexecution} The basic idea of this component is to execute a set of test functions defined through naming conventions, store whether or not the test succeeded in a central logger object and finally write a test protocol that allows to precisely identify the problems. {\bf Note, that RUnit - by default - sets the version for normal, and all other RNGs to 'Kinderman-Ramage', and 'Marsaglia-Multicarry', respectively. If you like to change these defaults please see {\tt ?defineTestSuite} for argument 'rngNormalKind' and 'rngKind'.} As an example consider a function that converts centigrade to Fahrenheit: \begin{Sinput} c2f <- function(c) return(9/5 * c + 32) \end{Sinput} A corresponding test function could look like this: \begin{Sinput} test.c2f <- function() { checkEquals(c2f(0), 32) checkEquals(c2f(10), 50) checkException(c2f("xx")) } \end{Sinput} The default naming convention for test functions in the RUnit package is {\tt test...} as is standard in JUnit. To perform the actual checks that the function to be tested works correctly a set of functions called {\tt check ...} is provided. The purpose of these {\tt check} functions is two-fold: they make sure that a possible failure is reported to the central test logger so that it will appear properly in the final test protocol and they are supposed to make explicit the actual checks in a test case as opposed to other code used to set up the test scenario. Note that {\tt checkException} fails if the passed expression does not generate an error. This kind of test is useful to make sure that a function correctly recognises error situations instead of silently creating inappropriate results. These check functions are direct equivalents to the various {\tt assert} functions of the JUnit framework. More information can be found in the online help. Before running the test function it is necessary to create a test suite which is a collection of test functions and files relating to one topic. One could, for instance, create one test suite for one R package. A test suite is just a list containing a name, an array of absolute directories containing the locations of the test files, a regular expression identifying the test files and a regular expression identifying the test functions. In our example assume that the test function is located in a file {\tt runitc2f.r} located in a directory {\tt /foo/bar/}. To create the corresponding test suite we can use a helper function: \begin{Sinput} testsuite.c2f <- defineTestSuite("c2f", dirs = file.path(.path.package(package="RUnit"), "examples"), testFileRegexp = "^runit.+\\.r", testFuncRegexp = "^test.+", rngKind = "Marsaglia-Multicarry", rngNormalKind = "Kinderman-Ramage") \end{Sinput} All that remains is to run the test suite and print the test protocol: \begin{Sinput} testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult) \end{Sinput} The resulting test protocol should be self explanatory and can also be printed as HTML version. See the online help for further information. Note that for executing just one test file there is also a shortcut in order to make test case execution as easy as possible: \begin{Sinput} runTestFile(file.path(.path.package(package="RUnit"), "examples/runitc2f.r")) \end{Sinput} The creation and execution of test suites can be summarised by the following recipe: \begin{enumerate} \item{create as many test functions in as many test files as necessary } \item{create one or more test suites using the helper function {\tt defineTestSuite}} \item{run the test suites with {\tt runTestSuite}} \item{print the test protocol either with {\tt printTextProtocol} or with {\tt printHTMLProtocol} (or with a generic method like {\tt print} or {\tt summary})} \end{enumerate} We conclude this section with some further comments on various aspects of the test execution framework: \begin{itemize} \item{A test file can contain an arbitrary number of test functions. A test directory can contain an arbitrary number of test files, a test suite can contain an arbitrary number of test directories and the test runner can run an arbitrary number of test suites -- all resulting in one test protocol. The test function and file names of a test suite must, however, obey a naming convention expressible through regular expressions. As default test functions start with {\tt test} and files with {\tt runit}.} \item{RUnit makes a distinction between failure and error. A failure occurs if one of the check functions fail (e.g.~{\tt checkTrue(FALSE)} creates a failure). An error is reported if an ordinary R error (usually created by {\tt stop}) occurs.} \item{Since version 0.4.0 there is a function {\tt DEACTIVATED} which can be used to deactivate test cases temporarily. This might be useful in the case of a major refactoring. In particular, the deactivated test cases are reported in the test protocol so that they cannot fall into oblivion.} \item{The test runner tries hard to leave a clean R session behind. Therefore all objects created during test case execution will be deleted after a test file has been processed.} \item{In order to prevent mysterious errors the random number generator is reset to a standard setting before sourcing a test file. If a particular setting is needed to generate reproducible results it is fine to configure the random number generator at the beginning of a test file. This setting applies during the execution of all test functions of that test file but is reset before the next test file is sourced.} \item{In each source file one can define the parameterless functions {\tt .setUp()} and {\tt .tearDown()}. which are then executed directly before and after each test function. This can, for instance, be used to control global settings or create addition log information.} \end{itemize} \subsection{R Code Inspection} \label{subsection:RCodeInspection} The Code Inspector is an additional tool for checking detailed test case coverage and getting profiling information. It records how often a code line will be executed. We utilise this information for improving our test cases, because we can identify code lines not executed by the current test case code. The Code Inspector is able to handle S4 methods. During the development of the Code Inspector, we noticed, that the syntax of R is very flexible. Because our coding philosophy has an emphasis of maintenance and a clear style, we developed style guides for our R coding. Therefore, one goal for the Code Inspector was to handle our coding styles in a correct manner. This leads to the consequence that not all R expression can be handled correctly. In our implementation the Code Inspector has two main functional parts. The first part is responsible for parsing and modifying the code of the test function. The second part, called the Tracker, holds the result of the code tracking. The result of the tracking process allows further analysis of the executed code. \subsubsection{Usage} The usage of the Code Inspector and the Tracker object is very simple. The following code snippet is an example: <<eval=FALSE>>= library(RUnit) ## define sample functions to be tested foo <- function(x) { x <- x*x x <- 2*x return(x) } test.foo <- function() { checkTrue(is.numeric(foo(1:10))) checkEquals(length(foo(1:10)), 10) checkEqualsNumeric(foo(1), 2) } bar <- function(x, y=NULL) { if (is.null(y)) { y <- x } if (all(y > 100)) { ## subtract 100 y <- y - 100 } res <- x^y return(res) } track <- tracker(); ## initialize a tracking "object" track$init(); ## initialize the tracker a <- 1:10 d <- seq(0,1,0.1) resFoo <- inspect(foo(a), track=track); ## execute the test function and track resBar <- inspect(bar(d), track=track); ## execute the test function and track resTrack <- track$getTrackInfo(); ## get the result of Code Inspector (a list) printHTML.trackInfo(resTrack) ; ## create HTML sites @ Note, that the tracking object is an global object and must have the name {\tt track}. The {\tt inspect} function awaits a function call as argument and executes and tracks the function. The results will be stored in the tracking object. The result of the function (not of the Tracker) will be returned as usual. The tracking results will received by tr\$getResult(). With {\tt printHTML} the result of the tracking process will be presented as HTML pages. \subsubsection{Technical Details} The general idea for the code tracking is to modify the source code of the function. Therefore, we use the {\tt parse} and {\tt deparse} functions and the capability of R to generate functions on runtime. To track the function we try to include a hook in every code line. That hook calls a function of the tracked object. The information of the tracking will be stored in the closure of the tracking object (actually a function). Because the R parser allows very nested expressions, we didn't try to modify every R expression. This is a task for the future. A simple example for the modifying process is as follow:\\ original: <<eval=FALSE>>= foo <- function(x) { y <- 0 for(i in 1:x) { y <- y + x } return(y) } @ modified: <<eval=FALSE>>= foo.mod <- function(x) { track$bp(1) ; y <- 0 track$bp(2); for(i in 1:x) { track$bp(4) ; y <- y +x } track$bp(6); return(y) } @ Problematic code lines are: <<eval=FALSE>>= if(any(a==1)) { print("do TRUE") } else print ("do FALSE"); @ This must be modified to <<eval=FALSE>>= if(any(a==1)) { track$bp(2); print("do TRUE") }else{ track$bp(3); print("do FALSE"); } @ The problem is the \textit{else} branch, that cannot be modified in the current version. \section{Future Development Ideas} Here we briefly list -- in an unordered manner -- some of the avenues for future development we or someone interested in this package could take: \begin{itemize} \item{extend the {\tt checkEquals} function to handle complex S4 class objects correctly in comparisons. To this end R core has modified check.equal to handle S4 objects.} \item{reimplement the internal structures storing the test suite as well as the test result data as S4 classes.} \item{record all warnings generated during the execution of a test function.} \item{add tools to create test cases automatically. This is a research project but -- given the importance of testing -- worth the effort. See \cite{junit} for various approaches in other languages.} \item{improve the export of test suite execution data e.g.~by adding XML data export support.} \item{add some evaluation methods to the code inspector e.g.~use software metrics to estimate standard measures of code quality, complexity, and performance.} \item{overcome the problem of nested calls to registered functions for code inspection.} \item{allow automatic registration of functions \& methods.} \end{itemize} \begin{thebibliography}{99} % \bibliographystyle{plainnat} \bibitem{xp} http://www.xprogramming.com \bibitem{beck} http://www.xprogramming.com/testfram.htm \bibitem{junit} http://www.junit.org/ \bibitem{tfg} http://www.xprogramming.com/xpmag/testFirstGuidelines.htm \bibitem{runit-sf} https://sourceforge.net/projects/runit/ \end{thebibliography} \end{document} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/README.md�������������������������������������������������������������������������������������0000644�0001762�0000144�00000004476�13277574135�012623� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# RUnit [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/RUnit)](https://CRAN.R-project.org/package=RUnit) [![CRAN Downloads](https://cranlogs.r-pkg.org/badges/RUnit)](https://CRAN.R-project.org/package=RUnit) [![Travis-CI Build Status](https://travis-ci.org/romanzenka/RUnit.svg?branch=master)](https://travis-ci.org/romanzenka/RUnit) RUnit is a testing package for R code, inspired by the xUnit family of testing tools. Originally implemented by Thomas Koenig, Klaus Juenemann, and Matthias Burger, this package has served the R community for over a decade. Since RUnit is no longer actively developed, I provide maintenance of this package mostly to support older projects that still rely on RUnit. # Using RUnit To make RUnit work with `R CMD check`, create a following file in the `tests` subdirectory. This would run the actual tests stored in the packages `inst/tests` subdirectory. # Our package. Used for the test suite name pkgname <- "your package name" require(pkgname, quietly=TRUE, character.only=TRUE) || stop("package '", pkgname, "' not found") # How to determine which files to load (have to start with test_ and end with .R) pattern <- "^test_.*\\.R$" # Which functions to run. Have to start with 'test.' testFunctionRegexp = "^test.+" # Path to the unit tests folder in the package dir <- system.file(subdir, package=pkgname) # Define RUnit test suite suite <- defineTestSuite(name=paste(pkgname, "RUnit Tests"), dirs=dir, testFileRegexp=pattern, testFuncRegexp = testFunctionRegexp, rngKind="default", rngNormalKind="default") # Run tests result <- runTestSuite(suite) # Display result tests on the console printTextProtocol(result) # Write results in JUnit-like xml format printJUnitProtocol(result, fileName="junit.xml") A typical unit test would then live for example in `inst/tests/test.operations.R`. Each such file can contain multiple functions like so: test.additionWorks <- function() { checkEquals(2, 1+1, "one plus one must be two") } test.divisionByZeroFails <- function() { checkException(1/0, "division by zero is expected to fail") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/MD5�������������������������������������������������������������������������������������������0000644�0001762�0000144�00000005423�13277600065�011635� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������393a5ca445f6965873eca0259a17f833 *COPYING 281d731d8ba9ab81f394e842759da685 *ChangeLog 4c68da39d1ad6a3cf171dfc7b00315fa *DESCRIPTION ae1a04371d960113fdad74ef7f101ad9 *NAMESPACE 064e3272cadf12e1b554d72080be555f *NEWS fdfb94feb8fae5d9b5085d808b592de1 *R/00Init.r 09aa5f32677a0c132e239c896b92f3a6 *R/checkFuncs.r 8e6d53a5cb3061e2fedf1dc809e70832 *R/exportHTML.r 683a514df0cc07ffe238287faecd22f2 *R/html.r 7d37388f85a1c445e4c11fca01dee7da *R/htmlProtocol.r a18fca4276409763b6d2ed72d63282e8 *R/inspector.r dac8cdcb71213d0766b2e7421cb6d5e4 *R/junitProtocol.r d2259ad7818d1a12586f84dc0a34b390 *R/options.r 008c7654ad519fa9de7af8e431ab9098 *R/runit.r ccb285d44908308bc62b7d38d127d3b6 *R/testLogger.r 3ad2fcf437829b6b6bb7d198df8fadd5 *R/textProtocol.r 4990717ba263fc595f16ac0a892e8985 *README.md b6d41a3163a7d68d4c1d4e20afddbe4b *build/RUnit.pdf a02006d8f5a0c7a73e74782beb37b98d *build/vignette.rds 4f62202a93b4588883bd6cf6c53adc2d *inst/doc/Makefile 0a39e751dd8135b2d4c01453941b442a *inst/doc/RUnit.R d1eae6df10a7e5dfc7ff1ced0bd99e81 *inst/doc/RUnit.Rnw de662100f3eb193f5272800e0bceadf2 *inst/doc/RUnit.pdf 42b28300a717f74028c32c0a87b8adfe *inst/examples/correctTestCase.r 0d450a1fca30e1c3e193d3b2d117b423 *inst/examples/runitVirtualClassTest.r 3df11b61be3a51c4cd3f2aaa62b4c005 *inst/examples/runitc2f.r 4201d80ec130d66cbc544f78522c3608 *inst/share/R/checkCode.r fa963471970494c4d471175456202d27 *inst/share/R/compareRUnitTestData.r e97e7c5dda56971910efb622ab8c71b5 *inst/unitTests/Makefile 87db5d27b6c4cc1dfd0182e6d1872113 *inst/unitTests/runalltests.R 20fea06109317e04deac5f348245dfac *inst/unitTests/runitHTMLProtocol.r d52ab1a703c4228273b69a35c65c2a78 *inst/unitTests/runitInspect.r 3416f47fd504913d182aaba77ffdd14d *inst/unitTests/runitJUnitProtocol.r 376b320c3d1576b1dfe0d6dc6925c83c *inst/unitTests/runitPlotConnection.r 7fff5a3a30e0a8c9d9691d9b4578ec73 *inst/unitTests/runitRUnit.r 713c2b0f3142caaf4a0ebfaf69b5c8c3 *inst/unitTests/runitS4.r f58c90583929b356cc16862b3fd87ce0 *inst/unitTests/runitSetUp.r 1ab9a24c678577d379c64bc455139134 *inst/unitTests/runitTearDown.r d2406b7de7c13f3d5b31bdd5c8fc226a *inst/unitTests/runitTestLogger.r 128d52ea94c7462f871e3242e39566f1 *inst/unitTests/runitTextProtocol.r 9aaab36a3a42fda03d01ad73df977fcc *man/RUnit-internal.Rd 40c5b6b20d4c22aa82198b1482890477 *man/RUnit-intro.Rd 04138fda366b5447fc26c40ac83e9d61 *man/RUnit-options.Rd 7d46ada122a310564fd0425abc5fcd90 *man/checkFuncs.Rd 5522d30b0fe7824a7892b000d7091f8e *man/inspect.Rd 9947537f7c7b0a4025602db0c3efb9bf *man/printHTML.trackinfo.Rd 0bba93c36806b00b9862fb74381971c9 *man/runit.Rd 759b69e118e0f509b1af5437d54c1662 *man/testCaseSetUp.Rd 07eab7008a41f2671df5609e292eb48e *man/textProtocol.Rd 983014e3af027273fe45ff75a8e72fc9 *man/tracker.Rd aad073d2afa8c98edc05a00dddc8dac7 *tests/README d1eae6df10a7e5dfc7ff1ced0bd99e81 *vignettes/RUnit.Rnw ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/build/����������������������������������������������������������������������������������������0000755�0001762�0000144�00000000000�13277574246�012433� 5����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/build/vignette.rds����������������������������������������������������������������������������0000644�0001762�0000144�00000000374�13277574246�014776� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mPJ1ݬխ@`R_RLk`s! ,ɚ( $39sf^�PkjlMǂv tXwN*tTI'30Aj�dWJs-jBeǓ 9aFI{ NޙJ̩zu) <h\8Yf'̮2<~Ka՗џu:3vyqxt\hc:ܗcZ xw0E����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/build/RUnit.pdf�������������������������������������������������������������������������������0000644�0001762�0000144�00000401126�13277574241�014166� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.5 % 55 0 obj << /Length 1086 /Filter /FlateDecode >> stream xXn6}WPXlt&{q -n T[E M;iv| yC"Y33{S{WgxKy(ܛy1F܋hWMr'xft*r<gxQ;X$As-b0+\֓$I$�[# 1Ep# ]7dZmz+,FD(DEsIBsj>DKrNY׉OQab:Z; ď}R>wz~(n鵗чeZ3^9 !ԘY66mݽT6G(]qbꟶm@[䝆gHIs>أ?! 頰Km:kx'FAsB]+,SDBDѿE(&3].Ix3>R- #$XgѼwU1JE֠L燔xCP^%9C |BʾR-E\ |}P]F2oJ@XW80i/0A$Z>f/5^/kYI7{!$ܨ?1?88[S,fm# uZ@q$BQJ*o~vn|hSh !w-!FEesvʢ AZuϢjjҘTijoQf'u }.̡yх.Ln,TS>Dg\ tmzP.hhгuT<Ed(qppW)']V"=$a -%͹5�!F4KmߍV- CUW,=c. dM0 7FD$!?0a.O/0=y7%^}Wgާ0醭'vZg3/A̘;̻^,:UU߫Gv5~b*zL 'NTNmQr$)_,ۓԨ[?1:!9ᄏM4'`>O}BJw<Y O@;ș?eUw[BLP,O$څ%TY:er endstream endobj 76 0 obj << /Length 1692 /Filter /FlateDecode >> stream xڵXKs6Wԡf"$4v'鴙:r/q4I%!ȸ.(R$p > m=zzu"J\IxH)"xi"Ej}ŧջ*m%�N}]z&n^pd:O[;̻[ 8gNGHT2I f` E RuUMX %+X"}mh͚ghgPa={Kf-I23Ômugt3o2:}^u;݂`bS9y7LiQ6p(®Xbt 4޵ ~zM;nGȥ@*ɥ@+n/HvHKu$ê2S^�˗<u80ףk7D:Q(7x(o-5*Z~Oxd5DN9Þ5ԩ kA92LDdA@ӀBZ9NozL+' }[urp1�fuBeu 59_8T�@XSS|7xt|apȆއqn5:Jھ}ʻ(U>5[ ]}2 qW{<_n=7|){E'eaj\DEoMJz>]NWy`ƾe X~V;^EA*"<GzeAh%Q` ֕L¤V~ߙ)'xrr$#ܲ(@,* ѳ*b`%S؏(oUy`ش֋{M{d`ĸVټH M״.Q|sjuCղkfϙib40+Vx*pڒK4471#UU6۪(Dyu:ͨ@L/De 8ъԨ &{'ڢ�@AޛTS[D" yߪSj&- $UH[]|θ̠Pk t21R)4Fॿo ^:֫@l?*gn-:fp(U{kT}ŽCɳhyФ1]껎ı@\Ŕ BfF]>| S,Ul=T bf["A2r[یQPB&`(ݺ=ICmz`^ go08*pP6@ @ߢ́(o:N|5ڧ8vª|P{*buaJreT}ncj86'B d2^@;y�6lJ',2d.՝g_-]SHfS)C3cqٱ:1611FȖXH84`QOr{d#C W>9dg1sʑȆߪґHd`GC|Z;DN41ooݘr:�^F (,:ofܡ;;s 1dGKzകs؄i?`>$0Ǿ-bbC[lO[֍;4 77&av˙jVٖ`u>mQx Ʀ 2oAg ? endstream endobj 101 0 obj << /Length 2612 /Filter /FlateDecode >> stream xڕko{~q8 S|.�Wح4EZtM$&,8;/R86 3\g3/~_X]YWg͙NXNg_lg/W]5߭x#t+n(/\9`u# I܏qxb}v~4Ukm<=hwxњ uyښCmٖ0^ˍBK?Z|5+i&d@&^lY] g'b f.74a,9h W2vB&zpf!FVVͦ+yX2 G Kןk ڈ=P:,=w Զi=S^I^FP(@:*x G2K~o*R N];JCa.EE2S4<6z"$ @ސ�e[ewĶ% <MjL<GBB^$N͟n';kAg27W$j:qw֥ :58ū9#opDb}RpXcLDd- O"=dq(x E˟7PvI50f<'PSDfæ_n>UE +Qƍ#~fCCm9H,Dꓼ3!1Өo?BKa%)|@yd&\C{h3,S]Uug SrǐR7 pvͰRN0la",HKRhh+3KiarpmTPE Kv>iIƦ>ci[R[N'!d> 0m#1G[_fYs=Tr Bv.tR!Q< iW.C= ?c|UTbr.Y₇OhWţb#�@=1Y�.KTZ9w2UOV k11 0L˙'N�I8s"R sVH04~BTS9!ʷucH�D߾$軉Zn > =s[tjaHb)" 8 ,DFIَRuqYw7n͟ޞϙ3 jE APf.2Iӟkn|tLKnsOs# ɲF6uqA$r,֦MVX:v)f%36Fjq͝Z,}wsB?XHL FPX"CD1hꪾa˴qlMel߀f*)P1xӮ88{N.P{U-Af-z۲%덆8%$,Qy۹Z/!~εK|T#a@(-:1s6?7 5CK~0m稹N9դ :AJ.z|kZH%u4TryP� 8z!%-BQ00:z(Y5~۞0cآc=v]h?w`?b Pv\/\-h'վccR"O@ϯb =[OٖȸQB2Q7s5uO$Xh6Pis橣|P ]!6qڶ>Ō9jA ^v/|:*!H Ao�D5 <n^&l){1G3B?'9Q <NPo+~4M/AhUAG{[Tɵm;9͎-U %kV䀘`e© rI$Y%g1;[x)MMÞJ(KTNk9Gw=2rlp HEs2N#)@ YO{4h%մoGL P|TN e R"y "NRsf: Nj1O(p^=HR|E.Z 0l2Um!ehΚ}C N`wxHD^xN�Di[^&ʂl =-̲ :LRMG=8J/䙀~7tVTS3CɌM|B >G'LE,V OĶ] & PCYRȮSߊ/j(J=P+)n XSTCψnSAjy*FQGzʖ=-I8G*PԲ?"Mzҩ‚&Elq�r$..h O1-}ha,Ub-O|/lW['ԈM]pAm-ɣ< ok_vzuo.-_@lo&ng[iV+?e[)?9}Tїt% endstream endobj 117 0 obj << /Length 1244 /Filter /FlateDecode >> stream xڭWIoFW J8Nʨ ȉ\8KØID)33tlo)YX˱8\ckfY:Ȋ#g28*[~R$EEWuRAzh\x5N'3;'t(q;2Z:@v%JjwNl xGQoXZpAv,/j &7oQvwmA4AH {;EQmo=7l, հ8gA�#&=AYܸkT!T&1a-ӕ1pa3T<}^1M]fOyh]INqyFf(NRF!FI%ӟ9w3u,ԉRSL0`QpBn!@:6%(b1/0k9C Nq^Pu+C))Hqu&2Z_%$pƷel\Ht�J`eS{E% îCS\2BW` RG Dq'\X娢khcgZZG':7oԧ Rq:/gVu3iO5 AUAE-KRFfI0). <-Al=~MI<΃6뻲'~JCJF|d<wލcKz"13j]xXG)oD'UjM5"Y4-U8<Ҧ6)1AJ c׵U2E]3jOιANb�"L0 \@!Bb'ޟjyJ(4QMZ:OR ͌HI6OCh/KN6TR@;Q/jh[= ] "�L=2:}ĮT҃Th!Sϼn񩥛gfyw |쉛Svt>PeٍFYbya}k{ViÞ+1EXV7tl/Oj/-?ӞlBH`ހ(O9 +& s ,PJG2X2: ›C9[Öv-ڮK'\kwSuNa>bnu|x/vcBN]3D He A(8_X +$= endstream endobj 132 0 obj << /Length 1443 /Filter /FlateDecode >> stream xڕWK6WHQh̐zh$i4qO2m(Gu9VRbՐ|wvUz9ɓ {QJ(҄8 No {D&(3fYQvyŀUD$^}H$ϼ"H FQޯͿ (I"4%K1~\@9/85\1#G^NY%$WH6{D#aߚwfGux%yL 0a$Ie VMcpnAy"LI857Emq�TCnv @oKK[h +i܄4/ogݥe1k/ 88FQ.6eogv32NB@\!Fz[M@2QsEInڦJᑹQ{EC :Ꚗk %G=)zߴC 6 4AbJ2HKxc(A:6C_Ew RF3u:C<K F&,HZ*]6(LԟhjRum_vʮ ĵe8B*ۆ}ۢkP]qA俶jTS;Bq{BZ b~STLB"+!5R-T,f?nD-\#o9[xH/Cj_0d'I8͉K4H˪Q%VB1w:?5i'(VM 4b#cJYuUu �]+bSyX7+ Im5H [)W 1.y\lBxlL7z]p^ke͚MI yp!5gVUdvĨeb8mZ%͂/6qZ9/ݸ>O&{iu8,}1"Y-Hd+D3|k:T[4AQ̧4'Y<{ M>*1UpL0@ p'OOO3Sca Ʉ%Дbnph]h :XL*W] DMUۓ7WaI^Һ=~20J5w#<G^>XFUr6u>W76y+Oݯwskx#gn?|:Uy0KbyPv> Kx\*?t2IoQqڡ :q1Ԑj%wn4fʬFg?F$ ",Bg\-/.mCXw,zmyd @qlrU w_!ׯ=/1wѶA2S7SQ̠Q-M�;( endstream endobj 148 0 obj << /Length 1583 /Filter /FlateDecode >> stream xڥXo8 ~_alfjI9[ۡpm{:Jḇ3Yw؎v ԲBeZ[fj1;8XYu+ \Ka-';p,>dOPFy5Z5YY(8s+!VK"D5SgN 4EվHQ1s؏++U>se+}n&˕L<+ZYN 1`.,w??3$J×h b X1`L-Gp`VP/@ZZ30{ kCJ%5Q] ʱoEZ].ffVpng~LƑu%9 =^n6~jv{-?YKiz``q[8k,2.ϸ%f0bVuZe6C"0N)ba{#G5GͦRj~II'w%:3˻$̈́<**'PUvNژي=(N*Eræs^>4hghX޼Lh#ŒUj[:܇h¿nv0EM.I9ݰe-^҈FIpD9٨\S{+bEQC/F6ʈ8n%rwgו,$T+)k{`d[-W׷UߔX솭YUS("BAfᑽo]=* \D]H7*z#U]֎1 e'̃ /ĴTC%.aimƜɼov{H!;DzJ4DA(sPzπ}3UYվ`B¸]Uwe&%=&TҢ,0�U5 glgQ }E@c V=K7 vpR {5pɊs LmAꇑͅiejvqiOj >0 !#uz4D?k hV/ʵasHp¨Wu<lZB;=Ikj:2@<DFH_=u1m4QF'-֝* pr!ZA?MVaу_tX(tg‘TjU:iQ EHԣ.�͵: 4<xnw6SR ]QoWvq3c]d$rs LC*pdC!6js[@ñ]_:B^[~┒<gGW{,/.(ƥr bv5jC[b'hLh̼ɞJtD fSV޺<c5ӻi6YbSǴvm~ XdUkޜ~z9x1gF\H?_cXwM9{rW("d~wGl<4.x~_O=/N}kgh7*SW.'7xx&0\'n ^OI`ʣ0Ju2?$ endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 797 /Length 1753 /Filter /FlateDecode >> stream xYn7}߯cBqfx �M lhk7Gru}JH^穀a.8"L0"&ϦT 9Sؐh MŸ3 aa :ѠWp2`A,,hD#x}x2> (u1 s1>̀DS >FLӁ E PJX!{ƚ -@\ &8I1lv LD?n@H!Zpx0 aeKLJ…ā")$ Ar"̅!FI)I[|6J 5$, "}<T"' CFT F@He@$zD0$=)tVCHN!̘L`2( JN':(ҽ@‘t/PȞnƃngEzE]Aݢg8(?(ZS$U@bU!cZ3Mbѯ4~ZO_ޣzj:^#gLzܬn척̦륗dڼ:~6ռvq~:Y3k;qqh&Mo_wY3.V6R|=~LuspЌ&ͩ<F.k3zwF/fӦ6 ha]BH;>=}5'Nj aFٓ:s,�uϦ;Q_kbפ]S&鈟V7HWf2\NGFG/N~ƍ9 .Z5|#ȭS͒�{f{ Xn;,m]sz?%D5-pbSkmte?�J@-_spdSC~�؀H@9[$8?Yb"~ Dsl325H6D@†$YM%]lENeRIL..N@uX<c |MOLi4rg8m>c1t|TCpe/qu;\lŸc\_K)"5n-8�M{Fjr04!ޚ ].ElpO\2Ψ6@zA,1}Y{b΅IA ۶3�TXi5y-1,ڕG7ET)S˾KGA;c[@�m3IxzpI5Ԕ8 '^\!KgpL[9U/@e0׍^Pľ8@v9ѝ`.p6YΦ J֢֌-/[YФSLq]\﫳ᔘ2k*u0TK=kkJBM=49OCJh3xE% MpwQiZ8JW$xhk^VEmG^N R~Ј-F`|/( w?]hƥuq( 1{GJXNWzz^ k<vVnb/RPZ_�{-=Wz=Cr'r?DHO041m9? w56lxYe)am}[ʖ9dKn pvd%IcrЭ85ζ2Y`mG#Į͝p o endstream endobj 163 0 obj << /Length 1500 /Filter /FlateDecode >> stream xڕWߓ6~_9:ř#;Z(-AqXrvwW+9v`CvەœÜgQ^8>c^%N0/gv޹Vݯ7{]ˋ՛f~0F,$�Z)E3'=_>|2?D^r޽g6_9gέ9{) dN弝;'A0g^NR//zқhD#guVvbb g ]iv./s?zK<LAPu1$ %F:a2$$!g*ZdSBp 83/ɾy}VCƙqH]uXe+ih;EsA_&T\Xqef'&^򚱀 nт- zyFy% WRv!ߗUu [^i"w[iuVCgjnV~%Pü.�Q-ċC620 5Y�1c}4\x O!hs:Dݩ/B2) IA&P}z<31mHcQqe9_IաOdSw\}]eO=6w/1-LP#eY9|0K>߿.d�>Hܢ�PSu@-#6w$,. ?7M;MlA64-BpTŦu–Rc*JlbҡI>CLt{åG:NZ@J鯣0P=}BkgvR}OrO`1C^ly4OUm)v%mt4<.04Ӎ09rڐEI.uWŵT B*&(=t;(奿D"CP+Q< 'vi#aBA^ FFjطf&.n0=qE+Y +GcѬ.E�_t-)RnZ8N_mY }L? ;oN)e|Sq Qcа\L5}Rĵj@hnhkjN M4;4d}@L^r s6L`fJT?뭑7pR9ݠ{ә}9LMZtHU閹,UVM?}|8 MF*dv7씗sP8telvz}&I-~]qϫ0j㵹& RZgo0oe3OeNuχ4\dvttqԶlY lg$ O2c<cn_  %u2�'#c#LeAF 9|i"3 rnlHXVCgTMHƊlڼf$lL8?HKSyQ>99|`lkŔSǴ;˻br�g'q$LlB(�2"a4DJc/#CHW endstream endobj 179 0 obj << /Length 1821 /Filter /FlateDecode >> stream xڵkܶ )३�s&@{K :-wOVPp:I>G{8\zw:ɃRYCI)4 L n)Ÿv_Ɍ0"q$vva.7ތ͠J@X�k"6IǻM�GtW_[.7Mןu=[Ý&0vИCT\ZuӯaglLs#tt?|)V* ,DGXwkA |2*VFtG/)v"opxQ*Q[] ,tl;ۦN s1^%/߻_"A4T&HJԧ6A�#=iLmSWA54 TQ4gWkU*+e, Sq<X͖Tuͻ* rOK׶9Լl3O1*3P*E ;N]@8 q4wwX'IAd t!&%q5ON::w6ZnR #y5U^509 TPp@ƮqRU (7ѹ̡:uO~[P!hiSØA j,KFze0܍xTu( @9(݀5aٺ6Yg?M&I n,yyk,gsۊyE#C6Ni"&*%8[$gΒ3S᎐!zY1K ;) ]E_AEw손KX)Kf @{}ᰞ0V3Asrh^έfC0)9Ĥ{N_2g ]8 :(+EL�iEwoг90.#1ЦB^-hbi*$^)Vn@X+nz _Dqtؔxo⌷`-:D'tX}j7'?a6Jҿv#j#Su;-Cy5GFtP<Ovix o'~x;!)�7ֱ7!02%0Lw]%z9/nx}O\'޼z0"{}}b_SGS=<r %ҲXj)E- ׈"mke<k<6I.TycIK4 4`)`2Le)ds٨?6`o!4(8_v?)z\0tdB3y2ҿ??y/,֊RS,/y]K rٜv;;G2ۼjUѵEXO2$ag2#T棞UjU@d#Yh"֟C90 8d-I*Kj&4)\@y'l2x$p;#D p](ֹe{/?1rXK#+GTf(Sݧ<!S6']ݎ{"AϦg]]0Ww>iG)?/aHeT7` ]T#=+q*wX@APB;/cxY+ _e.W[ 9Sv1c]W1~\zwqknKI".Eg:w4O%1VD^t I=z*q3E;uhM endstream endobj 193 0 obj << /Length 1860 /Filter /FlateDecode >> stream xYo6޿AFkU5`-^m1헶hȒAJMGʒIWtCQ:y&ۉ7|b<dndxĞdwdS.pUiDͧ=ξIzG,<#EtxgajY F|y$qlnE5Q0|dΎ.iʼUI_ꊨk  YFnVA\sx|a%N!6LfFYVgk{`G~d~ QiY{W =YIj7ύ?u\0^/ʟzؖ|?S7NGy%Y B*DM/D|?#=^.>>>{) 5k= ?On.uK&`MQIysWܳpTl=W&к6踺⏊`(<r]`v\.ޞ]M'.WfOgǖׯA(lI0dng:*z]</cJ1RP$[)\:ԽMI|=;B�*/\N}PVw"p1(O2Yڢr 6M4?s<bzf�һ6UBUF+8T7~Ҫ^ѳ+ZmG.\=ܮ\u.3[Lu(gu۪}זּ^Bq4mg-`°-^]ZF6N$J4 !S?l} `[*pfOI!`>_7^md  Cc<<ܑP"n 3Iƴ &d@C 7c4|(PwiIV4-_xP nFF /m+#t>#GI2QLSg1{ ғ޹".Q"&Q+" V Gag2Ihn;FVΔQxF`7xޘ- uf}PH/؊Dh} C8n͵KƽDs7Z ӘáW C&m)y2IhNbj"Us㚨"vɭopÎ튾AVuWוnDnN^5�(ApM7cmi7YRT[2sj{EPUNM�Opo>hO؊m8k~x?3C8Hʫ=5EzvO ş BcۢZW2}zt<poSi;�z:{v[Op]{;Fc{ \(lbMcbdKЕ Ş&߱RY=ey95$QXQhzϮ㹲L,@ULAYC 3f{+yo1C=-ѮL?{mP=5.2]w5Q&b<wA5 lNѝc�u;J:+DNKV%szC^ZX_<MƊk4RKBiHzx^-E1 gq PIg&r4Qm)zт髐DdaZvuՅ>4&||3x90b(pa}Kos:kHkJ䝚b YL`$&<jM3:;Z0}Hn0;2dɓ$|ʎ <m/E?w|$ETcb7OzkQxuuG3|tK endstream endobj 202 0 obj << /Length 2807 /Filter /FlateDecode >> stream xڽ]۶ݿB'ތfg`7vfZ'^EBcTA2.(Pv<c `_/گw/l^|.LWȓ Ymv+"U"զZ˓ݿ7]:;D8�٣vsꇟzPx_,qP(48A@ LAd}}FѸ<(q*~uw0Ḿ7[z#HA?p8 fXXmHWb {i۠rmND'"cbo}碌+F\a&� p�GU@iY / S!|02Wl I)y�Yamw<sFztG»7D`27xW7^[IRJфD0cS Ia'ZԟTYI@0'f@ЏF.n<>�]ϖu67%$Qq5l[0s; O{y�q2G])Fk a@0F[^,ӡX|f)!jˢ |oRj+>L Kb?T5`PRLjƶ|ވPkDFQ(s"H8 @A2"3½ʻEqZ XL, xYY$L ֦�6+ X)g \>#)'%cPvk%l$Pl`h1l ؖc8a.YE4F mt#: ;ޫjD ^}G2b j bG@># AhE�T0?fM znMW6ES>�LD~hM%+EDgvD)~^ Ī&^ Á nHB!9|ը#( TT е҃F[hQ꽝Nqƙ(o"J|"=A9@36Mg5sf-oϬ2<rQ.xJ6ѶqkjNRs4yϳonk�o%—'to ,,G-Ss 2x&Is6ޡy,?z-@ #$+R͵A̓"xqv'9Aub]aİ2'3A?q|L,QAT*u?6MLsK \֖YR\j~uJlxB>I${g-nŕ.R ;F;wcXf3 Α7Hs+�yxlx bV1d<�^b͕(9]6X\s <v@In.1a2Ŀ9e{sL!X&s^нPOiX?TF";jU,"9y)H[.H%�ՙJ�H18Nl`W2Xa\M|~o*2pJ B{ grb*u"[-;_޿_R\(>bgڃf7,u|WQ ?KCL0i.g _=5o**QSBspྪ \0]i~3E5/ yFMj,ϯ=aLpY; ovK;@A#9<`΂;6l yT(pb;\�}tO,d>߽~ED #.Oy辡[  +d'hzS̶}S'رn** [qK{JaXLPGձӏ>΢g~?_!$7EfNс݈D\D<]kκK$BSYТL9<ǭ|d.uoV~a6 �xx&/鮡0X0Ɛ2:ȹ *d(@]-ii�vδ.}R OaL pZ Ձ bpV h' "f.Uo"9;}c Y:So=KT?z9{+L(fb9RW4]js�JW%CJ(sb jb@<9ioY2"n'9ђ9A>32HwK.m?1tyuGS+GJU S�̫EDGB&Y$zHi5XO,8mU }xR3�;W `Da}}!mb=`BǗIwh;},A;+!%Ë,ž@fJ5J,3T#imA nhāM+[er�E�0a|¹T&~W]z!uL0H6f NӏC, 3`e>L AàG>ѵ(m$MO `rO|H%Sqj:3MlTs }q/o}v -a Pxx�QM 1fxҦVnɫM([p`An#n{e[:rǎA]#-Vv c<  CsR3f|/0.z?�i y^KM.cTvﶳm5OҦoQNGߢp:g ;-u[˿PUxOsoi$.ɗeO>i|/[R͋T]sX endstream endobj 211 0 obj << /Length 1407 /Filter /FlateDecode >> stream xW[o6~R`%=kӭ]UMȢGRIwx"r ^l<7~!6^/^gQ(OhWQ FqHb}[ؿSWKy"LPQ@Y`dl9|cS[..p%Lml0C2dJR. ^8I֛n);f`JQveڅ߻Rmzщ% n hplȘzH> eПv[֪- ѱCh'i&}v BWfZ.VԪn7C1ZL"!F0F߽[U)_NE(K?k$8xj@@ Œ"BS@<Gv L:jRkң4|$FgU歜L`˲^|TU7^݀w =ݾaIO$IP>'OA/$1;`V 0;v ,ɟfWuˆE]V:vp%!ėtnM˯RܦAǘؗuqnřBO'}E ؆UJ $$b6VLvn{`V'J8�]_vm յ0DjV&a憭6gZNT&2-ڗjkjv UCTupc,Mz˞={w/8'sW gQա޳(IN,V^}t :�Ua+޿sэhHguքh7ov=}DŽdm˂ⶆObW6~koa*avz�so*Nt3AcevLNOƉ<]]?h(IӃq~߫.Ib2{q8W1-q!Ыv_E@yѝAϒFj?N^㟶Q$" NwoQ4Q(φ,aMYv;�D„] 'ihA%tSNӲ E~9'Cs5pmO 2;{D ؓ7FL+/DmOo f~7~ fC`ٯr3_j/{Q�L]!" /0fe!%Dvj2iK.I֠\`L7ׯm6ۃk+*훭&];<ARs#FxB#fZ03(|촜\a3 }Q]tuz9{ JHF8kigeSojgw\0;/X./fնl>)dV Izy9x\K,$Y[6wGdSqɆԿB3 %o8o endstream endobj 225 0 obj << /Length 2128 /Filter /FlateDecode >> stream xɮ60Ce V$j?Фy]E�h';ʒR'Chb?<{~.H"^ IQ b_܅f@ҏOot+x^ 1B]adpW"A Q86e.'4.˄&]!IjS;}C'GKv֩M_U$bg/VɮoRV \Y}=PxW5uYhs}Cws{"G-/w{Hʺ׽8c]#RZ@XBuu0z3x,CL2|"*(nM,bMAVlZ%7;kИ^yh 0wiF7^hd*NyPaN&91qGs=J±X3,2i`CYɆֆxk:eSt oP֝1ȸPȧ�BZ@^.r2qM3'{^ψwED䁇J.$"1q,LAYz~Hݰ?l`Rwj U09\Y]0-cݢeYA`^ɹb HZv?ds7C+F]u[;uN7 3s(hs>]~iƩ=jϝ^S(=&nFf Cd5YT1�0Ȓp0@x,`_pK-Z2b%\�("HeEy0g" D-w Y_&[ hd%\P>-d>)̠Ox$yaY_$f?}KznfnS r:zc|b~$ ܦjg3z UNeh%[For&*Ȭv;oߔ[yA>E'ܠI;uj~5x.nhWDNZK?twf&yCv{rq&APHČ^BYfM֛&P6ƞc.SD]p6 emf&##a.FIuKbߕ/@,&FXCNĂN`􊁂 %t,s3(7XCMUD> /U(uVfV6gA#&Y2mwXWUNsEg8˘i1hox$7OM68s÷5c+o >6HR׏]ra; 4HөTsMGad'O[C파CkUg'!qq%\4@8\S!خs7hdCN b.=@l~le+ӵQ^;e"iɭoxfu57Ѭ3l+9p͹UXZ T 5f2i{ ,%ȓwF_$r<^bG'$Nl3�)i v�@Sq1r̰5&}<alQ浃)ɝ91cL1DXNI9 1!+Բ"Q)P kJ`Ғe ")V e< v5#aQ pK"?-!A%R%f(_��c7YYaI\¢QKSAmDNˍl4 JД]g^`C�C2Gr|>OxL|[+lEQ r21kfFѤ'XO�d=Y('s+H )Zle+Hb{ɒoM_¦Օr䧁GTB8SYع0;_�!؂^TyQM!JȷO$tnf1ɞ.+aepH@ Q[uϸ�9,k�Fq@8ȉ0Ep@ĕ5x&4J^}[UaX=Թ(&,u^< I|}*� pcTЋ;Ya�/8rU!EEfhLtڅa_vDA`vgg�& endstream endobj 232 0 obj << /Length 1593 /Filter /FlateDecode >> stream xڭWmo6_a`wE7]] lm:@i[,y$ mQE=;nos{ 7uf˞Nƽ$v({E@ ϲUVÏ i؅$pj �U\tG~22y)teװo偩7q Yb8 xg`kY$z.I:gE>M ZK]ӵJd:JE ^ܺLT囪.5UKxYɡF)D7<gE& PM+4a4Iy^!7#M.0[ZVR{f.${Yҗ4޲Z#5DXO˺i3[~Btw9{-TE| +' 2M2H\^0a?mq480Fv �FviʲZJyI002h9Z $p'W%YbVKfjzB8_QC-;W5L &BFYXo$N1`̨YɅ9x{ )h՚ L!jUl$lgd@ې:K&oU0s9É� pF!ux|4 ,$/Ӎ8P* jeosg: l_"~vmW( bFB,L"%Kk h*q7Zm>vunb4IB5؏US. *Qu a0K-rO7/QϬ軩1psSn[l+y~r+ԝq�Z"^k~?Uוr#WnjcxqNxZ5 ,y<\Z`%s9NMXy^3 ~]gғ8Nl3[Y3HKP?8sznP@9@LYZ>ד'p.|Qh 3#0F]e^"C]CbzV5me^ƸئummǼ;iWgBݶmUpe!UȾM_]UzXyG$IڇSA|]b7Vg=97 =-nɧ]Vfי?>:[[m7xwڌ qqu טV# η_|~jbh<Ѭ4 dUז:Tlפ!e $cGƟ,.Aofmw1\8CY`6Vw"^DʪcezeC#vu%﹔貪e&*ipoF6.@@݄, ,P-<Ne.x G_ >zR)Ž0ZlCE!fv%2_ՖUwAU<B}Ts<+/t{w4x6's*qp9 @sIknEN;4]KZoOnL-1RhZM?D{7]ϰ t8qp.O/s endstream endobj 241 0 obj << /Length 1763 /Filter /FlateDecode >> stream xڭW돣FM"-=4$]ݎ/yH,d18 dvק3SջwZ{ 7N*8Α/(v6/T8ɕwQxF&Xd۵]mx峈ۻ pxG > }@2<~^{ 6yVU5uhe䦯i?%e""U)k�LO>лg_7?cڦ7vMUhE-ТD—ܻү~w$1_RJ{kU7&�6rGSW4]7;@}vSV]|U̓wuO|ɲ滲~*jܟ10 Her &zY1<\ܮ~_IwD"V?;\D-Hߩ(Ă?K,;ҍHRu4 EwH9vkRY|mY?@q¬ÍkAQu{yGYGmrݶiۨD6T\T,sK<v%N( D-ÍtK,"f%[Ҫ7}WMЙ2neU4ScI]Тt1 jBլ>" !o4x\^fܠ'yST'Hi%>L Guntֱj#mt|45:R9bUbƂ=d5VmXoجpv,IgX,kx1Zlh8=6l!uYM(Ӂn9{_ͨ>/0HXv{Zq2em!ՍU6X8'tF,2{,QQX~g7hio}ivKgkF*lG7!!&�wljِAm8dS@",Ƹ-os~.1)IY|\q *D9*nWSueV @ڝ3?,NQtAw[\~S JQoj9 80ts1܂)C%%g:EmVF5k.v *C˃ޓ]z2>b\Ph54o?*b-9)=/>ڡƽͶ8BDXv d5DĥYF)խhdGqE  ƉESn??" m %XtƁi%iMqq6, X'HakIxohl{Ãu%&uWGu_ԿL'~̆~MichJGΛf}b[ gf*!kތp ̸,FP*ɴe= Ͼrq};6 l=}֠S^kڝls lW�GZe.*NxJ-I}S㭈0WPH.5 |A5sta_hg(<= (@g�ݒq:~4R4ʇ|mꬢOy07S1}7 3(!S]\+U1zEoFu'˺t{s|VXK/8jM�<?wm{[>>te#S|!:c�o!aiWAS/Dsy endstream endobj 251 0 obj << /Length 832 /Filter /FlateDecode >> stream xVKo@WXjC+@ppux#->];CE%]|3c 7trzofS/=! !b AD.Ӧ|W$8xZEJd)K;pΌʙYj#XatvA AIb~%9-r5'qXMV`H,Ysq|LWm̽8qLA'ƌYQA|BxښG1޲EhBALc?6kfQ4]Z&4"7|W.im 0=oyXPZ˕ҚiV.'xټM?� !& JEQ%EvTջ&b\fmꑼW)`4t24ёDP44H %@PF0s#*KnUS!¨zWyrNlUlY>֞ݾZ+P5WŚ]ck8e֑&fc%8 g! )]jum쩹 {}¸[.~nDzM0Y-p0!` bmfHe˄>b:ݤ2(%oxV5 %K:V&&]wu=dURd40ܸm\[ɬ(&PG3ش+Ƀz9cŘ2f<ƣ#Fߘ/kf6Λ\y)R*cTGUbYcW׶Yռ7goPX208uWP>F)/oǠ endstream endobj 159 0 obj << /Type /ObjStm /N 100 /First 867 /Length 1603 /Filter /FlateDecode >> stream xYMo7W^(rf8$#@>@R 5rpM#8 YF7+[Ql, ͡%o! .D\JE/&r1<1HGㄾRH.jp QfLȕ]DL&Wj>~�D!\" P4\`FIzbs *TQ4%8=,VKDBXؚMfFIhd| ـaRA"%(PToz.r"`XA i$J@M bGC I0a`jl1g# 9g["EBWTNҀB:XDaEmQd0G'%W`W@\@h6QS000ECTX Iq(%HpsBfON<soJTP Nhk6 2 f)Pa 4>N5K-,m !PS2)@ɭhFKB`ʝF;r_<۷Q<8ٳŦ@{�k0nWj y{fRkd*SF0'垚';8p7_V!s;..1oéҼ Qq7snաt_fn1W^ /1c7]#?}>uדywg~]w1:{1NR TW$R?`)D |<@iuҽv4 _Lݴ!|4|yq:bb̞-!oA-50U*ݱLTDZM/鏦%(}5JMrf/ek,c Iq#8gQ ;chB60Wv28E_s#ej9lA5MPlTԾ?Swl,Xtm>Q_e!FGEq4Nh-Gv{!ʣ7=DQ8ݱAe3__AHء2 1"k"j欸]d<|y`Tފ&`Nfۨ<Ws+T<`ŵ|GUL}Xm2dǣ&XFj;:0= ܣ__LeWvMx ʪDg60xF0N['v6?kCMo%i8iu?Φ&9]:7؅ِy R|b|h(oeTUo-wTkky;<bB^;*\ Xr5gf$n69|a턚Vc^PZ<!u4Qzs)c4mH*9_;e<O~r5M1@\.ΰ,V̐k+X؆|p`/vKՍҫrLA&~J^Z8I{H,c`ู%T$<ڦV^֮ mR~XFmQ$} EuI2J֍fޠA\֘|N*J#|òSgi.%aêdkpRۚiHU260%`Y9 }eƹ ~ _$JCɩ~V& endstream endobj 326 0 obj << /Length 1314 /Filter /FlateDecode >> stream xZKs6WhT0Xviӎ`cTI*u}AR Hw|.~�ap6?'W! »�A pj5 >3xWnc^�U?CHF-@Ilׄg&hZ-B@1Jf(<gH&By5#UadGI9 $W ~T3@ia$4 6G̜bޞ`Y*N(?ta@RP7Էwĭf:[OVuԼ+quգ8[iuJ3i翄 t2}EMl8D+Jeq:~C5 [v+Տ ]š?WQ�BPcG4�Pg__>\ =hG<9s~E뤌g-+A͡d+Iz\A&Kj Lh t~L(;;QGF�d ǾkgOZ 4ؠQ|?h@;kbjmQ& 7ۣ'6 Tsb: pŘ�X޺uf=acW D QJ[0@՝>k"*Rǩ^?mrlbW+sLGM7O 0]eg$[|40;1ǘDz9~6&_~<wQ a*b_{!La`6>>I."@!%,78M4xۮs ]\NX7Dd'#`hH3 -d]U>ޅ@|ǒVO$ Vaf @$a?:Srwj5`ݔ99BjNUp&DHƐ]le;hh>h1C؄V;XMl ҇}ŶĶF͌մWP[ ^ƞ[} g?6#^gYUY7Ջ~;d¢69ۺlr!gG+uT)#]Zq4SA˲ؓoc kW8'{U8>$l>Y]U4Gc"ܛvP 5=)ה[_'YKx{ +f:ߗBdr ̢u/]uJbj_ �`VZTZ66ZWv?Ӡ>3Jӯ>~=J(Ô/?s endstream endobj 339 0 obj << /Length1 2887 /Length2 24716 /Length3 0 /Length 26158 /Filter /FlateDecode >> stream xڜT&L]BA5.!;w .a\9ɽ3kuO풧jWw7 !PƉ ecdkhkedaJ 4u2p�֘`T͝,:ֆo}H"j2�DF�fN�#'73+7+ߊ�9s#3@K&bk`nj @iD2ddY3 �@sP;8:X hjm`nEodkME9dbf/% _ F@cg*P578Jlc t�HL@ ��@44q?j4srf`%zGzb6"@'GX&Fhjn+UwG&1X H`.ƶ6VA0Hjɪ+]@`cpөH161ݿpg(xTt%o!016{ t6wc?ZWu˄^7WEqN le.�s�/ ͭ7kj�IrgJ9X ٘Z-2w7w+;L  P4ljfndittk hc_@mLl�wXF03<� On�(6` _S`KO`8� "'A708 A7b A7b0HF .2o"F . #N]7EW@U#Pt]7i@~#Oo93̭Y;-/'N}F C#KGИ h_NN):>2�Uobddkj`KbmoWo ߥ` ;Β \M~G31wv�6n32angC$3ZAke ;(E:N:;ȗ h7@M+&&PvA1 6L>5a$@$AwD &232'gMdp2s�N t18a�mvܻAMPq9; 761@vyֈ'Ȣ&JϕnoD4ühɣЧnCzn>Y7acFګJy$iFGچNdu/ݵ߁i}<)v]|u^G]+a~@uhE"kt1#Oc0W~4 Y ,Ǧ'Kj4C8U@D#WhSYA8*.$f:|'o֔oM~ӌ ImaY`ࠓa׬Wl."(c&i (]oVzpwXkGe|)#Ww_.ðmCk}em VUFN{rz.^`bv;I0񎏖m?;fuù!ǸjE>وA8N7kziͮ{gaM/j}^Jyb 9]4ٹYҭa^獐 "Bn|)b+C}OCd`v,ȯ&yFjc<aeLSdM.z Fb#ITԨX`Cg^mv nB(. N.)`fο!e"?/=v25! W¡qW0",w`XJWGש.Bg8$R=C0\R(Iu3<wQ _E[} XBkt9bV>74 `$pO-paX4p7~ D|ru shHGñi>R\ł N+Aph~*ka9XGiy*11E~oez_zs@;":zJq7@.=?QKҁA:}('X ^DMDg_s/ۜ/LCw6l@33R6O߈#lBھ`bym sT@tg[8vXW3~g4@  2QMV7 Qt< '3@В {-G_<#+ڃŤ:908a.֦ !zjlڒmD+͋p Mz_˹X̺T0bf4q# kVL鬞'b.6}B{w-}*l0U'RU.}$_ }PcP{1FCæ:|Uxe5]Yw*,RbʛP?5i{ق]oSj-uyv-ŕ7A)4&|p}S[*SHHm7\/5桞FgiwBJC&$Ufh`]!(N]/;(hiumW\!H Q6|66DR_ΕuPXd� rM^g3(v/~ϓIO wkZluUdoKQ06 Y; W: ߞI{N) ?rؗnP7m/u=O(쇘&,E)e5XfU$ͺ1\}myQݓ2mۏJX9oY4'x(o+W%Q WgD:3H|{=o rb f^uth5ŬH#F_Է>` ˢnۨң}[1]Աr +(}U8)Z_)[۱(}{sjWԾljd n3& ~(ǂϜg_ϣ%gهrvMOE8:gaM"n@ ~'m/ bL.)CR~Qc =Q*5O,Qa+=]%>f(ћ|$UXwQ6rF|mf{q |zz=6X^0_"J \(�:6hR02Tk,i:]z׼M+A|762{Z;:7Smf H1pHs\(-iU4i#]U3{#?CP\D׍b=5z] ,E`V(h",_6c\O^otw^#tz*'T?#hl#]oTSg*1J3*D!бSty@B>5FW].PhHLdj6@ݡ]tG弇>K_+łT8 'A)#º#pUK*|"ЂΫ9,Gb5ism?MƩn`c aPmuN_Qt[YksGk^ifv1qsɥx+*/,jYʡXەFmͬWRI&Qt.$Aƚ1#&yU]d9j Lw-- p>ﻉԵ=Rmq'A"? KC5Ϙ&$bW/[G 3zp;|>>5'=.5Eظ[SVe#=렚o:vxB=-QJˇ`}U'T- PZj]RVYK|9p⃍912[˾FTFUiHJ䠼޶c̲^ 8Z�0]~K&F* 1E?D9Wsk-3P*x|N9hՉ_1\h{gOS*$c EO'_`%ƈ8\ HMFщY%GɬG՘39\Y^ ۋ3AkÌkKPLSf|> s#y3%~~A1u�Br>mZ-z"*˕SS_N^\Bu-8DN�A2,%޸ev/lO0wwy öS2\�! G}G^_5f]l<#+q >N_RD:p=O&>OJvAMOG TdnrBΞ54@7q;:bS`:{YjZ䔼0@{3!ֆ&"[rPb-)]ɂ̸p@=u^<oowSubqIO5-;UgSYW1cIX?M/Bf}Os>mzkA7LLǤdkۄ2-iR; d lt_-7e?{za5U}4h+-7-!mEkġYF _^*K=O14Gy '_Nc.@HkTl~F Kjqh+  zT?Khq8l}5,OX~y97 l,)#&%UE} 7K'Ι3Br f,gTt"4]b({(фq/ m=Cޓ m_5QMԛdTuS!lɯ'^ikLˌƘJr,1Pir̄gh.P0E I]iQ&z[ U]y]d](m+Z ćp⣱,twWL*u?lenx?C0"pS =-ch12]Im /Uɛ;weZrSb5wuX+ڰ𤶏'GE zm jY{"$so+ӺϹ~PV-'{arW~!Cb7aM0}༮vkŰ3]&Gbmˆ)3]( Vt/ fcP~bǕn)<* _2EnmSpx#Y`_mw"H/8Lr1ƜUUQ);p}ОU| BG~yBS {+e-_~fH(aQhז/2#P>&$z; E}Xk{mR#\x%Tqxi puGnܓTʹYUɏsz+I1.Ck9[EX{ Wk턼>hfk+t9MM^PvVR0 ہl`e=2TRru'(k&FWMM~y;{[A>q1?O,[P<!3Ix/z&Zu63+6_BUiJ!7܋e6 ڒ#4P]"^Gq̒{0m�sзvZ{-T-*\Bi;WTۘ&P76?wMi@. 1:=v{@ITq)D໭ <4,8kCQ{svFs Г̕C\T}Щ,  8mO'.C.{avCc=z {Ռ=KM<wI*N-k|sWAӨ.M3)Bof9%0sm9+L 0MbU M2m`{|ij\6SiEeeʏ(%ݍ=r* gq^0}fҫSVKFgtqdDBŌP@WYX(HHA86muxav[vǶBF:E'ȵK ˓ V.0 (LV7WWat=|7K)nisL`PH nwGiE1u8 LѼhZ@:@"Nc2_;MsyafSaxw׭溛?k"h ?.4ȫ)H-6yG}Sh3u)A *مXCsGȜţ+/lEq_mUhqUt:睅WdUs.\q}HόYelǫE:̢kmZXby%!<cefFZ~д/NDdi}DMX|0\ήw]씗uk` _$'V5WC4Dz{ss =!A<fFHn yoiAzI숞$ITi-X˚25Ymuxu9fr[B8Ydbϴ}X(d1v5H >ьd<ҏm28oVE Bۘ:x .Տ:½77fI׫RFujz#} BFwp_*p4u$XTzKV[ ]qKe+1s Y04+0<q1~!KgN'gU:$ח,t>WoD2LI`f10Zzlu5Zۖ%qNWrŜF_繉P֭[7f-bO sKQ~*u9)r (K&{t)UnH¬ QILJGnH+@9Z]8 p~b3nѱM>^AϴD 񶻩G-\"+8^BV2U^4$Ďj)WoAUFx_=P$3T,`ϹF[ 8X̮πKc ]rvPYAAIO upVVJۘE9;F?.,>5=:(`0on<q;1Օ4(G;~s&z WipGRΰOl6ūεGڑ9J-Z@6#ToB?QܳߗfctBD|n-w!qHp}fHNĩv)B7tK."A|yUju s�%lsCH oaLz} !pycE:9}wK�wV^u|))j4-nwaq<sv{t~g-12wl45 _G28 `Ԅ##$BgQQ||}A~۸W܋xJ~bL\sɋoIh cO_,o4Kʎ+Y1&x9I3td>]&=n! S'KxhV͘Hi^>YjAXX׏8-mcCG>U+К'NDagz;D)/IN_nx`{R(I2.&k tz?q+zC\u1Y5EB]ʢq{/.F0x2rNY/\]7R_qN7gAR5ig'l@A3T}qeqׄ}Ӈ %=;6vsd6I8ME ]$aX禜:+%^5uzcu9ɝ0d x}1)/x-V)`ݱpaΪ&J̅BuL\q/㇒}V o6I?҂�2R{th;(rR]UE2ݷvX./bd:-1 .DX2*XSW5[̠n0_&+7ag::76_;,Iy>"AUq5R5哢 7*СQgKa=g"Wc,J=_sZob8o)~B ԃ1ix-4KJ,fPB+ؕ@%gi59J|wF2C?03V!Ź` CO&36GXw&BfdqkSX ٭<,&h ;&N!јzلMd/ȏ(7>[NÜֺ'"$gQXvuj|\lo^֛ƽ6\㠧8jbF47[[m%l&thl 5)N݌}5IwQQʪ< 7jBG's %eQ'>Yn^ɍM,8RnrNJ1ۃ3Lk.ͫ_Zij̈́} QDeK*h$nffa0*>#fν k5>J]Bk, pTrM`v5,*ZЙΙrCd%yuJNg3s Ojɀ}Tabr ˼tag;0 J 2+X6־@dS6~ځ&`$Rڈẅݳ L`Yږ'n\6pB 3eX] fa,ߴ1jt`blR朕0-RͽH"vH;41GP<EwmY-x2K$%hmhS57I=c Es&:E-)&ڳ>w B@ooPT̗R~iKP%J0poU>1,'Ee7͠e8QrBR8*@i^`ȝ2<:Em.r/JȮ׫1W]G7aegNs&l!u K|38#KM$ܹޟ |<N3`$?|1l?<H$$SL]}3Mh1n.@rPw6d7qI B&z&Ӭo̡RKVnuo?B%M^xvLhA2/ʫTirmXG75<?<+~ެd߂c]]cҞB@\dY]mXVʯ-dSJTTtAÏܼcJRzeMПc[k$vN8_s(#̜hڽSnRI�M1_9&1je]{0}y g vt _ "H�gΆԚ O�?nq8 1*njh= B`mJfPrn4? <p{\aeSNz,J7b?廢13e#Д%th!9-4z?dx";3ux Ut~s5\9EDYZ?OQy3%t�vJ# (Z߂kZ H-I#oGg(5'a3 DM $hP&d\PY@eA`͎Q R +�(m)HQ3&/aEYgLQICUewi5_&r'&_t&%Gn.ǁ!ē^8aH?z;e {Tڶ ")b"Y4Q#~iA�jjA\:. AWv=\;LVcWMjqr3=ڷR:Jϟ+?IqJX⇼맃(ŦCLI{VlZ6+4U>>] |&z0yO,,kc*ŴciRi{I;#!C6׉s{蹅Wic#Ѣ(-,m jIH[þI2)6x#eh&?dXtW>A-I~Rh>:z۔ I[JSIcGLUN%̭O!blU[;[pʇɔ} Ѧc CEV\|w3D2ҰU5<niK$˓J (#wE4XNn艥c1WMB~h쀌B6cR.AC*�nU(EK,݃00 |+pRBL&IB|¨ ZN2hUON0!&>P,~<u=VoFnufwńWL "^44�t|py8hY TufQU]!h)B'%Ys(>yP 3 fT shΜ4tNꊕ$=0lj{%KiNQ[LoY>�bʫ]E҉c/֗ CXa`+]"Οgń,YI8oey]{ۼ%yL׮B�&gVqGG&C )f N4xo!׽2C4xެ}"6z<Ur k/A�>YE7!*<"CXM| %.djhlmCU$lB/fjwx)'COMMpA̝q jDS 5t+y4pj }w=O>TJn8ۛ-!VsPE#I}#$;R=JzUg(kz* AդLmJo<Iu%xr+1*}.؞C> O\S7z3S^k!Vs$~.q}MZOesi(q+sBi-_W2t(Ύ2CX+~53鑟 $Vpft I8RgJJe- mvc z4ywB%ϕ=܅H;][,KaEP<)#ǡ%tEDZ hDPnϣ%>t9U0Ou/Y{K6waR[ 6SQgL'uꮤ͂j2v'NZvb>M`W:{ȲJ ` ǯhY(YܪxB8=d:Vsީ!5{6ث+btI%Duž0~\#I3?+Rf!uN�Cq7Žǵ0e0~|" +?|0"%`HLuOviҚt{X=YޱAmE8'іa*KŠb?6 "}sXSSNqQ|{H"؜V::UG:>0Ut3w)njP^A Z:=!?P[Z�x~c@l~6YC v+?tbPF͗QqͲ0ө涕\ F(!OJmI$_ơaK-O 9l z z)WWگ#h㖽l 0O.@Ao6j[ ND_q8wEۺ '{G|)[6 T`u!*ݻ \YG1!tq?.׶w>+ngwkX\p2vn4JLAy lYgkI@=S3ոKmȇVfx:02Ak,lϮTk_w</C ~0nj@N>% <~j7Rs[~ FnDT X}w_etCsНi]-&[Ф/0,_)#滀utԱ%.Bƅ0Q-BN܏a*yMB- J~W;Q%B?DyPĵ`~>WH1P)X%ԁWP?fKXϻFB!9LK|4̵T1cY0b>W KQ2|~%N7܏>Fm=)VKnW-u/6.cH6bâ̻.}oCasBVjSՈasg*t?ygn<0 S1Mk*r~!/* `Nb 0@~ TNMJ[o`:Z xۧ("nTt{߇OY @/:C+=]R̫VLkVW(D>6 WMgaxoHkv�v/td='Y3/bE_ HC&ߩ0!Pث\E$/CYm r6",Kz|1 J�T5zK0nh.:|mSo尒y}ۺ*}ē  !.3*3]]7S??֭+USǽrTS`{JlN*Ϟx? }cKM8yYu*r̤J[M !MҙT"Tr {vMYfIc QWgzkJ8ɜK6\o(vQpf#Kc$՘|݀2zW4?E!:AW @cɼrS X 0ĺ(2&,ײ=qlpf쏂H_9<BjZK[ ?4ۯSkyij fMEDpRX6J" 8I|Zv VÜك p@gfY*[õoMp|&_Mw)3cV1#Pq^HM( cAA#>k:QEձԗgXݻr(WՊ:l0+be΍@5*A4jhby.,Uχ}Q,.EfQCJ/d `bU- <.ruF(-x_tݛ}t4ץPH9vk|~ `2~G_ERQoX{plAݸ/2LjxJ4JZ!�+^eSr˞"K]i銍č DvV XMD%D>m6s]LI;{@E)'|'(->f(zMz݄a|\v i VhYmo0keck)qzL˺%Mا]ȷm]'(7% ,.Eg^C@(b@{`d Bt=J;XL{ _]xu.Jf,g8C6&g1b!�U4cxbAjND>&3ݜ<X:mݴUx^/oqݳmF~FN*Ka^^ 7wD&kZ^|*EaDڙaA}'{:+nNKkdIj<R!BMt|F3Eb\Ja9"ʞM3h3 %MY3SO{񀬄NꩽFN>޻M8rbQ+x.B^3E]kf 4n~5dNJDS8]bZ,疰FY.P=GQ~<Kx =\&"1Z_?άÊ o2KYgD&bb.IiӜS C+Nv#d1[V9cLQvZT q@Stf)^w0C: NYƒ%9p:+<nI$<I` g#ԞwCȭ?}DQmO ܹ o%SFB5 CPЛ_(ojw}6RBeh6%j/Eq+J?g|6^0<Bawjwa?j g xψ @Vr¤S,n7p̊|.Fº;E O;8'RT^M\qBp{w}i1a#~JxpaUH%vxwDNQ1CAa\{pcb 脃d9𵠵1Ӟ1ޑR>%"@;W8ꬱ>#龐w4lXph=ť&LX2Qta5jt?;(z(lKb:{[g$'(|f3wbq$ؤqTL3FĬ^oD\w~+RL_1U MG;ݶ%uxT*"qKYe^\.Qh5 ]6ۚcbXp[FOɻnádFYr1}Ia|Ch.؋l;AyW71 taIz6G:ْ٘w�kuWAx.PQk':DZH<?n/q L֨B[XUs2IN!v.-so۪;Nw>b@ƞӗeb;\J_tI)+4]-v فO= 0JRNI*`xq՚cRb`wJah|h  ?[j~RFރ,'rmr o'6e<LHkf0ɖS&9B3b(32(^׏o $mR-t T~}~e'Q ]h'l:r? =PT13r,pWH>+?"F)678.F)y푅9F깕sk={2y*>77d~tF?8枨ESe̞Ü?at7{kM4x@PZ͞ThJ$ג_(hFlDi \'K{CDFym}߲''o;,9m tէAIԻP @ *7gj^WMi{ z6]YGs1q3I\`UQ Nd Yᤙ;f-RSR z]Gx$祿іro1;GWh kmd'}mx( d]}'B[8Be>ު[q7&-1;yb0?w3=B /z8a=O<_rJ!9^o5l?)5V2cIN;sYV:UemxX%G%5ZF<kLݓ'ZoH9ˌ ɠ(d1`Oxx璾60r\: }M+i jwc-~n_Ԧ![ٜuF7JG?49j GBSTo𤼱t}8rv)ۤ_.n3!4 dgz .28g1yO_IBfU> UȠ#$y0Wi\e[nwԊ̀2T]|+QL6Ñ :xDt'HZ9^inW;YXJ͖ M%iav7'C%<5%C~,~;7*/#8E,/=u(eL1].WާD`F`=If3ܷ44iȰjQd80a]]/=�B|0¯>߯"xIxW<`ɮ%R58^@퐑g>&u̕XyA]T<tm�%/jhtF\AqIc�|L1+˜axzdbN+J)siЁ2jwn٪MB�w( ZX[#UJ�+@$ۛc𛅰:ZBeUi IEl#qRP EnAqWg Gs xaDB!ًwk06OI964%]/+- H=8?\rml݌$;kL) T-/N'rg9f1E違w79+Ђi? Qe }G_,s/&A+OٴoL3 ҹ! zTILIVXZ8wH%/I g�Qg7 glGBy`*Р:pd"~jV Lv6ɰ-TMkjxXSxYBچM\X13*;ei#t7g|[Y@:sW> ZS:pƺMYSܲ߂ >>C8v5k%~o8r2'ξ\.+{bj( 3d^J *X&'VTGfO8EECKx@%'f )gyL.j r.#%5y2qI_7xB_u?#u&6PaE.u Һ~%Z!vqCקۉOflfC}>qjZШgv&'#uj3 4Іq>OXR�rFln"\.ō_TWu>mԿ#L'u<'Ȧi*o dAKØ;A\f=Koހ pwƲ` _1b+[oH~:J-v跊6:xֱJeѪQT/ңwLXnROr0D+ys Ꭳn`͆ɫEnw=0D c�`tSG]EZ71:g!ts˧�Q0>ysOw>BhYn0.s4ptb@({69yѨ&!BЧ2l%#VQӽ+u]ljwuhvBRA\@ZN # r|LGe1F(46ĘD5~Pc\R`맭(%QxJ|W+(<4J^d"r)`dWA4bp{YU}([dyI'&jKC3qD瓲Z}5 F]DTST_rKUU1(4i\nf xForL9`'ěC;-?(۵ ` 6@0ՙ܍ݑܹ8ȸ[^{oT &|(JB #Ehc0SUc�8].D|}/0)_ y]9 oAkm í`g =f-oCOڂ/WG*vx[TfaQq>ւۊlޒ Hi$8&:6k"$,X :v.*@pdz$9Nl\<XƖ`ڨbĺOZٜh7GZC?8vY65L ;矓)ߐrknoߟ:aQ '܍ȓp۪M+dXjMh+u`TS&2ᵉ|-0xI_4舿sզ< ]xDH̲GSa=a3;stLwFk!">McHCtk27CKшF=>XqK A"'ppE%2 ``R>+S凮Wg #5;]絥yQ&9͌m3rwp0#27pJUIDbn 'Lf` 5~\8W�0m#郮5[`l^zܝ/ڍc/Vftp؀܁n}rw=fpK][pf6oCzq/Ɍn9).zV4L˴s+k_:`�(Z7SX3RO/oY|Fw�XuM-=ݤuEד4Tœ ud'%5yh,)H3ipQ,G3Oc0\�\"dz 85Zkfw[ {{tt$a4R[ѐ76pKvƍfSN5=%ٸe 7-0~go�u\)OK&2 :}QhSӐ K@Gav w{sxa>W2jmg!-#3Y=bZPX' ߵ# S/ OH%y~e.)>5fv%UJ䃛JTzT@D-'1(DS͌ Tls9ҸJ@i�Q466ډl7;f%r vtUUg^qcH Cl&AbOi(PfV~QN=ONۣ3lm$c`^kݘᅳ/z'@R}6܀{l$}�~2pY~e%-ufCE6H)%Ъ9ҪNb-Öb${X}GRɠp`# X=<zF.C:̸``hX@x] {7ݘn: 5h805r_o5L&HJ2XG>\CμW8x0@?zME(hY\aCyYe9ZIv%M`MJ<@xi>Z)Y8> eS'J(o=@4[clv[[O TR2H6띒Pmw|?XeAͲ,#CHLNEmM%!Ln )s$flv(< @yCNa a"HIGTZʼnfDƂS:m,=P+eɓ:qwZId23ѵ{aM{h\> c\1~c2Zwfk9:G'Kg-Sၞ݊ htCT6i"<\i&5wjHL6RuPt x*{ bnuq]E+֛˚LQ^Xu "[QJEn q'ߜ&}\R) B eR)"q8_n~x:;myi݇ wy:%#C>wrh,]ܟ2PN~0̈"jV*i3+fW;nrd%D̛l3l }0͉nFωj&ڎC\kܛFoo됮2M{L>W-KO8"r?6;tc{YB殔ѿtTvMEj:M?x Ov5nz8 $(.hNyi$7̫yl3o|YWϗcN&hy8uۧE1 neď{ٓ22nUzK+zg3#l^ 'G+IC%=sΝժ_Ú GS{Z\nj2nI_c6Z=tÊ(Ti̶UQZ p!_3L鍾m\Uo�ߪ&uTU.9 pڻ ߘŽy+_}|dB#@H ӓB ZVa^֜�cfe{'yyrxw6rv?"V�Ϥʰq'揝M6 SGdd@3]Rx9C(yt~}vS\<* ب<7VwNKMReA8֛iO/c斜*QR�9a1>@"-}v'Cj'H ]PI]ȜiH|?e`i޹Iukȋ+m;wZ{TbڑTIz2}elYe"u1jY* XTzÉaIb,H̕-PPґ۝ݭ ulԓ_evǎ7kE">\F#VUĩh.Ti 7̍|r?-~S1�@hoboe)u|@LBGhB"ʰ?PUt8.�A]܌a\ɝ<$}{Vs]"]4`T60Jfaش<NuP<pM0ΡUR+GiF7pM*\<}%r{De4sq[;GCBԞ$-N$`}5<ZAMYTO5u 4_t)6~|s�q;U$.Eν^!hGe㌩c~J;r[=H@8.Sl e@2)}>4ZY17AӇ,4W p@*cD8s{.F-€\ X*'XUX2}EN=$NxvobG$=F@7d<0ݤQN8U nu|91`-4Ve?T/xSvy4[:Y`npO XBFTg+5/s {*Zզؔ/bf7 maC-+pf*Nz^>j^k'Ca},s.<(;vMWZIsFsnžc.jB:'ua\E維x͎$/܏/G{﵍HNGřމUxb z>᷑w1|ǢM{F֭j_zVxrP4[Ra~7c!"% %K$}ʞ8Ԥzdecu bd婹r즼lvVȱ|ʜ[Id[)vT,Nuҩ'I~h\PonT:0ȖfGxsI0#2+Bp=y|"#R))͠-)H;3eF?Y\^y Esp%iqL87"V̔fZ(+Gd1B}%ƎWXq22:ֻd-@Ȓœϫ3a 깝;7= ZM y'~yd#Ӭ3M;ړ[}4Ns�n+uÌwj TsyNZK/D5k36P "ivQbKƞN5-䭧~0>C@A] :UI>R" i4{$2?c]ܿc]I%L |.AJeSc|<O֞Z%¨RMdE&daOr;<%3|[aAWK9ݳz8] G:m�w=3[K/s\AGfzdI3E,4 M:Jc&�gåҍ5UBxWfNĕ)·r㫢HK)+ NK@w6SOO+#Z#etE=W5 h̛k<# dmSE&9:W?:+aSU<T]P9 {I; { '# Z7S:cګ.1bljP*R|@M:bi&ױې9Ʒ?LgǀzEjN5JR|rj̳�Ӽ/(nHн־ BMĐkÐ: Q*P9"@`O hSlOVMALTk1ẉsKG`so$A"n"U:GcHUR\#Vlr|E_.de# Yo!|ZRk8w:c}mC-XJQ|N;j0JOD?�ۡhNv5r8,R$C^U3{.�xѷh|VѢhʡWFN+^LN1q=N&ǵ3ĢJo4m)Ѐ잡E5&>>>Y_HU8!r,+wcO[7;C0Exj5G6ދ>tEʌhb1+wױL'lg aPPdS=H$dc^lq#g~S0q? /H6I5ONo]C1|" v?%h /LIP3PHZXi UC)cC%\36Ui k!мd&W;tNE�ĎY̡-S 7;-NgC ␄)R'z 0og~3/};j%OYhgS!BhDPwjsc5{*|C_';9c2^HX~aTwkQ)Q3O!5*(<!;M-Z7N`S@9qHY{a?KRk,^1X:y_ 1>Y% P5FՃ!p8/BUIBɩqgQ-5q% Z9 tCA�(SﻮVjԍUB/s8/d!;`_Ûw%>r*T=d2 m1>=h6`'z_`IqMChv(HSW_uS]m€|>lMeCXxS+.J5*[f/@$6=Xm[u֏5j˛XƟ "Ϗ&O(()qFۣ0`c.aA$S92}=!U-^ KXT- FJ>;4X>L:R$ {pǭ&i!@1P`"(L=D¥; kS sZ t5-P![~ȩ@۞R0+=t{<oZw0P, eL+)T\˩>\{dIJ${ѱW!s7( {NBޔYo჈h�&&gNQ6ĺNN"6[�.?5105Tz\H,zWw:rbuI e蛵j @~ZCBc[yĢ&<@Q0 >b AػrsܐX[kJ!f/ܶ;qCC5*?ϟ==ίֲ&~.eާR_>[~[|Gmb?e OBS 41pH#6HFKs`\X}e{< 7E!3$eoH4d)dvK{.4bii[hf䵲PU gLѨIR_w %k<N=/]<VGØp$=yɟyn0UlB^9#=+9/K�Xr)tII.(p̥a3,)X˝$]~";E5c�NӇd6Z$W!df2=Ql!xTJ�0g9ȻqޮHp\U12AH |ԭutR76HQl˧,F 7nc<#�&2_6}#URdS@c~Išr.od/n»ޭZ uC! x6P)s�k/2H(>jPB?>s^Kz˿?sT&A׈O;T=I#hX;o3>\lf4nFL6<erc9RrFDs3rt2B#(< hڠ0*zr M=CvBgնFq&<J#OݫE4+�Ak]a3h[h3/TC?͠B%.�RD9UH@aa9\a)ybH";>tOIMг�xSbGPvx>)�^.fl&!RK%,^4\qG[҅tZ k=[jȟi/8g65~Qm_gPe@f#=#N!״t RgjL}kK.=}JXzWvWg"Vъ-D_CwmF;&]o\�}%~Gnh�%%' U?:)Kˉ>var{0ܮ:of\+#H֚zu< b`!{:HI:<K<:'Zu,*)YI^DLt5nG&PY d.Fᜡ9WfjuڿXxZ|qP谵,"HV`ؚ}Df brBlrLK9IF|IqQ%D$Rҳ<60&MnĨ 2/|31=uVda/ڎ'2m1Syv飌OԿp=8&�q1i!~hl@cLR$̜߼Mm i;4%2 endstream endobj 341 0 obj << /Length1 1386 /Length2 6039 /Length3 0 /Length 6990 /Filter /FlateDecode >> stream xڍxTSۺ5Ҥ#H7 & wAjHB$t^7J](]zQA^x}kk͹d30T`($F$[� PT!4c$f04 T,PD�@�4HB�RQ�U C!ahN% �III(<0 8ܰ!`0H#K {{{ B('y^7 0a^0(�=hB$�g8/1 C!H(0Ðu�l�$W?ѿ#�p #�H/ Fa^`8n PW2 cBh8׌¿`Y UA4ɯT0v}+{GBt6Ex´T`&ۜ`PJ\\ =�| ¿ ~;31`pG 0@tsEp�s#Ik9ƞ`�0( 7 iݷ43(@PJ�@� 1@?X-# W}e?#^?s顰̅xMtk;_YWwGo?_v#| `UjPs_ՅAn€jPB:a-+Vp /e77 3@0( |XA\w4]0YW AAMDL��`|~ ,Da!�쌁�GɯؙhW98r�LV{[0 B2?Ȅ8Ub<Jd-zY0[cꁲ=~\yl�1W!U}¡oG`PӸUdC {+|+Tً_]vKm�%lLxmGl˘aKkjLO-KG38oV Y?,zX[#^Ip[H mrf4ةJyz;C[$"颁AQF'=4y֣f{rL ѷ8 >P欁gՈ" zX]tQeg: MqDmLПg'Dl* XG.d44Zxzl.˞#wN+-n"7Z^w D8N$Ytfom%7k2SiCu&'NwiW`O4(4zgGl)<MINB'r-r'$R֭0v6c_XV뾳}=$xvņTȾbO ;?N֏ ,#bҺVힾa?rb;}G)>ð {x1)QM�m�X㸅ȣc7RՙݵwۍF=UsRպ\RfAd'dPYcBA{hۊQK,Uw ^4mu gxš? D?|p{jn+Aݥң"ę7Ej:"v"7[Q$[>S 7;<Qdnef&NJ[DVҡ5r=gUw8(B�J3{9Πsuwo!!|_mTEQkWM%i݈{1:O;̴LVAOE;747LE?!һ$}MaR4͕zWd'~ 3C?~ՖSv[&-Nn䃼@jie5{左[F׽Ts UIȧFr):]JZY4%P!M?WșhϏ$ءaSzGQ4cQ˚]WV?X[t8 4"Se =y<#0lZp\7.E{:pU"U^hzzIǶH�aITX>oxYPb'yq)F~Oi7&lT?ˮge(l~90qV9]\|>\*Zdxv]W}[?+gM)e Pjo}q}G.A�j`{ƴ5=G3WC*IDzZ3+<pʊ-o6`0reNcBč O[%,qe9E^Y&DYug46AѓC.!1wՆO3dz8{Μo�wzB>W- u˳m7fHqw0LgJ+hR7RI�[<]6C3WILggdgltyͱJR%5j0[0r'm>8i(s>{meǏlp|in|;ԙvgn]I0S? !0j)n-R}E:/!#G㨛U9:o۴?5f>b?^\sNMܥb=!ڌ8wnc\6΂'2,Uϼr`}Ʀk^%]q[9NJ [x;N&"- 5z.6B<{5B޾K~'\}BЄeG4lz}]g$-!JXo*T2.?`gl`)V !d~oѣnW?wݑH ]@ O7}oz]y)1X R|[727r4UE]zaEi-U'U7yYhc-b0kx'8tx.Dѳkx%{@! f njuɁby蕋Iv|Ho J8 3$%ͽl˾&wIbpa[rfR cG(]S6!bs~P^Ξ}<ѐ&A$㰓[v²s�&>'+Su oR!Oωm") gK[A!ţըC~moC| [P輱:Rǯ.n"cd67wK6Ù_'Sp|,F|a.2))9 \++ĺ| ,"bBnUh<gY 9q\nn>ME3ƢQ/~;XT悔 MqwQ,;[П!%7QM9J0XHtvdK.8JpS\dYiہQļ J)N|[!=͚QbY%F~=Q?cґF՛^gl᦭*Ҫd_-Ei;·'Mc]L]ecgz z 6R kSHXܕj^TQ J̐e4>c V/cbje`rbqؙaΌ O`kn_EkV2BDKW i7Y͎rK%ȑ<Q�Tn{_=+V4]7O9kc-gGzK/'6ƲF=K znHH`ָ@x:gV_y"h;L㪀)xk\(uƛ[ƻG uɡrկn]7Y!D Mc$ƣ8?(ۏ ҩثǥ8Sy n鰃a1y[[3e}s<i[eB%{h W%-([AME_,3摍Y3\e| ,E �٥gϺX'wk+ M}vвY!Rlv] 'k%z9 {3qF_4P#D-)̫G\t@?`#>/ɷkhԵW{|Czn,)v_-vw<r3#&ϕՕv ->ı{ e yѼ5OR d;, ]k<zlK}RUJ7r-Uv?Lw>A\8]vn>&אY8Ca"r7q֚啢s;<5 Ll@.Or%Ռǣ==+䂓6sS/n2~ }URڈV0fo0pj22fm˨@.g^pdt,Pb쎆DY0g+*mռ?sngS~)nFXN`fLe鳨N}t2m `^uyu'cS]0 `%O)Ĕ J(RK0)a䫌  "MO-5Y@+횃-aF $O8fh1*N>niȩ.38Ep:Z=g\P_kn+:Xh߄oqʑxXv:#-"]SY 4{r#}1E(BuY0ՊcyOB4/rky8H»rCo 27n'EPf^X|;8Ԃ&Q`YKFY4@F3nfyXܤE)b /c=u1r5|!*x]m:1LJukgsC:!a\ ݅xVfO^z3z:G/NT+t kNQg7ʯ62OWNm7w|PlU((?=$F_d2R^_EU\UE"||wp_*IA؅ӊ)AĨq\ݱD?jTI?"+!r S ;/B،1ПKfv#{POlduk"'r OP5KֺAyY9XbiD*NQz)hrM�3Sv�{COEW=U#sSc/$.gK!Aj Cb%\cV 1B&m.T 2@"fUR_B>kqQy'E w؋,%t=/齗AA]ޣߑRFɓfab<Șp[Ci$q6qnyQ 7(%CYFXfr9bR3ȓPW@яPHVrJU͋7p,lk_*Oh}'yIk|N�-LKR}şua sjR8Ė8w_noUmNf S`{*js,W|ƩI)i"flvX=5S]j}1w,oPN5b* ]*"KzKM%)։u.MCI.LDb#P3pAk˪kSE]u.z_|>M`qX>u"9=zڳaz s}%p^5`,hoN~Jxd~;B jwgTFCVclSd,iRоTsIXa-s*:EG-t>ğJX"[ss=d_SK hǧ'y~{j2K` ÍexlTI&yʞZԁ~᪸ nUmV}BWQ9<N6%fY#=V[A@6Ym)DWsjDy}lҲ}8ÝӜkV9~)30P{P#{E$kߓk}_.'B ?c_WAض]<^: }ږ5N(̠3nϯlE�ov<yF;%߬Z?]Ҩ6h~coe䕎KwS?ֺL-kw_lŸ{6+(^>MD`Ͼqn /ο`i$TעKr3ݬk-=mxA] Hb`#b\ ^y)Dgw06|bNmP`f&2E%{ E{S0d3)Fy!Pש݆mO/O&h@*-.>͍$lmKPYg5PCk-Ǧ *\Z&_&FLX?o-X=8~8 .+"=`Yδߜ7W@Ce+37q㼮Tw;?Fz0| /|;ܘ:o) Ds =K-a鴨\gWE <o4'Au@ Uw[%R*:f aܱ@4H֥U#Ov5*?$ht*cҦ [O[M;/:׈/؊ ˍ+aFʍm85w8dSiMfT]!IX6Jx#DWGjoL6Sssf#S"  R{:DqYd_(0WT6TvAa#I5D1H_mʎAƇటk߁w鄍XDg]?K endstream endobj 343 0 obj << /Length1 1144 /Length2 1528 /Length3 0 /Length 2250 /Filter /FlateDecode >> stream xuSy<Tk'EY%7[' 302Ⱦ a3f.JR]BQJb(*V9~<p!3:B K|HH@^-POS44 \% M!3MMX�%bmxZ| DD‡"X A>Qa"AXHx\dDg"B+1+|&WY#�]AĆ#t rt&TA>Z4s:¢gBvP#X4L,SB ]3i̜!>@͝[q?,fδ6Ptw'alPX��p+c62@gH4Lx`Ѹp;џb B;E`B !@5|SGa5 V k�u^(o>H0fn_T06x)"o1WB;B�lľ  îWALd3Ep?5wO-47˝dq�\�xӽsiiWsYw! 10uL 2)5,fμ87 `px.1"`P @7C0sN0aB0 Q̯4xf.=eςAp+P/AIg'ϐc0nYXm,Zn+t^fD6r)m`9o9L{c" j湥i0=gCT~Ф5EkcϝWFWO;T&#񺓛Qz|%1͏(u#%[҅S.x^Ѡ[ꨂJvU}E*&6޼d(۴dzt̬]ӣ뫻5S^ّX}Dkm60dx0t~zli^Kɚv󶞆{k'֩#%ILf=?x$6wjVurhu(237k<]iu4Mтָ'" ^&?S^PZo#fn=q-ޞ'IS 6Ɖg'v5+:+E-%F#/7삯O$1w_H\W8PAݓҨ@BT9>2hZJ?U7[qf*L&\꺪#oXl-Aih\Fѹw)}ʭDءx5{b 2+: M%w:~uxe[ؤ=j*/ާ z:V]q[e"Y)sa@&YDtd[~Lwp[:eMY1uX|ƹڪ~9qluL,a$+o[{$mr>[4|x~p7>Qi\XZT< 0\8e@<2}llDUޭ\Q=D-)p#1ve9k|U\3)J)}AؾގWuЉ<گ4kli3[}!FW7=81&A[%E R9etI犓%?Hd)g֍{}:drވ>~s@ҞhReQ? {#nq69WxKKԇn7r겜p=*VmI.xu$ #c|?M>ՙe:Y`{Yt2C eͺiۍ{6i8U捞5 K֭^]%+ ڍ#VE\~E"Pk~%lLs+ęyoj UVHF`iͶ8QO 6kKZ$M sSC] ąhv~B1Ja:`:>LcKRa-4&w([nR(UK}5*a㧬'R4>o R:`4V̷(2語rnxjo \s͓T҅ اPPhy`#qRãvEjA fR[SiNuC%eNy՝թsG9޷h{cdE>!Gm,)hi|-M7Q21dՈDZêhEm 쩒\h endstream endobj 345 0 obj << /Length1 1626 /Length2 11920 /Length3 0 /Length 12756 /Filter /FlateDecode >> stream xڭveTۖ-E RU{@.q~5Ʒ5ךkQ4T,bs4.P9:C!,J@K΃JC#4sAf.@A �pr8Pi�@O_!�sf:�7=�B5@ `$TTe�2Z� dfPu5Y�A@3`q�-A% 08C4�:9_ gu.�lajWoBP'këLl�^JJ_An�5bWK^a^.f 3W-s  7| uM'f t'uo{ ;8\V5-\^k[l `>7kg^IYB�K*2$2O-K{Z^u�^30(zh\3+_u`+WH֯ry�-UA.6�+3ym[A`૮/>M/x-TgVUc׿U_E?ZQX/qqz^ %Y 0x훝ϓѿH- uU/ky{@BmJzKIhaf^N@?%|C2nL!i[qg= xMKЙJǴf\[Ɍ:>/戚q#X3 C�[N�k(Eru08Uiozz;I}A2#K<itYkxt/JYLS*_eOgf*eݜpPJ2,p] Z?U-3KSFUxq]6{:V(UVDa݁q�BP;%ɓ8Xmǣ<m' zy�+(*pp8e["|F-U<J4JMP1K`ɋtm?> IR(.aP7|\p ]wA%[OT1^Zu|01,L"43AJf^#$;*4"Mwv|'d%P̰v0<(qp5<]E[zU"]H 'T3"ڱ6\֕"gDhNI1NzdPWcS'^&d={bm-%IYKLL ;Ys޸f:6[tG>,΀֯ac.}2b+׭d7J$z?֤E0<[Aˆ .kV9Y4Hn~B-}H?Q-"׾ugr"m;,<騈=ޖT#c{Ѧvq%oB6+ Ok dXx5#HO$Ā~.WQ1zoŘv2;KXCǛSĶ"E2J1;mj"!19lqv +@,Y-&O#_koⷱ]yE>adD[\4ܬڃLa]wBD:%bi-aD%P0 :&N8׾/FCõ3.w++U2Ue"ݿ]Im[ݸٿlP b=scBw |E-QL,>"ac4,=venSMB]>tVeI&I),qc3wkC暴"C*{qNNgEJ++pWSݖL`[>$(}7D bK5 O1QOY^Jca'N?ӉjQ[((Q^NKĖE|Co$P=,:liS3>�C%s~ΐcɺ_!G2 |<R7'qm%UlA_'DĦS7kygB4k)%u0'T9\Nv܄<=x6nlL3,ONΏ?i|y;[+|2n^YPL4̫3nb|,^sJpi{ l1OIJ9濙Ŕ%J}\_yE 5h&mҾr GI u$5QR?c&˳l'W vR I˰Uζ&<Z1' TZxXF{DIJG;|4]b3줅Bh$D?̗إOy 2[(ƾ:6eeGg1IHQ-EB]0.N"`hhX~YI_+x%57"[SIϙoer[:jX9< ,6k~\ehݼi ,Š^r_!kD#s�uoj .LIG! KX)8ѓYmf :"K}kOD<@mdDѷoƨ"N*QTO?8q7_w~z꥗JD(2i= u"}쭇̳3y j}1rZIctpu)gt#Q6dȣ0$e.XMF <$ nA,<p}v2XLXݦxا] f$cɴ JʇI[X= ~*u%qdԝ}.<u~`9YiE&vO͚~!A@n88A߸2q<@s>su!120GE(kYi!.dQL ﴭ,&C0E`,9{&GP4-x[DQ^47#gEq`Lv,u*Pnb25혾k^/Y5Z_è vgÀ v]ᾴ2, >Е'f@=M ѷc]$ڄuLP͹#޳|cfe/VQLSbc榅 D|obTX <=rٵcG`шF>jؤ)2-dS "DnDGV^tZ]f1ۤ|~`آ gc/"_>p灛b/+,R˟bJѪKK~P^! |߆rdfr7?}q6#0 iA<dltbI ;CfthhRPe43%S#FC׹%\=!wyə]EBLi!A/I47E xK׹ m MWmqGe #Q]fcǼ^d݄l)MBZV_tT<xl/Eݔ"P(6tL֣ S>XQ׍(_k c d (5GHb*5"߽0ʗ9Əg]6`D^=[UHۤ5IƙMׂE^u.1VZI0Nu03>O +.ݞ_t MRz9#Qb])5uGX#N3mĈ:`'t{ޯ&8()!i Jc}!B:&E@ct~X>fHv)rk#:L l/EogYרK,0QۻցMj\leW~! ?> ^7?* 6ŢcTNH֓9C>Zī~+SN;~K>I6MXZ,.R3;?u?Ri'<ј7a6Do_;! )әai(҅i\ U}F;(3&?s*0s*/&nҖvOuh@m~1<,aOR/z_-Oj;O̸Ss{7kTm% lZ"4;uUx% ό G0陕2P%c6քDyh"ӷ!M)Pd*@/眰,Y/fxoEAp ,~.al8/A"™Me]pm >S 'rz20E#sF~1챓zyf@ Mh4b@1#md=P,:R= k5}%Yx%{ nkq3M@HIvF=aGY*VR/3R~r[LL9\,?)]W_נfM�nqb6nItwڸ~ů޽E0wKvޕdւ!wr'X|WOR7G7/1=, kyīL!~;ٿ $i,LxgBoIN!e1)Km T< I3hGJGvIoӦPx?9Tw\]S{i%O>j ;+ 5*HJLB@t*ǽ/jBc$lO|* s*XWbCfb^0TKA@.IEdum̒ٮq"GC F4S) U&Z>d�ʆN:t+6A N`| k@fiB“Nn9{ϸl0;Ihb~V׷_bVh* ,544_S/P|i` m*/@F8{CEݑ tws<Aѝ]~TvO]нf8rh=DY|8nݮさ}UA΋S=uϱ~)cu Ep}n`B[;:GtАW#L,M9|wա BAN@^RfX)o;RFղ8@PJM<mR,U481MsD5u$umVpRNJ-XmNxSWO (*Q�w(pb*ߎbUp #(IRވ}FM(XYU: M3Tao{ RLǯ=Ռ7),#1>?F' oa%iwVi ޼e/> ^$'&MyUn&'D0&WȦ0|u05L)>FJv,c리?\<�/"׊S@!+C{rgM!�W :piOkR7]$UھH q`p~v{ kKH!Z;%1z@Oq&X6o6DA9ۋFvET"b1Viȟ.C j*ǩf*L1kXZǝrw!Pʹ"|tV=J~=KL/d.Ij R:Tű1( =<AU_Ua(=F%.gf Pz܊`IVgz @0ži KڜU9\N# zJDGPћ) xnE+JρH6UOpĕcogelE廡p*6~'haÙȁmYNZ'w]]3nӷqz5©R~EPs^3i@uwfѽMqdI-='1k759,٩gF{ H6,B'\񱒴8L9Ñ\b4Wrc`fwPsupo`܈e[\L#=1+3׎hM# 8 aěأD Hq `(1f+ |,hlRCc-nѺά>|4UvKǻa7\I&[$5uB;H[|ep,@ItʶrWf䌽sDl?OCȔPSV@ztq&ENxeP]?jko,W+⅒1Z@"°ʼn)9Koqw:|Y[(9ʆe{(0: ᕋ4UD[XbIG;#q潴U/Z<Ц9:xJ;sB۴/YvKJ^z{uZx=+w.rċd1;oS%8u~!& ?yz:e0){OC"WRl2;Iҡ3PPC> 3>=tK+$A)>oQ~Lb \?�4f\08�N{3ڢȨ4G lI]ؾcvPs?|nK`Po0^v $ =oM3VzPg.SE?wym&sZr3%Tj7 _J/e+$Zjʞf7fRcXG&xW%l-0ߐ I ME_!R$udh%,HW/D=éviCR2,hJ8e4Sۜ/~#zt0y%\^<aBȔW&o^ }6Lz(B9i/29G WnC}6Y诇ό7nfҋ7@Hz{"9T}b3qR(hsO)E]|'<>w%U9Y&0vD8Dew)(ee*WˁHH3)g}1z|bb z[DW录$AeATq5Tɇuڰc mr7]e5ZTV@fh= )%+y ןrMhmC΁$AJݩ1gjj4rtK!RӨ4$F5pӾJB8v&uhQ y05"8ckg0#!}X>zBk\bZmDǚWn)e{C!DFye78vyBu5ot?Z<[^ PUGIc%>L}EN|_�?}k҂�z)27" 3L(I0q<R r9Gr#|"TKU_bi}SF,b,/* OM_0)!p.tF"͕5SS}FgIAZ(w?ZkǓn9h]&hulo+Jqi=e <Dc0c!d].zc&Yc7qn&Zl݈+\0a2)bh]}#S4/U7P!Yme1a|2C<^m lcGil?2ڴpţsٯR$&=&{a$mg Tc| my.fv&aD[?icK}?&36O,֨Gn uY%dB�SSQ8Ё!m5:=1<Lۙ{K'M/fDuU=AZPCգ1gV2,H)=2@sVMAähB[t̲p&? .JwVmpS{_!Rp#V>WeO^N𲷂c/ aM.=;dRꓒVjqALq98(G. okG172Э)?eeSɽGmH"i](t{z@vU %p*_!TR~$t� ݀b?1Xh;Q!lxmf]@q<_?EVgUxž3~*v%mh>MC7XK@Vۨp̬eW>;Pd N;X-"O#W@;d^WedSjπjk^Gsw8/T~G h סp=_:ɒ Q }E߮_T?HX1ܙT)t(ӔkScsIbڸds<J[P<}ӹX=#FśΫ`DtOʛ-Ȗ*O>F?u6ޕe%xOCRu|-K YXeo~F۞<96ZjQM% \ Q3MLƹ9 o7GuvǀXc%r;0èE\=zy6$7i#S tĤk|f6Ek)Ҕ* tFrSVlg˃y(?#q"L憗a&Yy 7̦"K盫ä1M"^ttC%`3f!ǡUxF<GOQ b:$P55ԩ+/O/%o۾ǔl|oe|+0[& 2#ڟ4 ^r8"_,H62T*)OgPÒ@Fۺ<;T~9܋xʷ=j usWOVf_+9nj&Rz '0:PtGMM7)9SF7Z]Fbx9Y\:/Y]æJ|Fޛ T}XƘmR5)F3C~ n.<PWW6b\)V2t~E^)^2ܮl9:/:ao !)K(RSbBsD[�+6뎟 *jz僽T?T0w02$oFV(]'f"өo~ N.N9+η1I{l"k %2$saypLnVڟYȉI48<]^WOb,s"vҤI•.bƻ{=5وz[+N(|UzR/^]#\9F^%}"ڨ3)eʻ.3G; [r0{o`> /[jjH!h *�O/$y5OaڡjI3] `O6}cs:.ޢ:/ ڠ<}]5 'mR[`5lv~IA͇!֋r6u7FY#'X7i0'%C3;~ +iKz{>iG9IxZDS.U]�jNn?#O`F\E;Lr]Os24V�`O'<gGSܢ=ʒ0qdpYm!\@ډ -ѝ)((@ 'OPp2SeW#D`9bpg[['QQ13DIU۽&)(%$iC /TR<~xuda;Ic0:"<LɩN`Gf`kOhaZLV~ΐHA<4\u D@Ew5'kOQ HaL. mz<`ȼ)/65/u. b =r;ooZ3\ kNY0GN v޾ E$2<? 7PJ0y4 4CAy6jsfF}kʝMp$baϦjkBTfո_f3nר0Ɖ:t;~nwh:jS02[/lR፲*;0SmlJ1!N YF6WQһVq)a8k7wȮOw`|Nf4jFKrK)/6ZJM4~QCY Yξ6RR'qsq}pcޏtZ!l99) ~U˚8 e¾MsoCVʦ)"]6 LR{Xu:t`aNrTõSB.Oţfk|O٧cqQ@ɖ}wEzEpa]al63̮b35rCr{Fqyjs w%$1hԝL"_WaOErkoÚ `TL^Qӫ˯R0dTvSΆ`7"vmKƼO)YHtm҆ KH$~gjk NWUȔr _ާAC^9cZ:;ffy$+kN Mݾ)gO+a�3E8 =44OM̿xG6Z<ÈԴ8wkնOg}:9_׹i;W9Kkbpuu)k3Pf7/Ch?\ŗ49OH8ck*i B\%ZϫxЩlZeE`^1}Rj7-r--l�9g4J# vBؠIƨp$ dm~+ ł {0qq!97KEQ`OY:bdޭcllDH=qnfͦ٢*S7;miM^/* ՟Oîsc0H)g'0K;Z5)36I[e(I}%FW6%]e-#o<{+++ 4{ 2M0F;"{aPdXh9V%He!@4='&ӡDZ] fVo Q-~/o?.@H kPMl)REYrOxXܥ8RKs ~<Ng\֕MlCD諙GVGub-XPܨ h? 8j†N~.A\y ʨȺ±}HY@L=<}# w7{pw/t鉲D"%Ɯ>f-TLn8V =1[Q L+JumxfieF]3X~]RRSKAA0!jL|<{= HٟI{#HA?+1&3tC|Ԩ~*@OڔF/n?m6*Rv0 S0-h-w\!s" &9I}]1n lJD2u nt]cGKIe#FBXP(vb]HO_`,r;"IWYFkdpzfPEI0J*̂~HMpі᝚kyݯ|7Ñ5HK2N./ &i>MJR)pI0˦G}GpPʒ'yF;5>_E9 -0J&jƞ(}ߙ:ﱉIB0O:qy~[yQT0/ B^"Jh:=ps RNDޕ@ ¨#>SN\f"jά[徳k0'B<YMӍwՒ8i,Y 6"omEryk>/5;cG~HcIġNyv@Bg ^HViEՂ (..i&k|+W^ sоywgINI͏lPuSpK!9QQ$X]|Ϙ寜BI4 1x,>^յt#hdb*liK,8aڿrZ)3, Ӄ<Cn'Ƿ1a|£1 y 8v>/]O]a]Ғ"nMwv2TЖteOuLPA0R<kr0/r@ZiKKfRtAkJD5ʄ&tToT(fdwG}CS3'àYܟk//ߙV?\<-B͒Ƅa< pim&b QD2wSt~{[w}nn0xibŢo4+?rIq+ t̋Ɛk f[K !ʺZJ)9-3QQ*ډ>}ӇP;XG3 oya$ROkyP!�MQ- endstream endobj 347 0 obj << /Length1 1630 /Length2 18906 /Length3 0 /Length 19754 /Filter /FlateDecode >> stream xڬctem&bةضmܱm۶*FTl۶ <ۧOu>?랸&yϵ6 1P΅ oiklo+o%K 4w|`DF.vF.@n 403`�"N.�J5e *c0͗ hc` svT.@  %%/WH�NF6�EWcK H0w�034g/,!ghb0:8�l-�s'#;,Ll\MIKnf,lt_`.&N.Ζ_jٗ?%Ku1s�=\e Z:;y~spWΖv- hndjtv;Y'_9X8mab|6egVL: Q33T_IxLf ._!w,@ -_9_.}66F_Z2Fv=hl>F6'jw0)ٙQHoT`fdճLN6v/nV�#ѩZXXCۿU@;Z]ʟALNNCE`e5._jMaa{7;'}% / <8Yz�tfdW'#fgbo訸ٙ~M6qur"_ 8k@ ʢ OUFvKVи^oC卪%Eu=\ՆoM ý6=iB|_" N r3y-pmvFq%e7HN''��tGD?]M ŧgO#{n haxROS\< M> ^8krZ5Pܽ.>m1e^ 1 .˸_1$LJZ:[f_=ϝag w(AR¶N)(90h*ȉ7WL1Z!`*eG4VŗV5G"7XC{G^FAA-0곘GWJ8]^\&L ١(_(2{d,8(MD1Rdw>w=:3؂ ~7ɔ@{6iIv_Tj#65DnWteDdX1w F<PG@ mN4a~x:u~>Fق/˾MCSKv_/hw&'s؃qs^H(ȯ E 5SИ>`Qbl=lRG:KY#CzQ6Gi(K#CS+aJR9/T6W60 ㇳ:G:C""^YND;D%2AHޮ $ix[c]A%%&Nm9 R2|_-舼^މl^ہaner|;\Vo.'=f]?)麦&ܲզ wӉƣ\umSƷo؊`Fq>ߍ850ADJ7%hɑ*T&jJr|&k՘8y;__%β &@zY0O}f}§]fn`aI[1�L0*)f  &5wZ̔=lU1{\M<"C~x m떸q]^\GM 4[킜-Wx7d6ۭ, r(h{FGL`yYfl*v4ee2/iB7t՞N^)|q.^[!]y} ͕XG~6Ș8 �¿gYg^J6\6?wG2*�?^ i7Vk",Z{MdJG슑 kƒ4.'6lTzۢCLʞC2Tvz7&񄢵ȿz##KRË/wRꁞDM<_[7&X3Mš$e#\}N ?.E+[>8JZxTJ1][x On@p5*xM45wE[c>bQU; 1$J.0;WxvՖwk1#f4=O8t"K#آ^qufܼ߈CƷN&2rnNg!ti5h8zPdber0i*` ܹ#T|൅]0o-̟0[I0" �{zي׉/6?Kl1un{X V]囒twš$Qhl*zvB J V~fĐv΀xob9<<.%4aJƦ"TՄ2b޳MEvAʘFZB5\5ب!)F{ <%: Ȫnvfnr?VP,ߟ+uJ_,{#UiBOL=5X IåY*[>;'3#/O I̋D;2P ޭK/{j#Hmw憱ҷYlA\ HKD'T e5K-|Qr6 | /!Y}5!sg&4W*ăq; * !<W<|c* k dIl7la*�IIK؅DV7ֿ~ad +LC#侇<x%ZK@Tnׄ5#�Rwda;=E5%Qc/{ȜzQ#m_&@\E"VZ)YZwjr%/oUQQs=*s-59deHǓWymG/0-חVW_c߼[h0Zֳ%-ڻQL43xEoPAYzn (mX8i {(ͻZ):@L \V{ىx|S�!ҝ"EuS=mg,5s2N&HdnVѿ?BzM^0E.sNZyoܕgs,ftmMIjّu~T/H/M̺E,~XfqyAM6]o/8'z:qԲo۶\Dۛuv\Y~ hxm]BAX?9~y*w`WI~ ]? A(C5 sb%)F||ؤҾP)q.|CID #@81NW`ZQ*TU V;J\zD"_&ے')ADm,UgF_:c/rHPPJq_ؤ5}\UI=\%8!QVjN!1)CH֛@|Va/ zG;i!CA&/xnLlo^bt[MxFB#̸bV:*1l < >OjbΡyD@2*3]=%Q򖤥o#ƱezY'o9fYLPl`aJUcqn%aҦzJF:@xr!�%m Rkqqw^1ثP*4bmp %~Qk;g2bZ/P1#<fvC>GC ;HFH]*t^  +e~J4 a!1ΖD!zzsu]f+A $>(9YyU |1RhT3@`4%<I<t>+@G!vq#om%Sގ_ i6IU:S/7‘c 6Sjk6ݕa)]k廓l/}ud2bW]l"!$˙d6Ǣ?Ln!,20F\ʞDTxK#m,lˡcwl]{)~>ע 34to;AL 4p|6t b+#[d3\ ']5m4ӇRn[>Փ2{4^<viE\Ce#P/kj1^{W+^2PpmbBR{8'1o/\-1?k, .1oxtg^ԜTz6*$-:�m8RLlbCfV$'W}O~H>R8$ ł-_satlky'4ٕ!<zZ;7ԟ֋63*P>UK <"jRL,Lm:n= M`bqwI6VD'?+o_"Rpx�RLqu%hX}Blf~:L9/M^7lJ,)؇-4@y$7Sazd4ke4%$qcQL<2dU_Ku:1/p8 L}!g߻!:zEl?Y� Fؚ_#cBJ%Zʡo1Eυ)tlA3ICkbCbL|"VE%Qa |c%]y #o2(�(b1D}Tq=ˈ; pGg0:cρp)l֡7=]gBӮC,-!JWV`RmAvYż>u?A%FoS+瘷=U7wu=_E-ݎ%@']UI0BE8a>B|Q}YXQd~uy~b>q[: ;d.)08bHT 05^%j!BhD`[%!&Bf\53E)7 {NAΧϗ!9qȆ.8@C_D/#D1q+fWkS</pC'bEr&uŨc>)h0Cf$PgF`r_ s0b`#vPv!I>C,>>z됯Z:3$1g7Ve·@Kh]DQ1fzclIjx %C GK)^r>D<(caGg6Xo*;JjBuVٝdT,Q8ꯉ4`Dy9G` N?&t *Z2 'w.<# vv8ĮVOHG!s)@=\;hsa~[²p=VLeQٹ@&獩OJLyrXE޾_}v<  'eT]TF('gBF3 :vIpķ2^ m5Ixvx7K aZË3Xwi�P NV(C臻lo6FʊEŸ ]h53A<OPKp~ߏ?pjBoR1;LPa+*_THNH{56 aeUɲ\tZph6YE^lשg%aS[p`m1/07<|̕ n:X=eI=}nFg. w*1qs˨c@1~|gܺZWW}υQs~M, M@Hh4ۀDUvԲj/\,n}IPSӣoXrJ$IxL])ʑ>�R42$Q iFfG\YEͺ cԲ HO8n3}YhlF\Va8;nrT{--.6㪨wp2 3唥|o)~T:y׼?& F9TQ!yѺsh:WwYshJҏAh <*1HO9ru0 v`Ӈ~5U"p:{(;̣_g[Uq-MGKQALF%5 =g=SW`?v˲+T9鿗zѼ@1}.庺(QCjQV0tU-rXGYJs" Bc#uWаt}z+Nyg؞=nNU훢^#*Wד,^9;ߜS`UK|cU|&>LT Xcf 0H8ĩr\gZm3@ggB.(,JA]S ykf *<777B#G9i*n֐S"g$]<pp=DDܱ?tz:n _39[_R˫\CG$UT'THb[AD/k"&l\mba e{Vnq+rǶx&P Py0 ;~|qU5c#N/#X ~cz'#e o$}ׇ-0 sV#+xp|sPp쯻"m`mDj.ƩvAܽE3):_T<Jm^^?_^QF04bF S,M c^ ؗ c <=GVGc$0ü&̮PHB,dizh=zmv*9N"{{=b48EDrkononmB5y a%WD7|flť+&e:re᯿>ߕ4x@Kv|FX~esgYj?M+;i@>zCd$'*^7i!Ke>�V. zgN1jz0nPaFlӽ |+oGV䮈48],!t乷T VGrT0[17m@:{TUۓ 1xT2GVH.Xl <\-kHv=gq5z[W%őF`XkG(' ȋQ:DF9ԋyQiC@0TU>oH2a朧 ߓQ.!dfXl80Qz˪Ѥ{3َ >c f .7Ɏ$x=CP7~^ZӸZ4v9=}mr_;VcR jh4x9u/I!1@c}麗3[KE٘=RI)#u2gDh|+.WH1vY]D:'0zފ}:^dtcY!.5{RyU�=azsc<GrSmAa#R2-Q|z<$"wOt8.jI0TI ANӘSYV1eU1 (T[fʔycr{O*X\ڏT@\Rxa3LT whd[|K ?Y0v{n9aNMA'ȼ~{m3d/j7l ^ Y" cq} Z8nhDt@)zCue_~ 'ccf^,Qu޹hnAE6h-CZҙőɭhjU.^u:H{Mƚ؄Fe .x 865[RBJ1.KP'(%ʆIى!{L 5W{y{Dc2*{ ѷ/*P2O+eu2`3玽j%u{L]Љ|@tƞ覰Ho-fGD`ZRgrk|w^$73E܌8g`P{>4Rf38߀ eQܣE(`]: ^!XIQ,HbӴ% OT1TׁB"(l)>Kr�X8#m<#6A; ؈3UY8 &9f1i\pƭ[55~-olݤkT� H Ӏa_pCY[[[&;ߟ:JHk W æJ?ylE;4Lĺ)RΟCSL'yhYUhv }vlPRGʼnvw4ϛb̓'aC0Ǐ9ӡ/;NuRN~ߋ*̀j0*$|` 2$mX]^ R`&'T}¨bR+]YG.G^\\ P$\Rd॰.yLTEsNgI;_٭Rl8ވck?ZGEu5xlaL#YwLȷzbY8%F/&G l$~s[ghz!Ut~cd <Qɼc, $q7+DRXTpA_Y@'Q>0S7hU}l!~[ `Gzra깪h@YGoq QZn潗Pbu˫ЛX#%:vkp0kdȮ}6vYEH~xȒ* P@)L5z Bk7YeWD8?LX +HO?k|WnhMIz/`4kdžp6^-q˸iWf/)Fdf)iHܒ.NM|ޟm뻉ǵ^#c$70v( ޟ<?_uơ*O ERQQ$0Sey~vZH]j8{E(#GU(: ~>)'<Q\3UjKHn WvS`XMj\ _,)/P Yw+M']+h}x⮝O0ڧYwbU*pq"gz}3qU]B,I4Z HE'oʤVt/A3gҴx6LXweـQc-7GYKT@V,1 G3_0x<V \b̖jȢ 0]w&$q cYOphKU1"k(?KucGqpGG;8,wKcK Fͩ^ K(Mm]YGp[ޓS=$\z„"GDWKC1oUK̬/r/$ڂJɒUM{7|hT{UhJgM<ց&=~G+ ep#fSHA;;{ZPy-_WIm7Fܢ%y"nܕ!wJoWVNv3oXE�;N[?T`L @TՙFo\ޅƷX#Kߢe0 Mf۹{Ө68*̕Dj]e~;Ǿ/pr5pGx;[Ǖ#s5IgRcyH)QȦk u \>ȅ2엞e9lVꥴ&gP }w<Ivom>3{B*cb_ @~%W=HXwySWP/,fwS`YkeBE*ACIJ#,nLChܲ~bE{9E¬s|Fr3ޕ0A9i3KIfas3ֿGȐH7 QBl?d ��eB'Cyh}ɥR\irxLrL!3$閠͟~혯.S<-XL`RNK+mJ|KZp}O$|m%9^C7ah\-ɾs{1Ƥ3N �mJ-QR>7:i²{�p 5tt3Y fd҂_Ma_C>l!3;cvbX_TA"qĆۍK|ĪNE=!~;@M�F`!:"$u[0N1�Y]G%W፷'kU9Z.x [(t|p|sn--"k1?ݔlּv2!eY;[.Vc $Gߛܮ_ 0WZj[f+#rSQP1*f-(Zg>㞑rYFj` M}aV�#t$_n/v䎽`c<S0Es1尓ޮUd8 y6/nGxa2̒`3 tPo@&9mg16{KfoarɪT-(+ukլ,k皊n#x;aƉ˩1ˮJ/0i&8߫MR08ٹalyysA?-JhxÔ,�;;B^>{ԯT7*!]#Ὢyk{2VB/D=E!LЗw[(rCr|ghs G؇[9&ROH_s\}ċGH δũfS}cd:qL1 &+ ?uYpа+@ N9#gp.J6łD4Ru|;xyșdͤ1 8R_3¦=#+mCQgTcK:[r˗`H_Tbedg&)%C']6ۯ qN5n;uh`!8*}c gV g %]{;nF3�b|9 9FmrXLBAY>3Yxd �kVuyrz83p?,TG1.Y܁ S*6Ƨ1EUW0_a\"�c%J-(2%Tt"d[h]|?treiu r%v!t B0]8FVk`]߇ W8,>xP9h-qIZhx�?9hڱYLbdF&<=z%U%6HW+S}Wl=?r%=0@ABjt  x|MI!khtL# ^ZEQC_;p{( H.jn(O^u-jJ-s,F[gj+Q5ԕ) , 0 +\#-A e2rȺ6@Vgs 'Z\q46 F4#uʽCL 7:Caǟ^jrř``]`#qp=$#�êf(z`-~2ΰOYEXѲTm[ jD0L1JB}S*^nUnkajf fFHdBXo)LV͋ TNH nJb؍}go 9Z!,ۉa8)ɴk_%ߠ Fda3]pT� B1ut2$d?38C]dfksB&h|6;dwqk(hd7]wT|t;k@#(\oW-LqH0tO* !;@MSQy`]mtU {HgriK/ jӟcW p0m>Qɵ|Eܝ)tsU01$G=4"P,V (yB: ptxGÍ %=N|-{O,#p8#3S!�ڵB{oW;H7ELe3Sj3GJز+N f^I Г&em\`Pvgedd`UTudsu$;i}U oBӪp _`_L(3cӖ(^ᩨ}=PPLf(=J6txQfYKSX&<P/^Eڴ¬bmy "AܜsA\no66}(2>o$Yƽ5)wBQME 4 I (lDH+(Pd`f F 0h6agĶB)^5gSkpq`~']U @yWKO_5O! ~Ї,$];g\Jܶ(>oa]U)GQKc| ܃v-u`Ș8Jue!,,gѾˡAVGϣ[`1o*Y>�wA@0jD#@q?lOh~wMZGfxF96T.8"{c6톘#EqTp8Pםc suX7/'jj}l٘Ǫ;e;tG $)׻v#80.+k޺xyfsD,-$e%?$S^DŽ0/RUd78RR9R+[ZZ4\"DҜ!{,d=xCK c.D.&o|(IJWgYؾT^=1HԔP}E"pl )F#HE&۳>z)璛zւ9c@yez3qNSKx]:W1\7_C -h*z_Wޤc؉gkz(RWwmcVe-ZϹXz3fm;bD;6e3p!l=5~]z5qn 27RgS*ZoJ 286޴?{C,$<vw{WC?xk#`}V(&ϤklZj~K;REA(Zτ$o.})`1!/?QV0/�c1:)[ p PX}q/.T e#]rF/ȵc}Q¹9I3X.+5Fsyqf㽘۲SSt/ˇB*='RzmBĂ)K[)s}3蒪WwxASV7~Rz»P4ZTJ_q O +ܫ_${%�Qh\K\߰'7̕!#d#>Զ4 H l܃M W+3Co) euaxl* 7W��<rA[epٌQôo@pft(!'`L'd&¢ ĚnַI`7({nG\\+/$8o0PW)IYJpr$ƛy}; u#u9r6CиȼFRMJi 4&9\VAuIh!Wr&p̧0s0^Oٵ)fڛ KY+d'Qdʚa[1?GЍI"OMT}iA7.DfI15{ _jHmgvִk+Ed5,qZT,ZS-8GN)zHQ[n9pfe`nހՑ@)`֬K#Lbœ(-u~oͩYM~O=; "Y$xkM?IgEZ| D4^G /٨K~\@8'[QQp0wNèS]&\bڇ:+J@3A>qvKA!φ7:[>ZPd֩b ^'GwR:@n'?zhCIx?R>}5PM5pC;)m OK, NbIsȦ'  QQ%Jy"# H@nzXBj,'ܚ$n ?QqU/Ox__yfP,'! 2[ȩc>?YД C�_rcv|~|BBsk¾f$\iDI{s`!(g(k W|3;`O-<ԛ3aF ɗ=7cŨG3#G~-_ѭʿ%rcVvO){U\*ō9|Ec$b"ITk{`{;  njsb+m3<֢:e%r&�DbEZQ%)Y}KUEɖX( խE}Iêx0MsN-N'_ D7w1fc['PŘ$ I%;yN@q`YyG 3zaRLo$k3G<KŭPnx{qcN Ij#IRNzD7_dB})T<Fq�/җRId}7H+Ix}qPOe9,j0߸]2zIJ\O8Q1k?k%"hRqUW5X?ߵes|'E;ZLv.1O0[4U25(zGz[)ZHDZL\Y|iQԩ+3ƺF{^C~!G+Ȣt~R0G]  ^=M*. mW>#XxϖE4/1"xR=ۭ :2h)oX55hĴ!Θ#>lnqYZc컑1ͤG m+&0|uFs'kB~G!g$)Asu6< aSkUTK&6TL[&%.p">Z>us!Lw#i4#3x^<٫xZ H+ieS[s@-x=`- ߒmF-p{CQ:r7|x3 hΣ.q}mjG2TΖy қj -'@)ۊ88Q@hXd|Mp%Nwl.~{^Jbҥ\,C t^A$"q;eLRfL@az$-luw!ʞF5v{qn_,,Ŷ1G~ &dD\IACHL;T&a4 6` Ï S1ì+W9h9AB;=,gjyaM 0$a)U >vݪ1̩ C݄QjX1<zNHx]OZP�ҳ g' ^Jk/H@m:~DX :p8ʐ'JvoS[jUmN2cXBQjfiw8Wh;% }&dI jw no6:T1|pp)g*qBy D b^Y dZ7Đt;p !mܼWޏ,>s1/:$  ?hy^0^9mv;:/d#( m�c-<Y:֜*!nEzLUёTt@W]RƿL Ez3O1_Lg¬{XqͿH%8&ڟM?vn\Zysr;Wkn g(Ȣy0I\qփͻ;~-]1\]Q[Tyg1[XOK{)Tgb^|XF%^OC]؀40-d"O9"ue拵SВhYwC-ˏ8f#樼bV,z\Xhp4!jH7 Dhp_.cCվCG;g@<" H1@A('$1hË/x{87%+[N<׿XZ4@B~s'X`ѳ8 ?HQD=Vu H^7cR׻7+'D#-SY Bcǹ'0]GXQ{~8JַTO}4{7Lj*#bTͻT8AX*F"/d>R ('Pe?`sS= 띝r(T=J`6X,T? >4*�TUIe.�' �)wC$h䀓\3U;6`FmC=s\MM+`KHDBB&swT%;8Rw<#4xݨ νl_ږ ' y") /CS^ }ruo8?Li˼�P##ׁw aOKϜ" F2uqR" QE MHe<=UCdg}O8p>pS  dJoeYx$UI@. _bsf36D[3.^)nW,J!ZP ?=^:_- 4;u܂q] ^{Y>6;!Vp_5F}*U83uG⫥ ݳ \c_"TBNaG;�EUa\vLLx Q.R:Ǵ PB ZS)P7|޼k<|vr*jҥtIgd:uՙB.AI9c]"FtQ6]37%ǒj8jߒxW$mCΝAի4ns,}uV/}Rf�.t 3ƯRMtoUFj&4o x|kUӊE�C5fFq˶w"$'_mq0sD-ՊOТØ2|,JF:"s S!;-Y3بf?kcF%o۾Ab#@6<A\Lu׹BoA87901N!13 bջ12RsSK8kj7ɻWh t`\DG)6so )3/7N+NFQxL,P8{ʇq7 W-F - Z{z-'7T1I,ׇFߠrƇl ~|i6@GޞI!?gn^e"k kՂ93_]3^B^P("1naOH#>Pz\r;*Ӣ-&3wJI>-Ƒ˚�t'tb9LՕYb;Q5rƂG2C!Z#Oiv-G+=)q/GTk%gJEԴ[9.vvz.7Yebe[q)WkU6+LX4Q frRqp Yx1qV�v]Bׯ-~ud4JkR(qHCcسqis%թAXuoi$bPg]g|u6?KwoQ]ߦ:-V!Fb$۝+dWi|Mx\ N\G`*wnパ s,S;Tl<z?^^1j"# "'R`4y]&rT.42bǕQObQ F;p6vnbZC&[P�lO2IPXHI8j�h`h*~gkHЌVt,dlvф85!7 cU:Ӵd u[cuDvH{H IU}[·Y>~*bNiϟF~ ?U P1�͸q`J:3e$o\TgdMc^-xDMѕ4cn. f!t>{K~w`x?CUQ"_v\jqnJ"P>4W3ni֨՜M6 mbjmilE:q."v5KY栩jׄ%x̮:2Kt _(paJ뻮!]Ă8TLG~bD iz$GfH(fa)dC{TCU '&%_#t?ر!<Yc0By{kq@ؗR5^GEj} ɧ�byT>Zb3Jz⥥O !r-�b U {e1sa-e{ż叆5C{2ܧ $! #u Ju|<]G1>~rƔنN3aN6U'%PdLuƠsy=a@߫E}'&lnFqTD/a8u^c|Kf젫,k. g9y0W%S OcX-nC@_ρz2 :;"iʽ)q8ĭϷ9~#!NECRހ/sxF0ϰS4-Oq-?kl;rIB|-|9 CmYdxm6&ъr}B^.@|qӓqG aRQ#(*KfB]5#LzDZKTh) a(u|JTBbf!X9c\k,Ri0#Vܑu"F^ @K3{ D'x|Nq^KBTl ҙi ҡT?5^Q_ȇCϺ2E-;#`|%[m"p endstream endobj 256 0 obj << /Type /ObjStm /N 100 /First 913 /Length 3495 /Filter /FlateDecode >> stream x[[s6~ׯc;8c&ә\]'M4iɃ,36Jrw@R[v[H߹�B PAɠB qgFG'!TFIf=   7@=z0 =�ϵ9q^sďH .`A0haC`0lq;aa�FU ao%-ފ2: a=$BD/lR9 #)&t处`"$E ʸp 3ךAx\@X2) )R �>PF]D 삓,D,6Exnҍ#AD 5"26& 'b�`(E" Q 4b`to3ڈ u4% lN9r("0eq 2 Cp+fEaH&j9ZBiJlOb xnzd"c0ⱠSSXKp nHV8z`$_vY p>_Gd#h<VO|HՓxg,ὫՙP)8CB~xü\N/Vx`agPO;x#!zکy~5/$trPϽ:{sVW:[PWmJ詴-Tk�-bא/ 0^|!zK:+eZNU6|Y}:?Z|YTT/A"= seHu ?ԦԶԮԾԅ/| PB/z ^(94u XB/zЋ^,b XB/zK^*R TB/5865Zڔږڕڗ::УB =*УB =*УB =]B&M?Ŀ8?QOLW[4Uưq |?J]0 �U!*`;l�DjA*BQPtQˑ܎+C�u8m1M H*"8*`;"Pa~9ۀ0}v q"y#g; iv` RuM D7I 8N )F@ I9L6*x7Lt@E>i1"R݉3'`j5F[vi"9"[%dLv ]P8 x+^3Xg*n`]`mB0�'֘a!3BXeyR4=h 7@~ Y=8`('64Pq)O Ja�ºSJ1 ҇ċV q0ӦVL& m C:.gb} 3$zD(X{VP1if<J[MNԎ7-GG^W0Z]�KMGм�1R`&XbLnf`t A#KMI3IZvIvQ]Ybh1r C}fa(~L&Fܚ7@(AȪAPC1!1h8}*;8 xe= bBދ[D3BernC2C!BNP V䍐 2:")G�Bc6R_=G׿r I+7j&mb?pEVyZw;4 rYro VjyoW)K:Ta]Ƽu얔6"-zK`bp 测qWfBu)9ȴi1?)}& [ 5N}UK{&>Ѻ2Ua%Ubǟ,y=/fF1MC޻$> d| *˻C" Dx8ٻ!N4;yݯnO8f؎w lrb;_"G ̟ ֚|Vj&nyk@i @ L;qsdTL-Y,.L@[p1C wb[ m .l sHaC+} hwb}s\SWs9y->o:Եqˊri9. )+Ϟqט\TI,~yi`/?/Ci[݌YB~9kUZ:L-|?a\uK./d5Ac|m:|趓Ks>-#}޸ͥʻoi0F3Yi#:o.܂r_-׭I'Ψ[I̟6o-(KߧNZ})u_7bˤHeg^|5l 5.&g5uQX#X?=Mv_POm>QAq1i2VW"UJQuNȇRUlu ^cí{{ܷ3YTwTya)V׭ϜV L (꿵x{6 OFuN|h(lgsuۯHO'Uhߋ\ϻ{ciϻw>'j^&!}>o:Ob6^ڽj6掳JG^(V Մ|<|ZOWdzlVx#SE<>/9B>cD~#9NKg^GX?W'Fc9^Mt9hFs]/r'd<Wd9Գúi-Dn~m9YsSyA8W ?L<[c'o\NG9zr.y-W'r5=˅\ƥ_Lsrv?˟d4VSf 0r%r}ku!/WI&ŗ Po36]ȏ}ώߐ$t{8G륞U Gsm{qmêyGՋ|<UܛW|zqr: +rqHa8dn1s;d7E:—b喣Tx͗OKϗJEt_N_T"!v"̭fv';DvM?%g^?L[˿jۜ. qC6hll61o{'9x^ ԬyCbm_i2_ׄPEAtZ/VY( 䗲gyPQŴaUl 7E˫̖iCk-z. endstream endobj 349 0 obj << /Length1 1644 /Length2 9290 /Length3 0 /Length 10145 /Filter /FlateDecode >> stream xڭveX\ۖ-ݥpwwww  RH!{pN Hp[%hp wk5eLsͽ驵t٥,A N7vn.$ƮuQe]A@7Dr +�[XX }`1dfee ?5/P-@uA [�jj+k(5 �rt�[V  qr8}�X9AJr`IC@�d~qyZ� WG0�C@Kܜ�`^6N%b{rA\nZr '68ټXZ;Y)/ ̋ @n O?,A�k0WP0\A@Wk;_:;;x?r�AA6</1^bۂ!fEb[nw ׿gf_�Z;A� TN '�> [#_9/zZA2�˒B�/{hvw-j؅9V `O-ҷkzعEleC*_x844UYeux6 n]^M ՝q(# fqƗԄy|?Y ~ٿC b2{Q[P:x?d0d%bVM;0&@:*NorO<5{8?n u:0vN}i pVZYw8?aF{Q[721/y@huEu@ �*6 !5c@B6kN (/:e;AhlVaN{sTtWxǼҰ[\Rsq'ah<E%بFd'Tkj%D3chQsiEO8LRJ}3&a J\BQⓂ|5QRß6c[|);`*zkcw="/]ĸXub%7HRV e>U|X@@LkmzE{OXO%Ksl#M?cZ^OjW݀R1L*DF_#zQ?MɲZdR2;#6D)2׿7I秌`yFcNW ^Ypr9TD[M_G;Yo=/#3K*XkvbwBy.s6>/vr1Ľ;xԹ.yybRJ"K!* xX<qv2wzjYڣN 5zhQUJEkr~blJ~GlRP;`g|-^i-@օqm% B$/Z"$gS[ :Oc돚ywr'|^Lȷ-ud+mNJ"q]zޘΗHT"k쫅^}ݑ ?,˝3hfŜJ3W1 vڷ[6_E"[Zt0~X7}ӕm`b[a&4ևz%aTE#[I5~U{WvkEYN剕<܂-Rq d]P^ZWpvlHHxU;$-�[5EhY^144p*|FRzWPUQq;&Wkuޓ]W1J@ߟ;.Ӛiv&{"C^97OEqNcq:NmݠCigKN.GLCSgjik Y/`m)%3Tz~W`D^3eF}J'7)Zg"S7B Y+B Ⱆ,AB3tЏ ;%OD~@r.SV鍸 +)R ?y?}ຸ?Pvs`"h['ּ ."lrZ6do:SπykMy7+zvtg-]Eg<dQҬ۰XT=<ء"@ϖ+[S<Yѿc!]7&(>CPܛNJlw ii"iqKXh[JTu9t?҂w篿k,(< _t76Srcas%@:^T̳i5Y/i4}[nSa3'nAXLKhc0/AT+;YB{BF'-f?f[ӕL"#ϙCEQڜ+,+4c& Ͱ^hrA[#krֳW8'_xvؖ MSrHwxJN_9MGʍ>V7 :v\J=W0{nE.Jkr3' jW*c*�jۦUov\38z}ҿUObq8M!zs}5ᵖհ Wd*-mgCoo[L%q`MDYHH.8k \ґݠͫ@N2sr ME69ke)eBis11*; +SZ "}Pg^,`uaNnc֛*3T:7kTIm94�Y_՝oP 5@+jMŚpv T*SY919CHJ$H�ru!˫yR3&]|8+X^"'0{V,TnJP^[d7s@ρдUtJs 看T*B-,|v{ZL3&gH>K*i{UPh)9{@p|i^Kx:?b8sr5βpӕ)'OPo{etمYojaX9Mvڃ{=yY3}SկF =*;M!Vߕ ?hrkY=l\gB NWQ8j`//r+OoE>A}>IGbY\4c1"n@V|%WN1s$DN8A~ gK/jNՔZ| #(tO/_0!;/ŗfjTǟ-%ļ'Rb Ms B1} X> ]1Ňp$g?3 7f2�_K/9[sƐs pωʡo$SqN;(?QL]0"#wvM:ڝMfڄL|Zthm-ƩW۴8;Rf*'eH" "e"9޳@XeHLp^gݴ.Mn<f`>%YLg"ߞp5fr~,gxw?TA(/x73To>`'lIbH#?$ %68*6ʄ3%;1k<P7fhW'$Їϸl=?.zZWoXHC;f;Q#+v"clzr :Ŭja~XnQK=fbu0O%3hRoNskbb#oc0<nVի`.]rlNĬ�,W5mgutV(BvmdS#,` ?S|vP~|!f:)Az\/^D,3CwE5ӏ0&kK佂D8,Vzc{́ #= R?{HU>2B/4od1L9-57֏.�7w;FɻqC <iA�HPO[HĢNb 5-#YtpeO=\[Dk2ߘދqK@h{!k|HB2sm]7KZ Y#e<gu%|C+ǹҮ rDJF~8B8])%FPF^8e1U`\j{6hC7BC1 mL*ŲK$'Nq'F׭Grj\{(N|M06S|Ne"Y% *@A+lXy[d&>rt-`> 4 uTxg"dD7JjߓHn%|dH%bM8=$uŴ eWT▞v83M:+νmMg?w!/ !?NKG4~L>P -[\,K(hfbj5|s3&3 NQ*zcWIh??b<uώǤfHSNMmB1ZsL QM|liuKeİ-6ݽ5. ZwLѢRI ^"d`1U4"Ojc74mtb%5{B/&jR}5s97!e]$eSx-r,Ev�:(tD\H1|Ott +%_H<iZ$e~E%!fLO5 biޥuP\ /W1rxYY LgBAhKYL<'FA@5̵|dGM̄j@N5gDX?bo" #$ܱW <qpS+ck?q $S%؊ӝ^/(UB;d'A|w/4`M¸$ex|"P\ R39g rfxro,Ҭ2Cٸ6N /1MWrywѮ!]rCC<I ^0/&[3pA8N?U$i֢?t99Pvk:s|`m%ݽrX 7D�Rkڦ,5<BS hq$<zH]opp}l|"x-\]:wq+lh�޹[D-=$wҥ9F fLNFLZ sdQGB/i%=^KL]\[y<GS({ouz!;]tEK*e4qkе!dDTG(씸6vY}zIȫRq$SX%[ w\$B,V>#L̨y*g ٲ)Wa"} hB2;sܕH"f,eɹ,Wvӗ,rõ߷p:"1X*舧lk\GJ[V81{7q R(CTDi1)#~TUKzk-vc$HO™_ݷO�fr_z A,Â,p\4-%<r*\~D>v1sȻ1*9X!$q*N@o@xURKl{f)12>8r:_i^31#$u*Af{}ӯ?qJ l8c9 KN�x:bWnP$ʃ9*1�`zFt OSjYuq3!ûHL vNZ*Ř5[3sU-ʡvPt̥|n괂ʼUg40ۄ*h[EMIÃJf-}B=mn=٭v*YKٜ)Zו+P6if+,O2 M%ZxTSɾU(V2'QGP\z?ǃ צvr#!S. $O1"S NK<d< Sz&. ^FBꝦJl5YaZv 7A6Sm^_NBJi{"M-i :%Gws9ctdQPM ǁ^-@]ۺ9ePjI% v;R- ;ȼw]U*HDSXdmar\ K1/X3?hQwfT.|IJ~ ?4r+*� k߄]FCok{>I| ]sх*[w[N''I bˮ5"{4.KJ%tC˪^ꏗ'B~C /ꇗ[{82k>={u4qzY)1IG+kf&>PSFRZ-eˍ 0N/;`8:5bc<Ufdݱa bi+"y�0Htaæ- :l؈S"|?3-MO@>XC`YxU'e~?B g_75yL&;Ȅ~@jkY,j|gôMʺ6#9O<!NA-p+c;E Qrėv4jѽ6Rj$$s)Q'{=;}Z;,!SoA#S^ J/ ?zw-jIFE=$EOmל4r: 틍JB')Fx8|c@xel2v:ދd{տۨ%Wv_BjxрTdaA"wWC#칐+)҄$ju˳uBV`T eEeT.RZM*z ;$yEjS л7(}]°4-mz HS$)$)MW'STio#șS1ĕM89P\ή!&N@ . "O~{-i_ٔtȦ)t>+_6;uO-%ֹ]g  33 Bp.QgbE#Rycq>Bߜmsf6N$s1#sRU+!䳧[x`ld,PҋvT>k$-9o3vC弖luE+వ`VŁِZea}$Y|a(K 98k@k<DZV$*fx;Sn1X>$јa_]@ Nhw(s?|r]a=Hh.- ^ S"Emhʯ} oy}u$+ɥO?EdLw4CdI)jQ$CEcAr aZs6U�Imk|SD$m@5!L1#7:꙱׽3eƤ\q!+Wѝ_ KF}cL5>D'< ^<u* o8]c2XDB{V4[V5ӿ ^)h"Z<+ ѲDВ}e=IqN"yn|HЙcn>8.ÙD~~·o^^X-QxZT!\Y9m8vY-o͓:4&a7[eG|jI zSnGX]V!tznioTuUa͗etb  a|e9rUd[GZ DaOj: jo{{.GϠm@BpYf6oL-.&'KOQ_5&E3{e`L)4JB#!VPpC*[Ii5ebh|Ē,gp(Swh#cQۺ6+'L 8Gaߓ(qZ(kZ,ߡW[qb` B<`!)ưvSZ-d#QG)l[n&Vc?\;P=$tKl{ycLD$7<xw_I 0wYv(oǎr&miDw.|QACB\Usj!ȑƶSKoB)yܲO71#&Qrnl,Lr%w>Jql+jf Gtha|ख़$ݍ: ԟ"t4;?f1XJ5]‘KrQGU;%3mx= 9 |pY<3&~QNꚦK^j!-l#ȪH7 l@IwH+T\qA˔wi>…y|' & 5:Mnk{ޮvgIHT׉k97EMo>1?P]ltuB|` 0_� ew̑TŒ>:^C׺o rʧW-{Q4?zO&uJ"&]ګ /G52D}r1#I2|} ZX% ~[~ * KurrKBgö* (OL@̧LÅf P>rG|hXˇ\Vɸ#7ac}8r-k_<ʈ-*N0eIJPf9ǏvpArB/v̌S\n~R<4e{e((P/s㷻lS桲B]qZfL[b"Ue�,dcE`k3F߄ԁ+ iDfh6y^܊[@.3nӮ7Y^0fA .AM;n- ?<lbB.փ`Σ}UW)h=Z/^(e$+OzHlKwLrra9L-\s;վ;I7QTG}^q\|_F7£`&;yS&:PJXfZDX[}TQGwuʀ盡ėOLG:*А0=މ1'ec͜ĉW1t#r(ƌcctMA >){I/=ύ6k9@yA*>"~ͩiK*.I! vҒ5"pti<&:I!G٭]D av|(9YMo=dc�9hQkNo'0,9q}U2`>>1ZLT|y( CYAH7ApzG+)h3~Û,7vxUP"C?|J|$rLC,vdUPw05iSQylpF0ZKhOuT>⫫?w}kT3d o~cW~cƼ,'jFQ&vԾwwI+w0R vF?6oM\a o5Q䤲/8-)E;q"@ ÷T|-Fڝ4JKm N4w5׼@z5|~7a,M<Kcg3lES`w;x4q`sJpf,z;ґy*Ȟ("YamwfS8-xmD5)h1p>Ipc+Ncx}= Ŵ<a JLQtQƇW*-gg#= T v,ۥeLkxSVO�4̌/!K,j[004;}X(.FHI.a5ھ�c { endstream endobj 352 0 obj << /Length1 1647 /Length2 12521 /Length3 0 /Length 13352 /Filter /FlateDecode >> stream xڭyeTݒ.ݥ� 4ҸK Aww}gΜYgf~twWzj?UMM(bno 02AvΪv@KW[Z h��Z@s8 `E;x:,\�Zt `Oͻ3 w]!ۆj@ �bJ:2R�Z)E  tzOBd@: =WjLX"� n0:b�8�@�3~.��d]llrp{UG.V&.v;\Jo;̻v�=\e lM<}98W �'-W@.@[ &DVwf.-A`DEla`e:7DWнabn-]]h{,3@ +wKOߡ%]mmM s>hLY5llM� ejbn-?WÅ!FVN&AΒ 2 `ab~x5@'[Nno:u+ /68=w΀YIKSRR3mެ^.@xR7_P�oFV.^�#7{3#/@Z+8<�z,L,,~27 _ub6/6surzgi?74C\7NIOudP\^_e%%l:~seG`'-MG4ؗ+cc7n a1J7Yuh].QUâgX6v';:J<l[T?5Gniz:.`vbM\<^aݸ]QR_\))b?iۊEY)_CӸ}$k(B/woXe5~iNBdeO;42j\U]&IGRuUi<B2ڟ;EV]+UW~7~ ]0>ώaL=ITJ͂YrKQKZ+niqUv\i_^ H &ƪ<qӫ}_YK\!\PYUwΫ6�߽%j&RĚL&Cˈ?)$Z"IBQXC bG~kXG#yC#03EQ<[q4DUUFq*#_ M)~:2�aDء4\Á.qޮNxk&IbϋBLOw^BY2q :ڑq.Bj<-~94QGdv5bd 8 T;fyᙾ +e&A&]�6 *P{g%j<.#7R!y}OkƲaw!p.0glO~5 R-#A|<eh.:φ2| 0,2i8蕓e02HЫ#A#Ep7r !FTߡ\?z`ы6 vM)N_<D�  dL7qG]{T ךo45?VFR ]١Hl*hi{T,'|`e&GbqTO*af<-A]"T&:,Z =&;DHn.%\ j#=J`󢅰uh4͂0u>8(8yUD;>g-Fϒ͔/UfijĿY|R}\c[PSBp&>Gf^5Ie &]o6,\mYm_g1[X\l1FSFPa=eQsi0zo+)5 XɏIws 5~b]&_塳dXsdD4o%d9{ͅifC[ZSo (5v}8ђN;5 Y"i:l9um'ԉU bD 9Q`X­�i&j[pV 81먩?o64CHwWz0c`± ׅYJAVg,{DHi, R{J@S`>r )5[s4 ?hj>ɀVH(Oy'Al<6~uˌ 5?TFdȜ.|>. b43}^U\ dYeF[,@GT얟ϲ⡾|DP+Fhnp\jtN޼"kn#bDⓙARXV;"×5h^7SuI),|K$^*X 3yMHL49xrNw2Zڪb;^9r%BaKΧ\qaC>A~Y,59F<&XFH/dѼ)rv4k;3,ϨNnu []: ]02ԓa nHl@*նx&pS9z+ݭmy g�6@Ch<fӷ` 3v5$OW`:|7I̞y*Ү!W?FPgm鴀oI9?ͨ Tc5TLw.)(+3]:sCF ]ԸKjŋor`Z:|E{Q bm*kv9ER"9@(Ch+X +{,KC'OIE mڀ߬c'si'z>3-i,| C$N"Ȕ[ȥ0_.=2gݽϓ'ϔHBl\ 7x;Vh!zh_ sn ttbY%Eק0e ~?}&ADY~ąENYN*2.n5 SטJ c$puP. ;"`y}ߟHvq%߱1g7:'}qj1Rcw\CB}h=&QԽ +Xp?ԔieQc?#*];~X1]![g175W,>vƀ n5ktyT�OGx5ٮ ͆P0C6 I4?7{)KF� kMp:l !1Es:k0x{yMo¯f_( }M2%G*ف+{09>mVў{_cWj> ϲn}0Gp{[tv qcK|P!7B$NM'#Meִ=H.q} :|%Ei�g72d/_ w~ߩ# (7k𭾱n*VW Zg/hY>s;K{8z1W*wwJ֨O %˲'>) ʍ<,RZt`#'nK Yj 'lA\V{rHgMA,+5ѥ02'jts:1+ A}V z^8VI ( X U-x:u%\vli6-ngpe%ݷnތT,CX<6Km'?{ e˭ԕ~/$S=-'b|=9\7s4a# B=v -8U*+_1XkVk}Y{s·q0t zmI7>thT@:Ü~<xz@ ȭ x PTv� 1Kil'y]pzj ))& 1KdI@k| U7-֠^_ƺ(B͛"w?u SҚgƭ7ַ{Tmlۅ.}~cŊZAh@/lя#κY~#anz-QaVaϮrC$ӡ SYqUJ%Wo̡er%<{dtZ2ѽn1g"y %꘡&cD7~k'WqN- [a1bԩfSW| rsb޴ʡU}N/!:`uey! 7}0o(muE| *7_n]W5#jMo] ˜%J[2e VOT ?I[g`z�qbb%}aC|n܆ڎ*5?4⩑ta`DPV6;]Љ$6+'J[1Tj׫ꌫSPUZ&sk(&n"Z'6x,Q%i,57U.nݓޗkpȺر-!)q`q$::1EVK#-aA5Җ9a}A'RpAN =Î1߹S7x Go'30ũSLJ/u�QzH�a<w^AK;NYJ;neFqKx'/'*f($zʛm{`>[f}K-A=ڶBr\>[$+F& vwqAo_͞Cߧ/p׍ƬͯdoUgS)6k_~Qu!|H$w NP[*hVHܓG*aW\UWpRPiF"++g`B)^"X-1p7?ðPQz~kC$&R ^3SGm!n�pB9_[T*lx ?~Kq�#3*.p#DJ5.Q vZ/0U1x F|ki"פB|N8wb;B2D"/pQ$R q֦?ec~oU^r 䀬1Zh#SF% cI{kAzu'B& rKxlȟCؓp}- K`߼T_L&Y(u-tSEĀu_yx TJA!jFDet{D.%eϴKeg'Wӹ17dqa'6+HI[ Lȴm)J܉y#8 [%7^|o)*aW% Ia)2`!m#˾+?A|D xòTd m3ܰ6꬏nLO'N2弿+`^Q KMゐe*oEKLiʫUz:8 S9v4S@LRh6C$btb;szȋTVn_է^1$ך~)(k Rr冷*D\iE]BLwP6F\G:"`H-L䂛Pi2|݊s p{)i\.}rm6\TymLj$]T[=8U}f#eڂ(t0e�G;h<m+x["uC !p3<l[EkWf^y&k{n!o].[m),$>SW,;;$dfC]\a}Fon86h{W3tUWX^{Z'Ho'<>jofDYqTeɉ_u=( �<b&QYM(<2:fjDa*QD 13:EA^<&Gֽ_4Zz͔Ns_Y {]s򣟶;U~*Wr_<&/D/gĻy h^|,r7/WǛ:/ ʖ)=P>$\WUp~`v6 i&#H΢ų0K1NcI6hy8k| <'FL@BO6{]OӯfxK+h{f>5uH4~l8je:m;`U+L9r(`}ʡ gH 9ɦ z7Tꥧp[QOa,dqVҋ O7/TYz0 _!X* rs ѵшsBFfbWٛL: H{UU7:-X^я\"BuimK84r~\-qLCBP~wxЍbb U3U_?n5gO4:"U7wѯH1j>ǀMGς)k�)y/ic%/҇_`Ȱsd0+pŎ7z=`-X垲Ĥ5Z'pB>gS=1$xҦ9;PY/9W}imfe|i%d%G�ҡig?if\$-)9;qƀetN9fDGUť'-5iv5,)E41DR+I }O<I-g{,b4ڱդg9Ne>/fE)2bgI]+\�:�Gm [Ývɂ- OuS!HIφaj" _K&ln ryș t'W,aZU3bP\$R]g3gǪʄ Ə'8B_ U9Hς jr I|9fjd@r`> } \\}DŽh~" s3YhQ#V@93QVOM<'8WxƟ ŽwTq:Q ڡ݋ƃv~B+4_/JE#2 U&B=69 3y Έ!D̈{>chX'g[<mcʧKDE##dW p@KgkB,TduvD qQ)*Ǡ>7NxT0' lVZ&".q_]Y~SD/e$I?[!MMe`mP9>蚤8@8!rY wy>J0Eq { g3q-miCK÷&CsmtF@ ąh[XSHRd,H 8Yٳ�~"N LGk #wkw5ٚh3sjmmaQ}dG0$Cq |ߘ@pϽ4ΣKSdV&_8'6) \5Yx1Kn7ksv+ǘµroe] P3: m]uV=݃4'v~LQ`*+z: i77CM(6As�< hejVz5ۊtDQ<|P~qm̀FHh%ʗ)B8s^MfGuX7F:acC l5}8;͖;_@dYB\Ȇ\[a!BLDl ye 8)ԼﳃKͤf-"KQP'TWpk+lXΉ$Cሒ'PqIAB)!u_"Dh7u¦)/W g |O7((<H VF[,+ek,2d@v`[ F- }\~qCkrOv'%o+2sA&۷ n/~;:^l(KSEzpMS𲞱ꔪfR_"twlgXܯ_<�TF_IOD(-\'2bhnUx ~>v]+"ZTk6sguY=ULQlN`_+~H;ҾuidWlvtnzqf˲+>lp',xjFt7ÞT\)XFӲ: {Y!2lqhOCWfX;<HJ$^ΑZ-Y8хJa AXԧxyZe'.9MgddFáaݼ5}`?o`<$2${:5F =^kR9\;ջ).r 3SBү:0e'/mKHݖ#:Ke +C"(>k~C+=iudʹ$"IϻTήڂZkiSgD'$8zZbCd-MQ$ʼn|NjomC9M8$TyX{4 :?t59Jj,o.%(d,ş" Zdo;84!&j|q8jcŲvsZ!E"hHLo~=JR@(J[eH7F`/5`vh:TiKw2<SStaP(rWpsqT'^d6'$DlL&T$ dR4Y@8M}\Di+gzDҕ} c@{2'Yކ~ZYuP$ҫ+L}{ΨԖ{LIM PoLG[L+ v<蜄)$ '4{S�'qD$i,DRwvMo?N:ͮm0-gvIh*ҒS%s/<jVMpOV?Kλ0K[M *crJ.rU#BQ~}poN wn*ƣ!2 }nIOҭ[ 뗻` "\vq1|v(|x Tk۰Va `xQy9oC[}iGl ߧ&R,`sWccY`V@_b% U�"N~ڟS3Gm|%p,?wW&}! lRe<;;W!/ZApd0"lR -wfr΂`Ic=p=FޣzTԻ@%:Y'[ox�g,$q@MP..90yvOgLR�ѣF>?h}Oop`ah0&iE͒*J>\,&϶TC: X*dƴIX\s_p8 8\`k2qhb|P0#޼ԙT}Vj}zW( C呐2ftJ%m} X�FD>CLQǺD?N*XpM5~Q^,敄b|C1$M.I!-t;s$1Co+XC7oKU¼L/H~nf 7>ڊg,>dWݫ=ÁH8AqE!xWpP:F'epՅ*>[V_߫E#ְLg!FQ7"5*ņ};ĨOtFZt( Ҩ2;\8D(X cE#W`4ޘ! lM#ҲSPm?[=Ve>b F[m6o6dtR9?VW .ƟHm/0+qʼ1T3M^e6b?1|KTy?U"UobN97QFa&w@}8' ZLhp. 3'vdn*o*mo67Kį$+n9B>=c&(·w^MW6ENbJ b!9r ׈x+?Ȱv2:96uIkH)EoK_fg&&nꟻ-5K{MUdCw\t@[}-Q1K;z۳O:|iD_*y&69ۉ6q�+`!/vZڙ+-J/=uM ;vb /b&R;{hO5䷄H"ޮƹGGqcS|A!%M{ YJcD3}wPnmʄ#sMp~Hnr&oh7d+,{ XSvTP%fͺbH^a B\S}m"@\h^0Am -\/w BG<_ ޕO g|Ze 59*8[龱: #cvmKn vbSpy|�"JxfTFa^_R!3l+쫢 ߮e7w束{ zz; 3< LxoENdD9%/1:Fc;JWjem# eid1-o&YG�/;a=-7H;^;Tbaj6' ı H4TF L79p>?$ :ma^N>Xf `F6r߿cp2?^x}lfk8 F{< f1Ag5h@m~}SQ`zAWGZҌ xML<;<$J_u(_|6LIoK 01GHEt?A|ENwiG7I1,(w3m BP*\)̾ⶣ)?I= ~6X.È"Xq\)tdn5�Q'>Bf7XD]&Ugx%s?Yci kZVQnb\76_J44sB?Rr˭]-cMqRAHx@yN$@l 7DFhHQpC[ KIFcjv2T /B6}m1%LO+ 6.xS|Bvq^PzҚ;S9:\6T dhq Q_p1ͶZ;IIo*lxZUQ32L%ܩZݓqDnS=ffyԣ}$yd: p d>#%Kx~C<V:S\p6:4bG[V}ȩZ(P<00IyЊ9 C[pD<n-: ]CMt.~VNِW!Q4Α�?Dl[57|Jgq2as':t!t9}/:Zj'M)R)ѕtg9Ql2[x(aԻ"-HP0m 'M$9q+wWٞ!KeRNٽվ BU{_ n06|zw_"E};wUu}ٮ7z,Ϙ.rӳd^_gYВdts+N8|̖j9MAXJppGh`mb`H~NӅ:f%,PY[�Y.tY/z4 R+K*NL@,5w^JDTu|8[n˖3fɼf;X%F&Hk6 |@ttu89dWM_KTUZ{Ǵzo[(R%OZ)[Ʈ~RUPf CDPxtd@ҹ=SbN*jhvi&U,SαHJGP+fBQpGL=-H>;qG/⋈ԩrEeD*"ה^Svd=S�# Cq^HbWU8.cɫ:Mh;";ׄ%�0PY(mlBuPJC%q ӌi|*>O"ϼ-@hZ?˂)˰'ވkdg~jg#4?ln%a$1g촁l ̡se_$.&HMTg{zZd7f&LJ2ػ@c"cPW3sSpp2&S~)N!(!m(\蠚|B|FpF٪T}J|I2.Ci5k8?۹6 @WlN;p xb�~9hyFM �Y9z <6T{$+Nlf R뇬k,__'G0-͠xxWJE$oD~.ߢib p-JYkE,g7 oUBm0ispND<#&h`ZOM~49 cv&{*#@y̤ClҌ˛89$GRdţ0d1kw>t{3 `9HtTnc3TtVq5GV"/+ 7٪Fm;md*"4DW\!]׊Jcfˈ+PeDQQAe�b[ endstream endobj 385 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.18)/Keywords() /CreationDate (D:20180518110032-05'00') /ModDate (D:20180518110032-05'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017) kpathsea version 6.2.3) >> endobj 351 0 obj << /Type /ObjStm /N 57 /First 489 /Length 2678 /Filter /FlateDecode >> stream xZko۸_-OIb4m<I[\(6Ɩ\INdd(p6Ù3gFCJP P"*‚(&DŽɈMH!т0Q01It@X2Ƅ3p#\*Ik! APDp#@M*Ec!�Xȁ�)а!,s �aNi)b\H"!u p3DT "*!:#*Q"!��9EH@n0cL/p U�_c w##@uHeHP '07Dፄ6WУ<L9*y;>Kf0sptvY:],Ov.ds9M `Уi2)׽z?vD \~нrd "t?6 (zǹW4e!;^VfvM"5Ejq"#zJKzE:{:McZВVtA5C_ ~x9>:ob$ ,`>#:^c-#,#L-)A&[J$s)٧@ʛ =,5|6K(K=ed }3 0K3,L M]U֊Coiaq>}a @#}M1xll<ww|LIyjFdF,ʌoAd$UT ^Nq"1YZF4p,wHtR@=[o[3zrtity"q@?Ad`qXq`z *:_*/m?ү7zstid$"(hoPY Xt:6S5,�eZ8],G(]Bbv I')A%%|GylI a:5eDҤz.-DWy!~dTyt(MGi1ZH|Y{6ې9U6G?CbٽNF=eq[! JJ3?<G ~QZ -$Yߤ꾄;o|\9>jZe}ADA% Ѩ.鲧 ݟ7=ԌlCP`.)NOo.z6p {[3=yz$ぃ |?vQ>J7?O'G/HVӤJړ@8F�Pw�@!A/ۻdbJ8 <c `\ _.ɀ3q2]_+-ӼF5<msV\>Cn\6zbs6]Ѳ[|QMao-D,Sڕ)DENȲǵf\Geづr?3O-u;lS><MّcGv6;cDk;-lLvdkyfxf.ϼg̶zhnYF1ḥ 8w<x~9G;p/wni!igGˆD8$A5J!Wý뽫Ã!EѓtVg4*\fc;/g)`.+O |^ЦaZb g20LN8k=p]x3ʕѽ=�މ(FF"ͰO}.j>3U0 +A;2euH+u*kbâ uy4sk;]f˭c=874+f':jH#sLd"r*NVyfU]h� HDoZWf__g-=(߭83ߪa=*uG*9uwذ7G/V,2d?@ŪD S8ۺqiIZCR\fIS-ܩjI)엁ߕZeP >N`d^Wt@5Oxm eA1(@w+HOU 'n-kN$8a@ %ԕhPq�C+g,DuT$E5]bqcW?KlE#NL Zگ6l;E })GHȲ+)|B)ᮡY\c@ QځTRN}$Ͱp]{3l -J-*KYՁ*?IRr#pD82|f92|f92}]&Ȭ $IauͰ2|VRdVYlgA a82 IFfM{ pd6*ȯz!\#$z3hDfc}66_Q.Cv,DGlѬio)8 @}f}*:Hٍi3߈eM{3lvBfϬBfe'dVv#IRb#pD8`ԱFfUGv:|`^ pq۩ iQv:hڣhބDsRثn>iܫSsٺ7U6W\,:|@ۻ/414=T4]S4}R4'(mSB} ?83|S_4kBD[w�]x|2?.l:lH,Mi>6}iVA&۳z)^ endstream endobj 386 0 obj << /Type /XRef /Index [0 387] /Size 387 /W [1 3 1] /Root 384 0 R /Info 385 0 R /ID [<D8E07A06758FAEE433E1D662928CE8E6> <D8E07A06758FAEE433E1D662928CE8E6>] /Length 873 /Filter /FlateDecode >> stream xrE! p C $@ B !�A W`ʽUnr%x\xZ]X劒ySRR:џѩ0FЃ'dž.ae/6jnMlA}b.'v6s/t;l}B~"/]v>k) b,ߋ=7ڀ(c&W&?D-=?H:' ,1s8I\.21{O6:p}w9O<|n6.!jܚ->>ÒbX#<,#,5r`6/gXsK❹|oj)uhqhqndw56[`wPleS:*a^^^^ lօօօօco-Sù…10&4,NA-B-j>WђВВ8>Z,曕&&*:ЗmLw|愪"$$$$$t#]U"rCK|6=ߢwҏ'PǖO<S]/͙H?k<w~ˊ֡oK чn?de�۰y.>!4RL,#y8Q4pg1 C.)\eLcW09\5:n X-=džZ3O.e y0@+FӌLJï9zWk:Zϯ,嗑|[[BBB#)4)<B&)4)<_R M M M M M M M ->ФФФФЦkN?U>ñn endstream endobj startxref 130541 %%EOF ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/DESCRIPTION�����������������������������������������������������������������������������������0000644�0001762�0000144�00000001271�13277600065�013030� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Package: RUnit Version: 0.4.32 Date: 2018-05-07 Title: R Unit Test Framework Author: Matthias Burger <burgerm@users.sourceforge.net>, Klaus Juenemann <k.junemann@gmx.net>, Thomas Koenig <thomas.koenig@epigenomics.com> Maintainer: Roman Zenka <zenka.roman@mayo.edu> LazyLoad: yes Depends: R (>= 2.5.0), utils (>= 2.5.0), methods (>= 2.5.0), graphics (>= 2.5.0) Suggests: XML (>= 3.1.0) Description: R functions implementing a standard Unit Testing framework, with additional code inspection and report generation tools. License: GPL-2 NeedsCompilation: no Packaged: 2018-05-18 16:00:38 UTC; m044910 Repository: CRAN Date/Publication: 2018-05-18 16:32:53 UTC ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/ChangeLog�������������������������������������������������������������������������������������0000644�0001762�0000144�00000120705�13267374743�013112� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������2009-04-22 15:52 burgerm * tests/runitInspect.r: added test cases for more complex functions to track 2009-04-22 15:50 burgerm * R/inspector.r: includeTracker: fix `<- if` call handling which caused the inspect mechanism to fail 2009-04-22 15:48 burgerm * R/runit.r: isValidTestSuite: check for empty name string as this will cause subsequent failure which is harder to understand 2009-04-22 15:46 burgerm * NAMESPACE: declare S3 print and summary methods for RUnitTestData 2009-04-22 11:37 burgerm * inst/doc/RUnit.pdf: removed: use R CMD build to generate, no need to store in repository 2009-04-16 11:54 burgerm * DESCRIPTION: patch level 0.4.22: clarified GPL version 2009-04-16 11:49 burgerm * R/textProtocol.r: specified license to be version 2 of the GPL 2009-04-16 11:48 burgerm * R/testLogger.r: specified license to be version 2 of the GPL; extended function descriptions 2009-04-16 11:44 burgerm * R/: checkFuncs.r, exportHTML.r, html.r, htmlProtocol.r, inspector.r, runit.r: specified license to be version 2 of the GPL 2009-04-16 11:39 burgerm * R/00Init.r: specified license to be version 2 of the GPL 2009-04-16 11:23 burgerm * inst/: examples/correctTestCase.r, examples/runitVirtualClassTest.r, share/R/checkCode.r, share/R/compareRUnitTestData.r: specified license to be version 2 of the GPL 2009-04-16 11:18 burgerm * tests/: runitHTMLProtocol.r, runitInspect.r, runitPlotConnection.r, runitRUnit.r, runitS4.r, runitSetUp.r, runitTearDown.r, runitTextProtocol.r: specified license to be version 2 of the GPL 2009-04-16 11:13 burgerm * man/: RUnit-internal.Rd, RUnit-intro.Rd, checkFuncs.Rd, inspect.Rd, printHTML.trackinfo.Rd, runit.Rd, textProtocol.Rd, tracker.Rd: specified license to be version 2 of the GPL 2009-03-16 17:38 burgerm * R/inspector.r: always coninue if-else on the same line removed semicolon at line ends 2009-03-16 17:36 burgerm * R/runit.r: defineTestSuite: gained some argument checks isValidTestSuite: warning messages modified to handle multiple names some typos in inline documentation fixed 2009-03-16 17:29 burgerm * R/checkFuncs.r: always coninue if else on the same line checkIdentical: added precondition check 2009-03-16 17:26 burgerm * R/exportHTML.r: always coninue if-else on the same line removed semicolon at line ends 2009-03-16 17:24 burgerm * R/html.r: always coninue if else on the same line html.r removed semicolon at line ends 2009-03-16 17:21 burgerm * inst/doc/RUnit.Rnw: minute corrections 2009-03-05 16:58 burgerm * DESCRIPTION: License field updated according to R-ext for R 2.8.1 2009-01-23 19:13 burgerm * man/: checkFuncs.Rd, inspect.Rd, printHTML.trackinfo.Rd, runit.Rd, textProtocol.Rd, tracker.Rd: corrected typos, changed formating of code chunks to allow for proper display in pdf reference manual (suggested by Terry Therneau) 2009-01-23 17:23 burgerm * NEWS: typos corrected 2009-01-15 14:47 burgerm * DESCRIPTION: patch level 0.4.21 2009-01-15 14:46 burgerm * ChangeLog: update for 0.4.21 submission 2009-01-15 14:46 burgerm * NEWS: updated for 0.4.21 submission 2009-01-15 14:26 burgerm * man/tracker.Rd: enclosed \item statements within \describe environment: identified by new R 2.9.0 Rd parser 2009-01-15 14:23 burgerm * man/runit.Rd: defineTestSuite default argument value: use \ escape to display \\ in resulting help page after parsing 2008-11-07 14:49 burgerm * R/htmlProtocol.r: compiler detection failed on MacOS, fixed, thanks to report by Steffen Neumann 2008-11-07 12:20 burgerm * R/inspector.r: use seq_along 2008-11-07 12:19 burgerm * R/checkFuncs.r: check for missing arguments 2008-11-07 12:15 burgerm * DESCRIPTION: patch level 0.4.20 2008-06-20 17:51 burgerm * R/textProtocol.r: printTextProtocol rewritten by Klaus using toText; getErrors moved here, TODO replace with S4 method 2008-06-20 17:49 burgerm * NAMESPACE: adopt S4 classes and methods; removed some outdated functions 2008-06-20 17:36 burgerm * R/runit.r: rewritten by Klaus, using S4 design and condition mechanism 2008-06-20 17:29 burgerm * R/00Init.r: added generics definition used for S4 methods 2008-06-20 17:27 burgerm * R/testLogger.r: removed; will eventually be replaced 2008-06-20 17:26 burgerm * R/: TestFileResult.r, TestFunctionResult.r, TestResult.r, TestSuite.r, TestSuiteResult.r: S4 class based design of RUnit 2008-06-20 17:26 burgerm * R/checkFuncs.r: rewritten by Klaus using conditions 2008-06-20 17:24 burgerm * R/utility.r: utility functions not attributed to one class; miscellaneous 2008-06-19 15:57 burgerm * ChangeLog: updated 2008-06-19 13:49 burgerm * tests/runitInspect.r: added 2nd example function 2008-06-19 13:46 burgerm * man/textProtocol.Rd: details section updated; example on RUnit test suite execution added in a dontrun clause: works only on source package as tests/ folder is not copied to installed package 2008-06-19 13:42 burgerm * man/runit.Rd: runTestFile example call added 2008-06-19 10:07 burgerm * DESCRIPTION: patch version 0.4.19 2008-06-18 19:18 burgerm * R/textProtocol.r: details section: output check number per test case 2008-06-18 19:17 burgerm * R/htmlProtocol.r: details section: output check number per test case; improved compiler detection 2008-06-18 19:16 burgerm * R/runit.r: isValidTestSuite: changed check for object class to is() to allow derived class objects to pass (suggested by Philippe Grosjean); use addCheckNum to set number of checks performed within test function 2008-06-18 19:13 burgerm * R/testLogger.r: added addCheckNum, getCheckNum functions to allow check number output in summary 2008-06-18 19:12 burgerm * R/checkFuncs.r: checkEquals: argument checkNames checked for correct type; not required paste calls around error message removed (suggested by Philippe Grosjean) 2008-06-18 19:09 burgerm * R/00Init.r: removed start up message 2008-04-29 10:06 burgerm * inst/share/R/compareRUnitTestData.r: check for empty test case results and exclude them from comparison; replaced seq with seq_along 2007-11-30 15:13 burgerm * tests/runitRUnit.r: error introduced in last commit corrected; some more check conditions added 2007-11-30 14:57 burgerm * R/htmlProtocol.r: fixed errors introduced in last commit: replaced seq_len with seq_along 2007-11-27 20:07 burgerm * ChangeLog: updated 2007-11-27 20:05 burgerm * DESCRIPTION: patch level 0.4.18; R dependency set to 2.4.0 were seq_along was introduced 2007-11-27 19:56 burgerm * tests/runitRUnit.r: seq_along introduced; partial argument names expanded 2007-11-27 19:55 burgerm * R/runit.r: seq_along introduced 2007-11-27 19:53 burgerm * R/exportHTML.r: seq_along introduced; partial argument names expanded 2007-11-27 19:52 burgerm * R/: html.r, htmlProtocol.r, testLogger.r, textProtocol.r: seq_along introduced 2007-05-21 13:42 burgerm * NEWS: updated for 0.4.17 CRAN submission 2007-05-21 13:37 burgerm * DESCRIPTION: patch level 0.4.17 2007-05-21 13:33 burgerm * man/: textProtocol.Rd, runit.Rd: changed example code to work with installed package test case example path 2007-05-18 14:58 burgerm * R/runit.r: try harder to ensure previous error handler is reinstantiated after test runner execution 2007-05-18 14:56 burgerm * DESCRIPTION: patch level 0.4.16 2007-05-18 14:55 burgerm * NEWS: updated for 0.4.16 2007-05-16 15:17 burgerm * DESCRIPTION: SaveImage replaced by LazyLoad 2007-05-16 14:27 burgerm * inst/share/R/compareRUnitTestData.r: initial prototype: comare two RUnitTestData objects: intended for test run time performance evaluation 2007-05-16 14:24 burgerm * R/runit.r: .sourceTestFile: updated to observe - after sandbox introduction - test case file defined .setUp and .tearDown functions 2007-05-16 14:21 burgerm * tests/runitS4.r: check S4 setClass + removeClass chain with new sandbox implementation 2007-05-16 14:19 burgerm * tests/: runitSetUp.r, runitTearDown.r: added for focused check of .setUp and .tearDown functioning 2007-05-16 14:16 burgerm * tests/runitRUnit.r: class names changed; clean up operation revised: try harder to reset to previous global state 2007-05-16 14:15 burgerm * tests/runitInspect.r: .tearDown added: cleanup 2007-05-16 10:49 burgerm * DESCRIPTION: dev patch level 0.4.15-2 2007-05-16 00:46 burgerm * man/runit.Rd: added encoding latin1 to display umlaut correctly if available; make use of describe command to format details section 2007-05-16 00:42 burgerm * man/checkFuncs.Rd: added encoding latin1 to display umlaut correctly if available; added paragraph on S4 classes and methods to details section 2007-05-15 23:46 burgerm * man/inspect.Rd: added encoding latin1 to display umlaut correctly if available 2007-05-15 23:45 burgerm * man/textProtocol.Rd: added encoding latin1 to display umlaut correctly if available; added header; added \pkg command around RUnit phrase 2007-05-15 23:43 burgerm * man/: RUnit-intro.Rd, tracker.Rd, printHTML.trackinfo.Rd: added encoding latin1 to display umlaut correctly if available 2007-05-15 17:33 burgerm * R/runit.r: make use of a new environment defined as child of .GlobalEnv to allow setClass calls wo where argument 2007-04-25 08:46 burgerm * R/TestCaseMethods.r: construct method added; verify method checks partially switched off 2007-04-25 08:39 burgerm * R/TestCaseTestResultData.r: slots checkNum, traceBack and warnMessageStack added 2007-04-23 00:14 burgerm * R/testLogger.r: replaced by condition signals 2007-04-23 00:10 burgerm * R/TestCaseTestResultDataMethods.r: .printTextProtocol added: used in recursive call chain 2007-04-23 00:08 burgerm * R/: SourceFileTestResultDataMethods.r, TestSuiteTestResultDataMethods.r: .printTextProtocol, getError and getTestCaseNum added: used in recursive call chain 2007-04-23 00:05 burgerm * R/runit.r: removed .testLogger; added warning handler; switched to condition signals; use testCaseCheckCount for check call counting 2007-04-23 00:03 burgerm * R/checkFuncs.r: removed .testLogger 2007-04-23 00:00 burgerm * DESCRIPTION: removed .testLogger; added experimental condition signaling 2007-04-15 12:50 burgerm * DESCRIPTION: patch level 0.6.0-2 exploration stage; NOT FIT FOR PUBLIC USE 2007-04-15 12:48 burgerm * NAMESPACE: new S4 classes and methods added 2007-04-15 12:47 burgerm * R/runit.r: experimental: enabled new S4 class based result collection IN PARALLEL to exiting logger mechanism for exploration; NOT FIT FOR PUBLIC USE 2007-04-15 12:45 burgerm * R/Logger.r: getSealed added to exported accessors 2007-04-15 12:40 burgerm * R/checkFuncs.r: RUnit specific signals/conditions implemented and enabled in check* functions 2007-04-15 12:38 burgerm * R/zzz.r: log statement added; TestResultData method def enabled 2007-04-15 12:37 burgerm * R/TestSuiteTestResultDataMethods.r: method implementation added, accessor methods added 2007-04-15 12:36 burgerm * R/TestSuiteTestResultData.r: slots error & errorMsg added to cover suite level errors 2007-04-15 12:35 burgerm * R/TestResultData.r: slot name changed 2007-04-15 12:34 burgerm * R/TestCaseTestResultDataMethods.r: method implementation added, accessor methods added 2007-04-15 12:33 burgerm * R/TestCaseTestResultData.r: class definition reworked, slots renamed and added: NOT FINAL DESIGN 2007-04-15 12:31 burgerm * R/SourceFileTestResultDataMethods.r: method implementation added, getTestResultData added 2007-04-15 12:29 burgerm * R/SourceFileTestResultData.r: slot name renamed to sourceFileName, slots error & errorMsg added to cover source file level errors 2007-04-15 12:28 burgerm * R/htmlProtocol.r: regexpr fixed for R 2.5.0 2007-04-12 10:29 burgerm * R/inspector.r: unused variables removed 2007-04-09 19:42 burgerm * tests/runitRUnit.r: all check* test cases reviewed and extended to cover more failure conditions; checkEquals extended to be checked for all basic R types and S4 objects 2007-04-09 19:38 burgerm * R/testLogger.r: errorHandler: rewritten to be more failure robust; docu added 2007-04-09 19:25 burgerm * R/checkFuncs.r: checkEqualsNumeric: one more argument check added (now identical to checkEquals; docu description made more clear 2007-04-08 14:14 burgerm * R/runit.r: runTestSuite docu make more clear 2007-04-08 14:11 burgerm * R/00Init.r: pass lib argument on to packageDescription to load correct DESCRITPION file 2007-04-05 16:28 burgerm * R/htmlProtocol.r: included deactivatedStyle tag to HTML output: non-visible change 2007-04-02 00:08 burgerm * R/zzz.r: class and method init calls added/updated 2007-04-02 00:04 burgerm * R/00Init.r: packageDescription now observes the library path for the currently loaded package version 2007-04-02 00:02 burgerm * R/TestCaseTestResultData.r: sealed argument controlled by .GLOBAL state; slot failed renamed to failure 2007-04-01 23:59 burgerm * R/: TestResultData.r, SourceFileTestResultData.r, TestSuiteTestResultData.r: sealed argument controlled by .GLOBAL state 2007-04-01 23:53 burgerm * R/: TestCase.r, TestCaseMethods.r, TestLogger.r, TestLoggerMethods.r: playground extended 2007-04-01 23:51 burgerm * R/ArrayMethods.r: - code formating changes ArrayMethods.r - setNames: unnecessary precondition check removed - concat: precondition check added method definition also added to Array base class - printObject rewritten to use show - show method added - array class constructor: sealed argument controlled by .GLOBAL state - applyFun method definition added to array class generator 2007-04-01 23:43 burgerm * R/Array.r: allow sealed argument to be controlled by .GLOBAL state 2007-04-01 23:42 burgerm * R/classUtilities.r: in setGeneric avoid assigning to temp variable value as no postprocessing is intended here and this assignment has a small memory penalty as shown by memprof 2007-04-01 23:38 burgerm * DESCRIPTION: patch level 0.6.0-1: more exploratory code chunks added 2007-03-31 23:15 burgerm * inst/examples/runitVirtualClassTest.r: S4 class example 2007-03-29 09:26 burgerm * inst/share/R/checkCode.r: utility wrappers around checkUsage (package codetools) to check no default location code files; experimental 2007-03-23 15:18 burgerm * tests/runitPlotConnection.r: deactivate test case if R is run in non-interactive mode i.e. most likely no X server is present but require by the png device 2007-03-19 21:08 burgerm * tests/runitPlotConnection.r: simple run test 2007-03-19 01:54 burgerm * R/htmlProtocol.r: fixed gcc query under Windows: report NA 2007-03-19 01:52 burgerm * NEWS: printHTMLProtocol fixed (Windows) 2007-03-19 00:55 burgerm * man/runit.Rd: added header 2007-03-19 00:54 burgerm * tests/runitRUnit.r: added exception check for call object: R 2.5.0 devel issue with new try implementation 2007-03-19 00:51 burgerm * tests/: runitTextProtocol.r, runitHTMLProtocol.r: added check for successful execution, taking care of nested test suite calls 2007-03-19 00:49 burgerm * inst/examples/correctTestCase.r: R 1.9.0 compatibility: replace isTRUE by identical 2007-03-19 00:45 burgerm * R/inspector.r: docu updated/extended; tracker closure functions revised: internal object now is always of S3 class trackInfo, renamed to trackInfo for clarity, addFunc simplified, reinit of oldTime was not written to closure variable; several precondition checks added 2007-03-19 00:40 burgerm * R/exportHTML.r: functions now return invisible; HTML head info updated; result page file names changed; HTML footer added 2007-03-19 00:36 burgerm * man/printHTML.trackinfo.Rd: header added; details text description corrected 2007-03-19 00:34 burgerm * man/tracker.Rd: header added; moved closure functions to new section to avoid R CMD check warning; added isValid 2007-03-16 12:25 burgerm * NEWS: updated for 0.4.15 2007-03-16 12:22 burgerm * DESCRIPTION: patch level 0.4.15 2007-03-16 12:08 burgerm * R/checkFuncs.r: compatibility to R 1.9.0 as declared in DESCRIPTION: removed calls to isTRUE as this was introduced only in R 2.1.0, replaced where needed by identical(TRUE, x) 2007-03-16 12:07 burgerm * R/htmlProtocol.r: createTestFuncRef: removed unnecessary escape characters in gsub call 2007-03-16 12:05 burgerm * tests/runitRUnit.r: compatibility to R 1.9.0 as declared in DESCRIPTION: removed calls to isTRUE as this was introduced only in R 2.1.0, replaced where needed by identical(TRUE, x) 2006-08-30 01:13 burgerm * DESCRIPTION: patch level 0.6.0-0: start new minor level 0.6.0.x for development, first public release will be 0.6.0; 0.5.x is reserved for potential S3 implementation updates; class layout as described in dia 2006-08-30 01:11 burgerm * NAMESPACE: initial commit: class layout as described in dia 2006-08-30 00:58 burgerm * R/TestSuiteTestResultDataMethods.r: initial commit: class layout as described in dia 2006-08-30 00:54 burgerm * R/: ArrayMethods.r, Logger.r, SignalHandler.r, SourceFileTestResultDataMethods.r, TestCaseTestResultDataMethods.r, TestSuiteMethods.r, classUtilities.r, zzz.r: initial commit: class layout as described in dia 2006-08-30 00:51 burgerm * R/: Array.r, SourceFileTestResultData.r, TestCaseTestResultData.r, TestResultData.r, TestSuite.r, TestSuiteTestResultData.r: initial commit: class layout as described in dia 2006-08-22 11:21 burgerm * NEWS: 0.4.14 update log added 2006-08-17 09:12 burgerm * DESCRIPTION: patch level 0.4.14: package utils dependency made explicit in DESCRIPTION and NAMESPACE 2006-08-17 01:14 burgerm * NAMESPACE: import of package utils added (required for R 2.4.0) 2006-08-16 17:39 burgerm * inst/doc/RUnit.Rnw: added a note on the new arguments rngKind, and rngNormalKind; replaced path construction via paste by the more protable file.path (Gregor Gorjanc) 2006-08-16 17:36 burgerm * inst/doc/RUnit.pdf: updated 2006-08-15 18:52 burgerm * ChangeLog: updated 2006-08-15 18:51 burgerm * R/textProtocol.r: return type enforced (a bit more) to be logical 2006-08-15 18:50 burgerm * R/runit.r: defineTestSuite, runTestFile: added arguments rngKind, rngNormalKind to allow configuartion of default RNG configuration, docu tag updated 2006-08-15 18:48 burgerm * R/testLogger.r: documentaion tags added 2006-08-15 18:47 burgerm * R/htmlProtocol.r: return type enforced (a bit more) to be logical, documentation updated 2006-08-15 18:41 burgerm * R/textProtocol.r: documentation updated; printTextProtocol return type changed to logical 2006-08-15 18:34 burgerm * R/html.r: documentation tags updated 2006-08-15 18:33 burgerm * R/exportHTML.r: file I/O: replaced paste call by more protable file.path 2006-08-15 18:31 burgerm * tests/runitRUnit.r: defineTestSuite test case added 2006-08-15 18:29 burgerm * man/runit.Rd: runTestFile, defineTestSuite: documented new arguments rngKind, rngNormalKind 2006-08-15 18:27 burgerm * NEWS: updated: added recent releases 2006-08-15 18:26 burgerm * DESCRIPTION: patch level: 0.4.12 2006-08-08 00:58 burger * R/runit.r: defineTestSuite - allow file extension .R 2006-08-08 00:57 burger * DESCRIPTION: patch level 0.4.12 - allow file extension .R (runit.r) 2006-07-17 19:27 burger * R/00Init.r: package version added to startup message 2006-05-22 10:33 burger * R/checkFuncs.r: DEACTIVATED - fixed typo in var name 2006-04-05 13:47 burger * R/checkFuncs.r: checkException: added comment on contributing author 2006-04-04 19:25 burger * R/TestResultClass.r: License header text added 2006-04-04 19:25 burger * R/TestLogger.r: initial commit 2006-04-04 19:24 burger * R/: TestResultMethods.r, TestSuiteResult.r, TestSuiteResultMethods.r: Lincence header text added 2006-04-04 17:38 burger * NEWS, inst/doc/RUnit.pdf: updated 2006-04-04 17:08 burger * man/inspect.Rd: updated inspect call and argument documentation 2006-04-04 17:08 burger * man/printHTML.trackinfo.Rd: updated inspect call 2006-04-04 17:07 burger * man/tracker.Rd: updated inspect calls 2006-04-04 17:07 burger * inst/doc/RUnit.Rnw: updated inspect calls, added some line to recomendations 2006-04-03 18:35 burger * tests/runitRUnit.r: checkException: silent added to checks 2006-04-03 18:34 burger * R/checkFuncs.r: checkException: argument silent added 2006-04-03 18:32 burger * man/checkFuncs.Rd: checkException, checkEquals documentation updated 2006-04-03 18:31 burger * DESCRIPTION: patch level 0.4.11: checkException arg added 2006-04-03 14:40 burger * inst/examples/runitfoo.r: example function 2006-03-21 15:47 burger * tests/runitRUnit.r: unsuccessful attempt to extend runTestSuite test case 2006-03-21 15:37 burger * R/runit.r: isValidTestSuite: error message texts added 2006-03-21 15:34 burger * R/inspector.r: inspect, and includeTracker have new argument track, which defaults to track for consitency; API docu enhanced and extended 2006-03-21 15:32 burger * tests/runitInspect.r: reactivated both test cases after changes to inspect, and includeTracker 2006-03-21 15:31 burger * DESCRIPTION: patch level 0.4.10 2006-03-07 20:41 burger * R/checkFuncs.r: checkEquals: new compatibility argument to allow to workaround stricter all.equal checks; tolerance precondtion added 2006-03-07 20:39 burger * DESCRIPTION: patch level 0.4.9: checkEquals has new compatibility argument 2006-01-20 18:25 burger * tests/runitRUnit.r: checkIdentical added; checkEquals test case extended 2006-01-20 18:24 burger * man/checkFuncs.Rd: checkIdentical added; arguments a,b, renamed consitent with all.equal 2006-01-20 18:23 burger * R/checkFuncs.r: checkIdentical added; msg argument default added; msg added to stop calls; arguments a,b, renamed consitent with all.equal 2006-01-20 18:21 burger * NAMESPACE: checkIdentical added 2006-01-20 18:18 burger * DESCRIPTION: patch version 0.4.8: checkIdentical added 2006-01-05 15:09 burger * R/checkFuncs.r: checkEqualsNumeric: update to be compatible with R 2.3.0 2006-01-05 15:08 burger * DESCRIPTION: patch level 0.4.7: update for checkEqualsNumeric to be compattible with R 2.3.0 2005-12-12 10:35 burger * DESCRIPTION: patch level 0.4.6 2005-12-12 10:32 burger * R/htmlProtocol.r: replaced HOST query by supposedly platform independent Sys.info variant 2005-12-05 14:44 burger * R/htmlProtocol.r: system info table format changed 2005-12-05 14:44 burger * R/html.r: writeHtmlTable added; API tags updated 2005-11-21 15:29 burger * DESCRIPTION: patch level 0.5.0 added - temporary - dependency on EpiR.base (arrayTemplate class) 2005-11-21 15:28 burger * R/zzz.r: class & method init currently required arrayTemplate and thus relies on EpiR.base FIXME: remove dependency on EpiR.base once design has matured 2005-11-21 15:28 burgerm * R/zzz.r: file zzz.r was added on branch S4-devel-branch-2006-08 on 2006-08-29 22:54:46 +0000 2005-11-21 15:26 burger * R/Logger.r: first exploration 2005-11-21 15:26 burgerm * R/Logger.r: file Logger.r was added on branch S4-devel-branch-2006-08 on 2006-08-29 22:54:46 +0000 2005-11-21 15:15 burger * R/textProtocol.r: added execTime S3 method 2005-11-21 15:15 burger * R/: TestFileResult.r, TestFunctionResult.r, TestResultMethods.r, TestSuiteResult.r, TestFileResultMethods.r, TestSuiteResultMethods.r: first prototype 2005-11-21 15:13 burger * R/TestResultClass.r: first prototype: virtual base class: the mother of all test results 2005-11-14 13:40 burger * DESCRIPTION: patch level 0.4.5 improvements to error detection in runTestSuite & new test cases 2005-11-14 13:39 burger * tests/runitRUnit.r: added isValidTestSuite, runTestFile, and runTestSuite test cases added test case description 2005-11-14 13:37 burger * R/runit.r: runTestSuite: added preconditions runTestFile: pass on error handler flag 2005-11-14 13:36 burger * inst/examples/correctTestCase.r: used for unit test cases 2005-11-14 11:37 burger * R/runit.r: added codestatus API tag runTestSuite: modified error msg 2005-11-14 11:36 burger * R/checkFuncs.r: added codestatus API tag, set to testing 2005-10-27 10:44 burger * .cvsignore: initial commit ignaore eclipse project file 2005-09-29 14:19 burger * DESCRIPTION: changed Klaus email address 2005-09-29 14:16 burger * inst/doc/Makefile: initial commit: utility 2005-08-30 16:28 burger * DESCRIPTION: patch level 0.4.4: changed maintainer 2005-04-07 16:17 burger * tests/runitInspect.r: added & deactivated 2 test cases: environment issues to be addressed by Thomas 2005-04-07 16:04 burger * tests/runitRUnit.r: DEACTIVATED test added 2005-04-07 16:03 burger * R/inspector.r: includeTracker: modifed regexp in grep to comply to R 2.1.0; removed semi-colons; added docu tags 2005-04-07 16:02 burger * R/checkFuncs.r: checkTrue: argument renmaed to match docu: R 2.1.0 CMD check issue 2005-04-07 16:00 burger * R/runit.r: isValidTestSuite: added check on folder existance; docu tags added 2005-04-07 15:59 burger * R/testLogger.r: setDeactivated: added handling of msg with string length > 1; docu extended 2005-04-07 15:41 burger * DESCRIPTION: patch level 0.4.2: fixed R 2.1.0 inspect regexp problem 2005-02-02 13:38 kjuen * R/: htmlProtocol.r, textProtocol.r: the protocol now doesn't mention test files that do not contain any test functions 2005-01-17 19:03 kjuen * R/htmlProtocol.r: minor formatting modifications 2004-12-13 15:34 burger * R/checkFuncs.r: checkTrue: had to add explicit eval to ensure the argument gets evaluated before attempting to set the names attribute to NULL 2004-12-13 14:58 burger * man/checkFuncs.Rd: updated help text for checkTrue 2004-12-13 14:49 burger * R/checkFuncs.r, tests/runitRUnit.r: checkTrue: extended: correct handling of named logical arguments 2004-12-13 14:48 burger * DESCRIPTION: patch level 0.4.2: corrected deficient checkTrue 2004-11-29 19:53 burger * DESCRIPTION: patch level: 0.4.1: http URL mapped 2004-11-29 18:12 kjuen * R/htmlProtocol.r, man/textProtocol.Rd: 'testFileToLinkMap' parameter added to the printHTMLProtocol function added 2004-09-30 15:19 kjuen * inst/doc/: RUnit.Rnw, RUnit.pdf: typos fixed 2004-09-29 18:53 burger * DESCRIPTION: added methods dependency, again 2004-09-29 14:18 kjuen * DESCRIPTION: changes for new release 2004-09-29 14:17 kjuen * inst/doc/RUnit.pdf: hopefully readable for everybody 2004-09-28 11:11 kjuen * R/htmlProtocol.r: deactivated table cells are now printed yellow 2004-09-22 15:27 burger * DESCRIPTION: patch level 0.3.8: RC 0.4.0; removed package splines dependency; added SaveImage directive 2004-09-22 15:24 burger * NAMESPACE: removed splines import 2004-09-22 15:23 burger * R/00Init.r: added .onLoad hook for loading methods prior to RUnit attachment: recommended for R 2.0.0 2004-09-22 15:23 burger * R/inspector.r: removed library calls for methods & splines; added 00Init.r file for this 2004-09-22 15:22 burger * NEWS: replaced tabs; corrected typo, added splines dependency removal 2004-09-22 14:40 kjuen * NEWS: news for release 0.4.0 2004-09-15 15:07 kjuen * NAMESPACE: DEACTIVATED added 2004-09-15 15:07 kjuen * man/: RUnit-internal.Rd, RUnit-intro.Rd, checkFuncs.Rd, inspect.Rd, printHTML.trackinfo.Rd, runit.Rd, textProtocol.Rd, tracker.Rd: some very small cleanups to avoid warnings with R-2 2004-09-09 12:21 kjuen * R/: htmlProtocol.r, testLogger.r, textProtocol.r: .getErrors completely removed 2004-09-08 18:05 burger * DESCRIPTION: updated patch level to 0.3.7 2004-09-08 18:03 burger * NAMESPACE: added getErrors to namespace exports 2004-09-08 18:02 burger * R/testLogger.r: copied .getErrors to getErrors, added to namespace exports, .getErrors set deprecated 2004-09-08 14:20 kjuen * R/htmlProtocol.r: deactivated column in testsuite table is included only when there are any deactivated test functions 2004-09-07 17:28 kjuen * R/: checkFuncs.r, htmlProtocol.r, runit.r, testLogger.r, textProtocol.r: several small cleanups, DEACTIVATED function added 2004-09-06 15:32 burger * inst/NAMESPACE: removed, moved to from inst/ folder 2004-09-06 15:32 burger * NAMESPACE: moved here from inst/ folder as promoted by R 2.0.0 docu 2004-09-06 15:21 burger * R/runit.r: .executeTestCase: added log output: test function call 2004-09-06 15:13 burger * R/textProtocol.r: added braces 2004-08-05 13:34 burger * R/htmlProtocol.r: added braces; added col.names=FALSE to write.table for printing out R version info 2004-07-13 18:16 burger * inst/doc/RUnit.pdf: created via R 1.9.1 buildVignettes("RUnit", "~/src/R/Runit", quiet=FALSE) call, package tools 2004-07-13 16:07 burger * inst/doc/: RUnit.Rnw, RUnit.pdf: set VignetteDepends; commented out LaTeX dependencies; added bibliography with URL for SF RUnit site 2004-07-13 16:04 burger * DESCRIPTION: added SF URL; fixed vignette PDF problem; updated patch level to 0.3.6 2004-06-29 17:17 burger * R/htmlProtocol.r: added colnames(ver) <- "" to avoid warning in R >= 1.9.0 2004-06-10 11:01 burger * R/htmlProtocol.r: added writeCR to writeP function: creates better structured HTML code, relevant also for the internal built script 2004-06-09 20:40 burger * R/html.r: corrected writeEndHead & writeEndHtml: both wrote start instead of end tags 2004-06-08 11:01 burger * DESCRIPTION: updated patch level to 0.3.4, corrected R CMD check warning on man page files 2004-06-08 09:27 burger * man/: RUnit-intro.Rd, RUnit.Rd: renamed RUnit.Rd to RUnit-intro.Rd 2004-06-08 09:24 burger * man/: RUnit.Rd, checkFuncs.Rd, inspect.Rd, printHTML.trackinfo.Rd, runit.Rd, textProtocol.Rd, tracker.Rd: replaced keyword{RUnit} by concept{RUnit}, suggested by Kurt Hornik 2004-06-07 11:41 koenig * inst/doc/RUnit.Rnw: some formatting changes and better descriptions 2004-06-04 22:44 burger * man/checkFuncs.Rd: renamed f to fun 2004-06-04 22:44 burger * DESCRIPTION: updated patch level to 0.3.3, fixed last failing R CMD check tests: ready for CRAN submission 2004-06-04 22:33 burger * man/tracker.Rd: only example text format changed 2004-06-04 22:32 burger * man/printHTML.trackinfo.Rd: updated to new argument name 2004-06-04 22:31 burger * R/exportHTML.r: fixed error introduced with the check on the successful directory creation: renamed res input argument to trackInfo 2004-06-04 20:16 burger * R/htmlProtocol.r: added R version output at end of HTML page 2004-06-03 17:41 burger * R/exportHTML.r: added preconditions and handling of the case the directory results exists already 2004-06-03 17:40 burger * man/tracker.Rd: modified useage to be consistent with required default name for tracker object 2004-06-03 17:32 burger * man/inspect.Rd: added seealso section 2004-06-03 17:32 burger * man/tracker.Rd: added sealso section, clarified comments, added one sentence to description 2004-06-03 16:54 burger * R/inspector.r: added methods dependency 2004-06-03 16:50 burger * man/RUnit-internal.Rd: added writeBeginHtml & writeEndHtml 2004-06-03 16:37 burger * INDEX: initial commit 2004-06-03 16:35 burger * inst/doc/00Index.dcf: updated 2004-06-03 16:05 kjuen * inst/examples/runitc2f.r: example extended 2004-06-03 16:05 kjuen * man/runit.Rd: small improvements 2004-06-03 15:30 kjuen * man/runit.Rd: random number generator behaviour documented 2004-06-03 15:15 kjuen * inst/doc/RUnit.Rnw: junk character that made texi2dvi fail removed 2004-06-03 15:13 kjuen * man/: runit.Rd, textProtocol.Rd: print and summary documentation improved 2004-06-03 13:59 burger * tests/runitRUnit.r: added test cases 2004-06-03 13:47 burger * tests/: runitHTMLProtocol.r, runitTextProtocol.r: initial commit, simple test cases, currently only checked if all input arguments are checked correctly 2004-06-02 18:53 burger * R/: htmlProtocol.r, textProtocol.r: added precondition checks 2004-06-02 18:51 burger * DESCRIPTION: updated patch level to 0.3.2: updated Seawve file, added preconditon checks 2004-06-02 18:40 burger * inst/doc/RUnit.Rnw: corrected errors in R code which incurred R CMD check error 2004-06-01 18:11 burger * inst/examples/runitc2f.r: initial commit, simple example test case, required for correct automatic Sweave translation 2004-05-27 11:37 koenig * R/runit.r: random generator set to kind=Marsaglia-Multicarry, normal.kind=Kinderman-Ramage (default before R-1.8.1 2004-05-25 11:07 kjuen * inst/NAMESPACE: exports of generic print and summary functions corrected 2004-05-25 11:07 kjuen * R/textProtocol.r: type fixed in summary function 2004-05-25 11:06 kjuen * R/runit.r: code that attempts to clean up the global environment after the test runs removed, because it did not work with R-1.9 (because of some namespace stuff that I do not understand) 2004-05-25 11:04 kjuen * R/htmlProtocol.r: junk code removed 2004-05-19 21:08 burger * DESCRIPTION: updated patch level to 0.3.1, version presented at useR 2004-05-19 14:42 koenig * inst/doc/RUnit.Rnw: E-Mail adresses changed 2004-05-19 14:39 koenig * inst/doc/RUnit.Rnw: company name and E-Mail added 2004-05-19 14:12 kjuen * inst/doc/RUnit.Rnw: some details improved and section that explains test case execution added 2004-05-19 12:48 kjuen * R/testLogger.r: debug print statement removed 2004-05-19 11:26 burger * inst/doc/RUnit.Rnw: added library(RUnit) before example code 2004-05-19 11:24 burger * man/RUnit-internal.Rd: added newline 2004-05-19 11:24 burger * R/exportHTML.r: added API doc tags, code polish for better readability 2004-05-19 11:12 burger * man/RUnit.Rd: updated, added links, removed unused tags 2004-05-19 11:09 burger * man/: tracker.Rd, printHTML.trackinfo.Rd: added CVS header 2004-05-19 11:08 burger * man/: tracker.Rd, inspect.Rd: minor text changes 2004-05-19 11:07 burger * man/printHTML.trackinfo.Rd: fixed spelling of function name 2004-05-19 11:04 burger * man/RUnit-internal.Rd: initial commit, list all private functions not covered in the docs 2004-05-19 09:46 koenig * man/: inspect.Rd, printHTML.trackinfo.Rd, tracker.Rd: initial release for the documentation of the inspector 2004-05-18 23:17 burger * inst/doc/RUnit.pdf: updated 2004-05-18 23:17 burger * inst/doc/RUnit.Rnw: corrected missing end closure 2004-05-18 21:46 burger * man/: runit.Rd, textProtocol.Rd: typo corrections 2004-05-18 21:45 burger * inst/doc/RUnit.Rnw: removed duplicate abstract, typo corrections 2004-05-18 19:58 burger * inst/NAMESPACE: removed Copyright notice, removed class &method export directive 2004-05-18 19:49 burger * man/: RUnit.Rd, checkFuncs.Rd, runit.Rd: added newline in lats line: suggested by R CMD check 2004-05-18 19:47 burger * DESCRIPTION: removed 2nd maintainer entry: not allowed by R CMD check 2004-05-18 19:02 burger * DESCRIPTION: added splines dependency, added poster, added more Rd files 2004-05-18 16:58 burger * inst/doc/RUnit.Rnw: added some lines to Motivation, added Future Ideas 2004-05-18 16:34 koenig * inst/doc/RUnit.Rnw: enhanced for the code inspector 2004-05-18 13:24 kjuen * R/runit.r: sanity check of looking for a 'runit' call in a test function removed because it is not necessary anymore 2004-05-18 13:22 kjuen * R/testLogger.r: a dot prepended to getError and newTestLogger to mark them as internal functions 2004-05-18 13:21 kjuen * R/runit.r: bugfix in .executeTestCase: setUp and tearDown are now checked for errors. typo fixed in the code that copes with errors occuring while sourcing a test file 2004-05-18 13:18 kjuen * R/: htmlProtocol.r, textProtocol.r: trace back writing improved 2004-05-18 13:16 kjuen * man/textProtocol.Rd: documentation of printHTMLProtocol added 2004-05-18 13:15 kjuen * inst/doc/RUnit.Rnw: my version of the introduction added 2004-05-17 19:54 burger * inst/doc/RUnit.Rnw: added abstract 2004-05-17 19:53 burger * inst/doc/RUnit.pdf: initial commit 2004-05-17 15:40 kjuen * R/htmlProtocol.r: some minor modifications 2004-05-17 15:40 kjuen * R/runit.r: default test file regexp in defineTestSuite improved 2004-05-17 15:16 burger * R/: exportHTML.r, html.r, inspector.r: added GPL preamble 2004-05-14 18:38 kjuen * R/textProtocol.r: useless code deleted 2004-05-14 18:38 kjuen * R/runit.r: small bugfix in isValidTestSuite 2004-05-14 18:37 kjuen * R/htmlProtocol.r: first usable version 2004-05-14 18:29 kjuen * R/html.r: small improvement of writeBeginTable 2004-05-11 15:34 koenig * R/exportHTML.r: moved html helper function to html.r 2004-05-11 15:33 koenig * R/html.r: initial release. helper function for generating html pages 2004-05-10 23:09 burger * R/: checkFuncs.r, runit.r, testLogger.r, textProtocol.r: added CVS tag 2004-05-10 22:58 burger * R/runit.r: renamed argument to isValid to testSuite; ts is a time series object 2004-05-10 22:57 burger * R/textProtocol.r: added API documentation tags; renamed arguments to required defaults for print & summary methods 2004-05-10 22:56 burger * man/runit.Rd: added authors and keyword paragraphs; changed ts to testSuite argument name and documented it 2004-05-10 22:54 burger * man/textProtocol.Rd: added authors and keyword paragraphs; added ... argument description 2004-05-10 22:52 burger * man/checkFuncs.Rd: added authors and keyword paragraphs 2004-05-10 22:22 burger * inst/doc/RUnit.Rnw: fixed broken LaTeX code: missing begin environment 2004-05-10 22:21 burger * inst/doc/00Index.dcf: initial commit, required by R CMD check; needs to be updated with this directories contents 2004-05-10 22:07 burger * DESCRIPTION: updated to minor level 0.2.0: CodeInspector code added, documentation pages added; package passes R CMD check 2004-05-10 22:05 burger * man/: checkFuncs.Rd, runit.Rd, textProtocol.Rd: changed examples such that no Errors are thrown, and packages passes R CMD check 2004-05-10 18:51 kjuen * R/: 00Init.r, initGeneratedRUnit.r, utilities.r: removed 2004-05-10 18:49 koenig * R/inspector.r: initial release for tracking tool 2004-05-10 18:48 koenig * R/exportHTML.r: initial release for exporting results to HTML pages 2004-05-10 16:52 kjuen * man/: checkFuncs.Rd, runit.Rd, textProtocol.Rd: first attempt to cope with Rs documentation tool 2004-05-10 16:51 kjuen * R/runit.r: test suite objects now have a class attribute 2004-05-10 16:50 kjuen * R/testProtocol.r: renamed to textProtocol 2004-05-10 16:50 kjuen * R/textProtocol.r: renamed from testProtocol 2004-05-07 17:51 kjuen * R/checkFuncs.r: ... added to checkEquals so that further args can be passed to all.equal 2004-05-07 15:45 kjuen * R/testProtocol.r: S3 generic methods 'print' and 'summary' added 2004-05-07 15:44 kjuen * R/testLogger.r: getErrors improved 2004-05-06 20:46 burger * DESCRIPTION: added GPL 2 licence text, some code improvements 2004-05-06 20:43 burger * COPYING: GPL 2, downloaded from http://www.gnu.org/copyleft/gpl.html 2004-05-06 19:39 kjuen * R/testProtocol.r: license header added, optional args added to configure printTextProtocol 2004-05-06 19:38 kjuen * R/testLogger.r: license header added, traceback removed for Failures 2004-05-06 19:37 kjuen * R/runit.r: license header added, file regexp in runTestFile improved 2004-05-06 19:37 kjuen * R/checkFuncs.r: license header added 2004-05-05 21:35 burger * R/runit.r: changed to default set in R >= 1.8.0 2004-05-05 20:08 burger * DESCRIPTION: updated version to 0.1.0, runit test framework from EpiR.tools revised and commited here 2004-05-05 19:30 kjuen * R/: checkFuncs.r, runit.r, testLogger.r, testProtocol.r: initial check in of rewritten version 2004-04-06 11:27 burger * DESCRIPTION, R/00Init.r, R/initGeneratedRUnit.r, R/utilities.r, inst/NAMESPACE, inst/doc/RUnit.Rnw, man/RUnit.Rd, tests/runitRUnit.r: Initial revision 2004-04-06 11:27 burger * DESCRIPTION, R/00Init.r, R/initGeneratedRUnit.r, R/utilities.r, inst/NAMESPACE, inst/doc/RUnit.Rnw, man/RUnit.Rd, tests/runitRUnit.r: initial import: preparation for public CRAN package, will take on most of EpiR.tools functionality �����������������������������������������������������������RUnit/man/������������������������������������������������������������������������������������������0000755�0001762�0000144�00000000000�13267374743�012106� 5����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/RUnit-intro.Rd����������������������������������������������������������������������������0000644�0001762�0000144�00000003631�13267374743�014572� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{RUnit} \alias{RUnit} \title{RUnit - Package Description} \description{ This package models the common Unit Test framework for R and provides functionality to track results of test case execution and generate a summary report. It also provides tools for code inspection and thus for test case coverage analysis. The design is inspired by the popular JUnit unit test framework. This package comes with a set of unit tests, serving as a test battery to check correct functioning against new R versions released as well as practical examples for writing test cases (see the \file{inst/unitTests} subdirectory of the source package, or \file{unitTests} contained in the binary package version). } \references{RUnit - A Unit Test Framework for R. useR! 2004 Vienna} \author{ Thomas \enc{Ko\"nig}{Koenig}, Klaus \enc{Ju\"nemann}{Juenemann} \ifelse{html}{\out{&}}{&} Matthias Burger} \seealso{ See \code{\link{defineTestSuite}}, \code{\link{runTestSuite}} for unit testing or \code{\link{inspect}} and \code{\link{tracker}} for code inspection. } \keyword{programming} \concept{RUnit} �������������������������������������������������������������������������������������������������������RUnit/man/checkFuncs.Rd�����������������������������������������������������������������������������0000644�0001762�0000144�00000016061�13267374743�014455� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{checkFuncs} \alias{checkEquals} \alias{checkEqualsNumeric} \alias{checkIdentical} \alias{checkTrue} \alias{checkException} \alias{DEACTIVATED} \title{RUnit check functions} \usage{ checkEquals(target, current, msg, tolerance = .Machine$double.eps^0.5, checkNames = TRUE, ...) checkEqualsNumeric(target, current, msg, tolerance = .Machine$double.eps^0.5, ...) checkIdentical(target, current, msg) checkTrue(expr, msg) checkException(expr, msg, silent = getOption("RUnit")$silent) DEACTIVATED(msg) } \arguments{ \item{current, target}{objects to be compared (\code{checkEqualsNumeric} cannot handle S4 class objects).} \item{msg}{an optional message to document a check and to facilitate the identification of a possible failure. The message only appears as text in the test protocol, it is not further used in any of the check functions.} \item{tolerance}{numeric >= 0. A numeric check does not fail if differences are smaller than `tolerance'.} \item{checkNames}{flag, if \code{FALSE} the names attributes are set to \code{NULL} for both current and target before performing the check.} \item{expr}{syntactically valid R expression which can be evaluated and must return a logical scalar (\code{TRUE}|\code{FALSE}). A named expression is also allowed but the name is disregarded.} \item{silent}{flag passed on to \code{try}, which determines if the error message generated by the checked function is displayed. Queried from global options set for RUnit at package load.} \item{...}{optional arguments passed to \code{all.equal} or \code{all.equal.numeric}} } \description{A set of functions used to check the results of some test calculation. If these functions are called within the RUnit framework, the results of the checks are stored and reported in the test protocol. \code{checkEquals} compares two R objects by invoking \code{all.equal} on the two objects. If the objects are not equal an error is generated and the failure is reported to the test logger such that it appears in the test protocol. \code{checkEqualsNumeric} works just like \code{checkEquals} except that it invokes \code{all.equal.numeric} instead of \code{all.equal} \code{checkIdentical} is a convenience wrapper around identical using the error logging mechanism of RUnit. \code{checkTrue} uses the function \code{identical} to check if the expression provided as first argument evaluates to \code{TRUE}. If not, an error is generated and the failure is reported to the test logger such that it appears in the test protocol. \code{checkException} evaluates the passed expression and uses the \code{try} mechanism to check if the evaluation generates an error. If it does the test is OK. Otherwise an error is generated and the failure is reported to the test logger such that it appears in the test protocol. \code{DEACTIVATED} interrupts the test function and reports the test case as deactivated. In the test protocol deactivated test functions are listed separately. Test case deactivation can be useful in the case of major refactoring. Alternatively, test cases can be commented out completely but then it is easy to forget the test case altogether. } \details{ The check functions are direct equivalents of the various methods of the class junit.framework.Assert of Javas JUnit framework which served as basis for the RUnit package. For functions defined inside a package equipped with a namespace only exported functions can be accessed inside test cases directly. For functions not exported the only way to test them is to use the '\code{\link[base:ns-dblcolon]{:::}}' operator combined with the package name as a prefix. Special care is required if test cases are written for S4 classes and methods. If a new class is defined inside a test case via a \code{\link[methods]{setClass}} call the class is added to the global class cache and thus available outside the test case. It will persist until explicitly removed via a \code{\link[methods:findClass]{removeClass}} call. Same applies for new method and generic definitions. Be sure to remove methods and classes in each test case they are defined after the checks have been performed. This is an advise gained from the cumbersome experience: not doing so leads to difficult to pin down error causes incurred from previously executed test cases. For a simple example see the provided test cases in \Sexpr{file.path(system.file("examples", package="RUnit"), "runitVirtualClassTest.r")}. } \author{ Thomas \enc{Knig}{Koenig}, Klaus \enc{Jnemann}{Juenemann} \ifelse{html}{\out{&}}{&} Matthias Burger} \seealso{ \code{\link{all.equal}}, \code{\link{all.equal.numeric}} and \code{\link{identical}} are the underlying comparison functions. \code{\link{try}} is used for error catching. \code{\link{.setUp}} for details on test case setup. See \link{RUnit-options} for global options controlling log out. } \examples{ checkTrue(1 < 2, "check1") ## passes fine ## checkTrue(1 > 2, "check2") ## appears as failure in the test protocol v <- 1:3 w <- 1:3 checkEquals(v, w) ## passes fine names(v) <- c("A", "B", "C") ## checkEquals(v, w) ## fails because v and w have different names checkEqualsNumeric(v, w) ## passes fine because names are ignored x <- rep(1:12, 2) y <- rep(0:1, 12) res <- list(a=1:3, b=letters, LM=lm(y ~ x)) res2 <- list(a=seq(1,3,by=1), b=letters, LM=lm(y ~ x)) checkEquals( res, res2) ## passes fine checkIdentical( res, res) checkIdentical( res2, res2) ## checkIdentical( res, res2) ## fails because element 'a' differs in type fun <- function(x) { if(x) { stop("stop conditions signaled") } return() } checkException(fun(TRUE)) ## passes fine ## checkException(fun(FALSE)) ## failure, because fun raises no error checkException(fun(TRUE), silent=TRUE) ## special constants ## same behaviour as for underlying base functions checkEquals(NA, NA) checkEquals(NaN, NaN) checkEquals(Inf, Inf) checkIdentical(NA, NA) checkIdentical(NaN, NaN) checkIdentical(-Inf, -Inf) ## DEACTIVATED("here one can document on the reason for deactivation") } \keyword{programming} \concept{RUnit} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/runit.Rd����������������������������������������������������������������������������������0000644�0001762�0000144�00000023366�13274056520�013534� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{runTestSuite} \alias{runTestSuite} \concept{test suite} \alias{runTestFile} \concept{test runner} \alias{defineTestSuite} \alias{isValidTestSuite} \title{Definition and execution of RUnit test suites.} \usage{ defineTestSuite(name, dirs, testFileRegexp = "^runit.+\\\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Marsaglia-Multicarry", rngNormalKind = "Kinderman-Ramage") isValidTestSuite(testSuite) runTestSuite(testSuites, useOwnErrorHandler = TRUE, verbose = getOption("RUnit")$verbose, gcBeforeTest = FALSE) runTestFile(absFileName, useOwnErrorHandler = TRUE, testFuncRegexp = "^test.+", rngKind = "Marsaglia-Multicarry", rngNormalKind = "Kinderman-Ramage", verbose = getOption("RUnit")$verbose, gcBeforeTest = FALSE) } \arguments{ \item{name}{The name of the test suite.} \item{dirs}{Vector of absolute directory names where to look for test files.} \item{testFileRegexp}{Regular expression for matching test files.} \item{testFuncRegexp}{Regular expression for matching test functions.} \item{rngKind}{name of an available RNG (see \code{\link[base:Random]{RNGkind}} for possible options).} \item{rngNormalKind}{name of a valid rnorm RNG version (see \code{\link[base:Random]{RNGkind}} for possible options).} \item{testSuite}{A single object of class test suite.} \item{testSuites}{A single object of class test suite or a list of test suite objects.} \item{useOwnErrorHandler}{If \code{TRUE} the RUnit framework installs its own error handler during test case execution (but reinstalls the original handler before it returns). If \code{FALSE} the error handler is not touched by RUnit but then the test protocol does not contain any call stacks in the case of errors.} \item{verbose}{level of verbosity of output log messages, 0: omits begin/end comments for each test function. Queried from global options set for RUnit at package load.} \item{absFileName}{Absolute file name of a test function.} \item{gcBeforeTest}{Run garbage collector before executing a test for more precise test timing. Enabling this option makes the tests run longer, especially when testing many small tests. By default GC is disabled (since 0.4.32).} } \description{ \code{runTestSuite} is the central function of the RUnit package. Given one or more test suites it identifies and sources specified test code files one after another and executes all specified test functions defined therein. This is done sequentially for suites, test code files and test functions. During the execution information about the test function calls including the possible occurrence of failures or errors is recorded and returned at the end of the test run. The return object can then be used to create a test protocol of various formats. \code{runTestFile} is just a convenience function for executing the tests in a single test file. \code{defineTestSuite} is a helper function to define a test suite. See below for a precise definition of a test suite. \code{isValidTestSuite} checks if an object defines a valid test suite. } \details{ The basic idea of the RUnit test framework is to declare a certain set of functions to be test functions and report the results of their execution. The test functions must not take any parameter nor return anything such that their execution can be automatised. The specification which functions are taken as test functions is contained in an object of class \code{RUnitTestSuite} which is a list with the following elements. \describe{ \item{name}{A simple character string. The name of a test suite is mainly used to create a well structure test protocol.} \item{dirs}{A character vector containing the absolute names of all directories where to look for test files.} \item{testFileRegexp}{A regular expression specifying the test files. All files in the test directories whose names match this regular expression are taken as test files. Order of file names will be alphabetical but depending on the used locale.} \item{testFuncRegexp}{A regular expression specifying the test functions. All functions defined in the test files whose names match this regular expression are used as test functions. Order of test functions will be alphabetical.} } After the RUnit framework has sequentially executed all test suites it returns all data collected during the test run as an object of class \code{RUnitTestData}. This is a (deeply nested) list with one list element for each executed test suite. Each of these executed test suite lists contains the following elements: \describe{ \item{nTestFunc}{The number of test functions executed in the test suite.} \item{nErr}{The number of errors that occurred during the execution.} \item{nFail}{The number of failures that occurred during the execution.} \item{dirs}{The test directories of the test suite.} \item{testFileRegexp}{The regular expression for identifying the test files of the test suite.} \item{testFuncRegexp}{The regular expression for identifying the test functions of the test suite.} \item{sourceFileResults}{A list containing the results for each separate test file of the test suite.} } The \code{sourceFileResults} list just mentioned contains one element for each specified test function in the source file. This element is a list with the following entries: \describe{ \item{kind}{Character string with one of \code{success}, \code{error} or \code{failure} describing the outcome of the test function.} \item{msg}{the error message in case of an error or failure and \code{NULL} for a successfully executed test function.} \item{time}{The duration (measured in seconds) of the successful execution of a test function and \code{NULL} in the case of an error or failure. When running with \code{gcBeforeTest} option set to \code{FALSE} (default since 0.4.32), the timing of the tests might be misleading when garbage collector has to reclaim memory allocated by a previous test.} \item{traceBack}{The full trace back as a character vector in the case of an error and \code{NULL} otherwise.} } To further control test case execution it is possible to define two parameterless function \code{.setUp} and \code{\link{.tearDown}} in each test file. \code{.setUp()} is executed directly before and \code{.tearDown()} directly after each test function execution. Quite often, it is useful to base test cases on random numbers. To make this procedure reproducible, the function \code{runTestSuite} sets the random number generator to the default setting \code{RNGkind(kind="Marsaglia-Multicarry", normal.kind="Kinderman-Ramage")} before sourcing each test file (note that this default has been chosen due to historical reasons and differs from the current R default). This default can be overwritten by configuring the random number generator at the beginning of a test file. This setting, however, is valid only inside its own source file and gets overwritten when the next test file is sourced. } \value{ \code{runTestSuite} and \code{runTestFile} both return an object of class RUnitTestData. \code{defineTestSuite} returns an object of class \code{RUnitTestSuite}. } \author{ Thomas \enc{König}{Koenig}, Klaus \enc{Jünemann}{Juenemann} \ifelse{html}{\out{&}}{&} Matthias Burger} \seealso{ \code{\link{checkTrue}} and friends for writing test cases. \code{\link{printTextProtocol}} and \code{\link{printHTMLProtocol}} for printing the test protocol. See \link{RUnit-options} for global options controlling log out. } \examples{ ## run some test suite myTestSuite <- defineTestSuite("RUnit Example", system.file("examples", package = "RUnit"), testFileRegexp = "correctTestCase.r") testResult <- runTestSuite(myTestSuite) ## same but without the logger being involved ## source(file.path(system.file("examples", package = "RUnit"), ## "correctTestCase.r")) ## test.correctTestCase() ## prints detailed text protocol ## to standard out: printTextProtocol(testResult, showDetails = TRUE) ## use current default RNGs myTestSuite1 <- defineTestSuite("RUnit Example", system.file("examples", package = "RUnit"), testFileRegexp = "correctTestCase.r", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion") testResult1 <- runTestSuite(myTestSuite) ## for single test files, e.g. outside a package context testResult2 <- runTestFile(file.path(system.file("examples", package = "RUnit"), "correctTestCase.r")) printTextProtocol(testResult2, showDetails = TRUE) } \keyword{programming} \concept{RUnit} ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/RUnit-internal.Rd�������������������������������������������������������������������������0000644�0001762�0000144�00000002775�13267374743�015263� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \name{RUnit-internal} \alias{includeTracker} \alias{plotConnection.trackInfo} \alias{writeBeginBody} \alias{writeBeginHead} \alias{writeBeginHtml} \alias{writeBeginTag} \alias{writeBeginTable} \alias{writeCR} \alias{writeEndBody} \alias{writeEndHead} \alias{writeEndHtml} \alias{writeEndTable} \alias{writeEndTag} \alias{writeHtmlEnd} \alias{writeHtmlHeader} \alias{writeHtmlSection} \alias{writeHtmlSep} \alias{writeImage} \alias{writeLi} \alias{writeLinkRef} \alias{writeLink} \alias{writeP} \alias{writeRaw} \alias{writeRawCR} \alias{writeTableRow} \alias{writeTitle} \title{Internal functions} \description{ Helper functions and private methods not intended for direct use. } \keyword{internal} ���RUnit/man/testCaseSetUp.Rd��������������������������������������������������������������������������0000644�0001762�0000144�00000002754�13267374743�015141� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{.setUp} \alias{.setUp} \alias{.tearDown} \title{Definition of RUnit Test Case code files.} \usage{ .setUp() .tearDown() } \description{ Either one or both functions have to be provided by the test case author, take precedence over the dummy definitions provided by the RUnit package and are called once for every test case identified. } \details{ To be written ... } \value{ Functions do not return a value; called for their side effects. } \author{ Thomas \enc{Knig}{Koenig}, Klaus \enc{Jnemann}{Juenemann} \ifelse{html}{\out{&}}{&} Matthias Burger} \seealso{ \code{\link{runTestFile}}. } \keyword{programming} \concept{RUnit} ��������������������RUnit/man/RUnit-options.Rd��������������������������������������������������������������������������0000644�0001762�0000144�00000004415�13267374743�015133� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{options} \alias{RUnit options} \alias{RUnit-options} \title{RUnit options} \description{ RUnit uses three options available via the global R options list } \details{ RUnit specif options are added to R's global options list on package loading and removed again on pachage unloading. } \section{Options used in RUnit}{ \describe{ \item{\code{silent}:}{logical flag, default \code{FALSE}, sets the 'silent' argument for \code{checkException}. Allows to globally silence output from exception checks for all test suites excuted in one run.} \item{\code{verbose}:}{non-negative integer, default \code{1}, \code{0}: surpresses enclosing begin/end messages for each test case, \code{1}: output enclosing begin/end messages for each test case} \item{\code{outfile}:}{\code{NULL}, connection or character, default \code{NULL}. If non-null has to be an open connection or a file name. Will be used to redirect all output to specified file/connection using \code{sink}. Connection is close after test suite execution call (via \code{runTestSuite} or \code{runTestFile}) has completed. If the file exists it is overwriten.} } } \author{Matthias Burger} \seealso{\code{\link{options}}, \code{\link{getOption}}, \code{\link{sink}}.} \examples{ \dontrun{ ## quiet log output ro <- getOption("RUnit") ro$silent <- TRUE ro$verbose <- 0L options("RUnit"=ro) } } \keyword{programming} \keyword{environment} \concept{RUnit} ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/tracker.Rd��������������������������������������������������������������������������������0000644�0001762�0000144�00000007346�13267374743�014042� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{tracker} \alias{tracker} \title{Tracking the results of the inspect process.} \usage{tracker() } \description{ The current implementation uses the 'closure trick' to hide all details from the user and only allows to retrieve the results of the code inspection. \code{tracker} is used to create a new environment to manage and store the results of the tracking process. The \code{inspect} function requires such an environment with the name "track" (currently mandatory). The tracker records how often each and every function was called by \code{inspect} and summarizes the results of all calls. \code{tracker$init} initializes the tracker environment. \code{tracker$getTrackInfo} returns a list with the tracked results of the inspection process. } \section{Methods}{ \tabular{rll}{ \tab \code{init} \tab initializes the tracker environment\cr \tab \code{addFunc} \tab add function to the inspect tracking list (internal use)\cr \tab \code{getSource} \tab return the modified source code used for during inspection the specified index (internal use)\cr \tab \code{bp} \tab update tracking info for specified function index (internal use)\cr \tab \code{getTrackInfo} \tab return 'trackInfo' object\cr \tab \code{isValid} \tab check 'trackInfo' object for conformance to class contract \cr } } \details{The 'trackInfo' S3 class object (list) has one entry for each function on the inspect list with the following elements: \describe{ \item{src}{The source code of the function.} \item{run}{The number of executions for each line of code.} \item{graph}{A matrix. Each element in the matrix counts how often a code line was called from the previous code line in the execution flow.} \item{nrRuns}{Counts how often the function was called.} \item{funcCall}{The declaration of the function.} } } \author{Thomas \enc{Knig}{Koenig}, Klaus \enc{Jnemann}{Juenemann} \ifelse{html}{\out{&}}{\&} Matthias Burger} \seealso{ \code{\link{inspect}} for the registration of functions \& methods to be on the tracking list, and \code{\link{printHTML.trackInfo}} for displaying results } \examples{ ## example functions foo <- function(x){ y <- 0 for(i in 1:100) { y <- y + i } return(y) } bar <- function(x){ y <- 0 for(i in 1:100) { y <- y - i } return(y) } ## the object name track is 'fixed' (current implementation) track <- tracker() ## initialize the tracker track$init() ## inspect the function ## resFoo1 will contain the result of calling foo(50) resFoo1 <- inspect(foo(50), track = track) resFoo2 <- inspect(foo(20), track = track) resBar1 <- inspect(bar(30), track = track) ## get the tracked function call info for all inspect calls resTrack <- track$getTrackInfo() ## create HTML sites in folder ./results for all inspect calls printHTML.trackInfo(resTrack) } \keyword{programming} \concept{RUnit} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/textProtocol.Rd���������������������������������������������������������������������������0000644�0001762�0000144�00000015421�13267374743�015106� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{textProtocol} \alias{printTextProtocol} \alias{printHTMLProtocol} \alias{printJUnitProtocol} \alias{print.RUnitTestData} \alias{summary.RUnitTestData} \alias{getErrors} \title{Printing a plain text, HTML or JUnit-like XML version of an RUnit test run protocol.} \usage{ printTextProtocol(testData, fileName = "", separateFailureList = TRUE, showDetails = TRUE, traceBackCutOff = 9) printHTMLProtocol(testData, fileName = "", separateFailureList = TRUE, traceBackCutOff = 9, testFileToLinkMap = function(x) x ) printJUnitProtocol(testData, fileName = "") \method{print}{RUnitTestData}(x, ...) \method{summary}{RUnitTestData}(object, ...) getErrors(testData) } \arguments{ \item{testData, x, object}{objects of class \code{RUnitTestData}, typically obtained as return value of a test run.} \item{fileName}{Connection where to print the text protocol (printing is done by the \code{cat} command).} \item{separateFailureList}{If \code{TRUE} a separate list of failures and errors is produced at the top of the protocol. Otherwise, the failures and errors are only listed in the details section.} \item{showDetails}{If \code{TRUE} the protocol contains a detailed listing of all executed test functions.} \item{traceBackCutOff}{The details section of the test protocol contains the call stack for all errors. The first few entries of the complete stack typically contain the internal RUnit function calls that execute the test cases and are irrelevant for debugging. This argument specifies how many calls are removed from the stack before it is written to the protocol. The default value is chosen such that all uninteresting RUnit calls are removed from the stack if \code{runTestSuite} has been called from the console. This argument takes effect only if \code{showDetails=TRUE}.} \item{testFileToLinkMap}{This function can be used to map the full name of the test file to a corresponding html link to be used in the html protocol. By default, this is the identity map. See example below.} \item{...}{additional arguments to summary are passed on to the printTextProtocol() call.} } \description{ \code{printTextProtocol} prints a plain text protocol of a test run. The resulting test protocol can be configured through the function arguments. \code{printHTMLProtocol} prints an HTML protocol of a test run. For long outputs this version of the test protocol is slightly more readable than the plain text version due to links in the document. The resulting test protocol can be configured through the function arguments. \code{printJUnitProtocol} prints a JUnit-style XML protocol of a test run. This feature is especially useful when running your RUnit tests through a continuous integration server that understands JUnit (like Jenkins). This machine-parseable output allows you to track errors over time, sort by execution time and similar useful tricks. To take advantage of this function, you have to have the XML package installed. \code{print} prints the number of executed test functions and the number of failures and errors. \code{summary} directly delegates the work to \code{printTextProtocol}. \code{getErrors} returns a list containing the number of test functions, the number of deactivated functions (if there are any), the number of errors and the number of failures. } \details{ The text protocol can roughly be divided into three sections with an increasing amount of information. The first section as an overview just reports the number of executed test functions and the number of failures and errors. The second section describes all test suites. Optionally, all errors and failures that occurred in some test suite are listed. In the optional third section details are given about all executed test functions in the order they were processed. For each test file all test functions executed are listed in the order they were executed. After the test function name the number of \code{check<*>} function calls inside the test case and the execution time in seconds are stated. In the case of an error or failure as much debug information as possible is provided. } \author{Thomas \enc{Knig}{Koenig}, Klaus \enc{Jnemann}{Juenemann}, Matthias Burger \ifelse{html}{\out{&}}{&} Roman Zenka} \seealso{ \code{\link{runTestSuite}} } \examples{ ## run some test suite myTestSuite <- defineTestSuite("RUnit Example", system.file("examples", package = "RUnit"), testFileRegexp = "correctTestCase.r") testResult <- runTestSuite(myTestSuite) ## prints detailed text protocol ## to standard out: printTextProtocol(testResult, showDetails = TRUE) ## prints detailed html protocol ## to standard out printHTMLProtocol(testResult) ## prints JUnit-style XML protocol ## to standard out. ## You need to have XML package installed for this if(require("XML")) { printJUnitProtocol(testResult) } \dontrun{ ## example function to add links to URL of the code files in a code ## repository, here the SourceForge repository testFileToSFLinkMap <- function(testFileName, testDir = "tests") { ## get unit test file name bname <- basename(testFileName) ## figure out package name regExp <- paste("^.*/([\\.a-zA-Z0-9]*)/", testDir,"/.*$", sep = "") pack <- sub(regExp, "\\1", testFileName) return(paste("http://runit.cvs.sourceforge.net/runit/", pack, testDir, bname, sep = "/")) } ## example call for a test suite run on the RUnit package testSuite <- defineTestSuite("RUnit", "<path-to-source-folder>/RUnit/tests", testFileRegexp = "^test.+") testResult <- runTestSuite(testSuite) printHTMLProtocol(testResult, fileName = "RUnit-unit-test-log.html", testFileToLinkMap = testFileToSFLinkMap ) } } \keyword{programming} \concept{RUnit} �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/printHTML.trackinfo.Rd��������������������������������������������������������������������0000644�0001762�0000144�00000005276�13267374743�016207� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{printHTML.trackInfo} \alias{printHTML.trackInfo} \alias{printHTML} \title{Write HTML pages of the tracking result.} \usage{ printHTML.trackInfo(object, baseDir = ".") } \arguments{ \item{object}{'trackInfo' S3 class object (list), containing the result of the function \code{tracker}.} \item{baseDir}{A character string, specifying the base directory for the HTML pages to be written to. Defaults to the current working directory.} } \description{ \code{printHTML.trackInfo} creates a subdirectory named "result" in the base directory specified via \code{baseDir}. All HTML pages and images will be put in that directory. } \details{An "index.html" page will be created in the directory "results" which is the root entry page of the HTML pages. The displayed result for every tracked function consists of two HTML pages. The first page is an overview on how often every line of code was executed. Code lines not executed are highlighted red, executed lines are shown in green. The second page is a graph representation of the execution flow of the function. Each code line has a edge pointing to the next code line that is executed subsequently. Thus loops and jumps become clearly visible. } \author{Thomas \enc{Knig}{Koenig}, Klaus \enc{Jnemann}{Juenemann} \ifelse{html}{\out{&}}{&} Matthias Burger} \seealso{ \code{\link{tracker}} for the call tracking object definition. } \examples{ ## example function foo <- function(x){ y <- 0 for(i in 1:100) { y <- y + i } return(y) } ## the name track is necessary track <- tracker() ## initialize the tracker track$init() ## inspect the function ## res is the result of foo res <- inspect(foo(10), track = track) ## get the tracking info resTrack <- track$getTrackInfo() ## create HTML pages printHTML.trackInfo(resTrack) } \keyword{programming} \concept{RUnit} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/inspect.Rd��������������������������������������������������������������������������������0000644�0001762�0000144�00000005126�13267374743�014046� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%% RUnit : A unit test framework for the R programming language %% Copyright (C) 2003-2009 Thomas Koenig, Matthias Burger, Klaus Juenemann %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; version 2 of the License. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA %% $Id$ \encoding{latin1} \name{inspect} \alias{inspect} \title{Track the executed code lines of a function or method.} \usage{ inspect(expr, track = track) } \arguments{ \item{expr}{Any R function or method call.} \item{track}{list object, as returned by a call to \code{tracker}.} } \description{ \code{inspect} examines and modifies the source code of a function or method. After the modification of the source code, the modified function will be executed and the result of the tracking process will be stored. To store the information a \code{tracker} environment with the name track must exist. Note, that not all R code constructs can be handled at the current state. In some cases it is not possible to track a specific code line. Therefore, clearly structured code with consequent use of opening and closing braces to indicate conditional expressions can prevent these parser problems. } \details{The return value of \code{inspect} is the result returned by the function executed. If the function has no return value nothing is returned either.} \author{Thomas \enc{Knig}{Koenig}, Klaus \enc{Jnemann}{Juenemann} \ifelse{html}{\out{&}}{&} Matthias Burger} \seealso{ \code{\link{tracker}} for the call tracking object, and \code{\link{printHTML.trackInfo}} for displaying results. } \examples{ ## example function foo <- function(x){ y <- 0 for(i in 1:100) { y <- y + i } return(y) } ## the name track is necessary track <- tracker() ## initialize the tracker track$init() ## inspect the function ## res will collect the result of calling foo res <- inspect(foo(10), track = track) ## get the tracked function call info resTrack <- track$getTrackInfo() ## create HTML sites printHTML.trackInfo(resTrack) } \keyword{programming} \concept{RUnit} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/.Rinstignore����������������������������������������������������������������������������������0000644�0001762�0000144�00000000022�13274056044�013616� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������inst/doc/Makefile ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������