RUnit/0000755000175100001440000000000012617030554011334 5ustar hornikusersRUnit/COPYING0000644000175100001440000004313112614544635012400 0ustar hornikusers 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/0000755000175100001440000000000012617014016012304 5ustar hornikusersRUnit/inst/examples/0000755000175100001440000000000012614544635014136 5ustar hornikusersRUnit/inst/examples/runitVirtualClassTest.r0000644000175100001440000001011312614544635020653 0ustar hornikusers## 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: runitVirtualClassTest.r,v 1.3 2009/11/04 16:54:13 burgerm Exp $ ## 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.r0000644000175100001440000000173112614544635017420 0ustar hornikusers## 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: correctTestCase.r,v 1.3 2009/04/16 09:23:14 burgerm Exp $ test.correctTestCase <- function() { checkTrue( TRUE) checkTrue( !identical(TRUE, FALSE)) } RUnit/inst/examples/runitc2f.r0000644000175100001440000000313512614544635016057 0ustar hornikusers## 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/0000755000175100001440000000000012614544635013422 5ustar hornikusersRUnit/inst/share/R/0000755000175100001440000000000012614544635013623 5ustar hornikusersRUnit/inst/share/R/compareRUnitTestData.r0000644000175100001440000001077012614544635020055 0ustar hornikusers###################################################################### ## 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: compareRUnitTestData.r,v 1.4 2009/11/04 16:52:47 burgerm Exp $ 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.r0000644000175100001440000001174212614544635015663 0ustar hornikusers###################################################################### ## 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: checkCode.r,v 1.3 2009/11/04 16:48:23 burgerm Exp $ 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/0000755000175100001440000000000012617014016013051 5ustar hornikusersRUnit/inst/doc/RUnit.R0000644000175100001440000000440412617014016014237 0ustar hornikusers### 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.pdf0000644000175100001440000034533412617014016014621 0ustar hornikusers%PDF-1.5 % 40 0 obj << /Length 1816 /Filter /FlateDecode >> stream xr6Б$ŝe\yzhz)jKBRv|m(Jskz-x{׷(LSm&D)&Y>I4|2_L : Sl,T0Gc4˃6:ō]X \šn[MTIQRadlVZ(w+; & 'ify6Ed$7V?^|$\YFxN ¸  ^(=o-Xm0 rk8&da|Uf%zܠd&1(Sˆ=i:&jx{΀nY#kG ;+F+"p7:S߲cPv+Đ#+ uZ0Ί( >8ԃ` ǪG=\de 3`(K €~C6Gn?NӘ<d ^FPטTM$I9*(! @rlV!׮fO_O@LYR#7h1 Z{ Zp18ÞRLxEwy=ء;DAFv+[ظP:;`%kDZ֐ PTփ[[w @\38'D ݈iIbiz9uP2Jg6 j םM ܕuZ{ $bi q=/9Pgkx{9KpCSsل%i&xSG2jL3 |"Ooo[T-6@j)5`r'5`dT5[xk)ԑ9ek<](`dEZP,ie vNNƏMȪill_+^lc Wh~J<;ұ0HgRjIgY_Du]*/AǕ޾rj8K/h#ֈ%5D^'T()H#=ȕk ˦(E[A\\2r8쭽CE ;VaɏLpsa8.T7X^01_u.r󵙅0N29* N¼y_~`cЕAWe]> 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 940 /Filter /FlateDecode >> stream xXKS0+9.\ٸ30 b$')WqÎ}݅Pɘ31~9 -s9n񋈁 .3odBTl!v 4x݇HV7 { p@;\81:0y-$ʎ^ "*^fSi@9*\O3ZFKAYѶx +plrKZ<R[>5 đpsjVQ/)KߚUGbW6#[6/ RIۼϕ(lTyIlSBjU'JYȔa?ZOޔΊF)YLuwŅV7Wڟ Ji]۩4NRfQC3G2J@a{j6W3deO'@̗EҸ,tk äReCKv@k,x`׋zo5(>.FC#FnbLA;&4iOGPr&35-1Tbo|;ߛ;#6M! 494*I5)ONз0$w endstream endobj 99 0 obj << /Length 1542 /Filter /FlateDecode >> stream xڭWIoFWj1AP$=zhz%VF޷BlÙ7owWolTZ͖H˼ƦefFX%Ɠ<˒%kOlC%kAu\j[]0o2Hi(\$^Poz숥גa+DiOM-[}޼/3Rk(crWLdisGU.6-+GN8Iw96 Ö* b[0 H"vcK8!rfnي\@ݢpӲ!<_wP\s+qA>WfDn%q,@~~j' ۑU8_Z  sEf+AYTKD%Oސ@B۠ӛ6UNxu:Mdyv+d3O`b;pQLr/tVBe#ZLv/Ul"#A `"0kfh|'y\8/"Mt}G CqÙoRm|ߡo|} w~ܻZ}Pūw )9廝pNHm &۔:*ʥ{Ho> stream xڵWێ6}߯0>Hh(Y-ZEhR(AemA=s#ul䁶DR33gɟWWOlT"C$̢x,^zr|U:hgh~*2=_~_D@%a΄ѱ=СBcEa~vm F)ZN':6u2(Ѷ]Pq.,sG-m5ba4e!?2nq()Npwᯠmxhf/ nÖA®ygzWsIhnęd%qeطLEFݓ76< 3ٌLRyLǵ SyоNK>3-̄j uqq*J..}LAD}!6Nqx7X+2oґE*LB֫qgK;km=Ph[K$fE;XIgL0蓵ki5TE hQ=rs" =<y:d= IZ`蜏PpxۈyQWs@Za8@,`(Ӽ:noÃħ%ds *[$7_6 f#Cvx 䈑*)Jh8[:euHG `@=Ǩ7V ^ԗ8PG~*ܖvpn,6NhJi)ugJiߖz_D~[)pIqɗP2hz3hJa뭵jц~$K̖V5P''Qx~a [ـB~_ZJd-z "\%ÐX;C@7ڢR`?uY³T=5i&U,4~Pj;~iϒPSSms 4Zm΍4ދ^h\*A҉dwEV$ܪ-OUFs*,~ɡj'h7xx&-J3Yt> stream xڽUˎ0+XU ̣JM.즳`0%!2}$Ҫ#]}{l>˕MnvdXФvݱ7,=_] %ߖ"ߑv0bN]` S3/ao90)4:QIr݄w ZTi=wU׼37i94. '@bw uwGN2R? M!(Y(E@M'Ȩ0Y\[&4+"=c/0z_! kEG=\U= {R\5sB3{81BKלwp焇#ͺuךrg Kx.6B+k;:x4uN ޱt^R}b<)ʤUu3@znqaUCxe2vS >QJG]}\|>"^h8bS-ku^!!Y 9r>]z+vo_EP~AChp$n`xN؟*v=}7 Seq}UOkWw8CO5a)3mes8).+?wMŧ'Oh# 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ա\xpߗ?Ѽ+qY>P-$bk#frxՆ]*Qډ(6OaSFcmEFB B7Xgjw؟$rOHG*r\ Ļ" *Rpw+m, .͕G\M#b~:M4&Cʯk X_yy*뛕i6hfWË k_[I,[ƶAWxl 0Ϭf] k#lKtz`ÂyFfd -~X`쓦vqm cժ~hX=v5yE G;͖p, 83s ]gfk BV pHJ ,x8XKYdy 3:Chn7J#2:ލne4)֏ gW{1{Dkz5-T~ֺ-$IDFcFQn#܂[K艜{ۀ}nwkX3=/}o*Fs*{ mHpRzK "=|znD\lIg î\gec1+kѽټTE )˩M{5o:bK>;[G5>ugz^Ov9O{gjYn,$ـz3/@nt,[2| G-+/T `X4tSh|PXg ,e&.4NE$>] M,O3Sm9Ąj7ύ%=ITztxF/IkCydN1on-g鳳eĽshɃ>g-hEi!yiy S9sOoQxbsLՄ&@jI߳%S+=/34}n~ccX2xn;vZ+O}j1fN usy,$BCaDPoCO9<ӫS%+:rFh`0㉬(IuBaSSIH`z`7i h" i1]d r&lYVcaIf6#6md44DȲdk.7&.{ Jc_uoTݏܽÓӓwgRA{S3%^-Şl#N#(\~uY68e~'ۯz7 o?6ž }vG6ß}M?#~.=u/5.[~}~|ȇrT _% Gc>%=?hm6R^!^{xԆ!C0z+m 2/HIX)?;TwWr}(z<+gp0*Ge]jwGpG҄n}K#?TpaQMçR|:.?ú> ܿ{c|xvtj?9ط88MA2qk[tXv|RDh??e{c/oa)6]->.R.LsR&9&JN!yAfUU&w(Gi8S]ι]0{N&>19q8fTv:e]/<]bNU7G?]kvc|˫WgpakrOG=in֭nuM=EۥȞ`JHfOecx nI$OkI]m81ݔݿ5޼}cDdzb2ZNNO}wGo-B?'+jh(((ZC/K ՠ77Z6שRi7^kS%<[ZUkzM{P1)4"ݛkQVW:{v#_~%X]NTmǏ6nVG79_D;fb endstream endobj 145 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.15)/Keywords() /CreationDate (D:20151105210134-06'00') /ModDate (D:20151105210134-06'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) kpathsea version 6.2.0) >> 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 [<2458F75BCCD1085ED8C537B10E2EB5B6> <2458F75BCCD1085ED8C537B10E2EB5B6>] /Length 363 /Filter /FlateDecode >> stream x%2CqsDމN--cX[Y0 w%cg~_GT_~Te! U ^ك1¤JjS0 0a *W-l@6 S>۴GK*CT3UTog>oyDrhG38 fp4\y?/̅6 endstream endobj startxref 116849 %%EOF RUnit/inst/doc/RUnit.Rnw0000644000175100001440000005041112617014016014603 0ustar hornikusers% -*- 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/0000755000175100001440000000000012614544635014322 5ustar hornikusersRUnit/inst/unitTests/Makefile0000644000175100001440000000051312614544635015761 0ustar hornikusersTOP = ../../.. 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.r0000644000175100001440000000740412614544635016634 0ustar hornikusers## 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: runitSetUp.r,v 1.2 2009/04/16 09:18:52 burgerm Exp $ 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.r0000644000175100001440000000473312614544635020064 0ustar hornikusers## 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: runitHTMLProtocol.r,v 1.3 2009/04/16 09:18:52 burgerm Exp $ 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.r0000644000175100001440000000522412614544635020240 0ustar hornikusers## 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: runitTextProtocol.r,v 1.3 2009/04/16 09:18:52 burgerm Exp $ 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.r0000644000175100001440000000401712614544635020344 0ustar hornikusers## 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.r0000644000175100001440000005267212614544635016644 0ustar hornikusers## 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: runitRUnit.r,v 1.18 2010/09/15 13:40:25 burgerm Exp $ 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) } if (require(Biobase)) { ## class still available? #if (isClass(Class="ExpressionSet", formal=TRUE)) { # ES <- new("ExpressionSet", exprs=matrix(runif(1000), nrow=100, ncol=10)) # checkEquals(ES, ES) #} ## cleanup workspace ## catch error if this ns is required by some other package ## and therefore cannot be unloaded try(unloadNamespace("Biobase")) } ## 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))) if (require(Biobase)) { ## class still available? if (isClass(Class="ExpressionSet", formal=TRUE)) { ES <- new("ExpressionSet", exprs=matrix(runif(1000), nrow=100, ncol=10)) checkException(checkEqualsNumeric(ES, ES)) } ## cleanup workspace try(unloadNamespace("Biobase")) } } 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))) } 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))) ## testSuite } RUnit/inst/unitTests/runitS4.r0000644000175100001440000000574312614544635016066 0ustar hornikusers## 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: runitS4.r,v 1.2 2009/04/16 09:18:52 burgerm Exp $ 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.r0000644000175100001440000000550012614544635020525 0ustar hornikusers## 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: runitPlotConnection.r,v 1.3 2009/04/16 09:18:52 burgerm Exp $ 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.r0000644000175100001440000000562712614544635017660 0ustar hornikusers## 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: runitTestLogger.r,v 1.1 2009/11/05 18:24:38 burgerm Exp $ 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.r0000644000175100001440000001114312614544635017174 0ustar hornikusers## 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: runitInspect.r,v 1.6 2009/04/22 13:52:15 burgerm Exp $ 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.r0000644000175100001440000000634112614544635017316 0ustar hornikusers## 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: runitTearDown.r,v 1.2 2009/04/16 09:18:52 burgerm Exp $ 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.R0000644000175100001440000000056412614544635017032 0ustar hornikuserslibrary("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/0000755000175100001440000000000012614544635012505 5ustar hornikusersRUnit/tests/README0000644000175100001440000000012112614544635013357 0ustar hornikusers unit tests have been moved to inst/unitTests in the source package structure RUnit/NAMESPACE0000644000175100001440000000212612614544635012563 0ustar hornikusers###################################################################### ## ## RUnit ## ===================================== ## ## inst/NAMESPACE ## ===================================== ## initialization of classes, namespace, ... ## ## ## ## ## Version ## ===================================== ## $Id: NAMESPACE,v 1.7 2009/04/22 13:46:38 burgerm Exp $ ## ## ###################################################################### 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/NEWS0000644000175100001440000001421312616742143012037 0ustar hornikusers Dear Emacs, please make this -*-Text-*- mode! ************************************************** * * * RUnit * * * ************************************************** Changes in RUnit 0.4.31 o checkEquals and others output optional message on separete 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/0000755000175100001440000000000012616742143011540 5ustar hornikusersRUnit/R/checkFuncs.r0000644000175100001440000001723112616742143014003 0ustar hornikusers## 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: checkFuncs.r,v 1.24 2009/11/05 18:57:56 burgerm Exp $ 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.r0000644000175100001440000003537712614544635013754 0ustar hornikusers## 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: inspector.r,v 1.12 2009/04/22 13:50:56 burgerm Exp $ 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.r0000644000175100001440000001013312614544635014600 0ustar hornikusers## 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.r0000644000175100001440000002117112614544635014437 0ustar hornikusers## 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: textProtocol.r,v 1.18 2009/11/04 17:02:44 burgerm Exp $ 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.r0000644000175100001440000003112612614544635012676 0ustar hornikusers## 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: html.r,v 1.10 2009/11/04 17:00:39 burgerm Exp $ 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�0001751�0000144�00000002605�12614544635�012775� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: 00Init.r,v 1.9 2009/11/05 18:53:26 burgerm Exp $ 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�0001751�0000144�00000041503�12614544635�014420� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: htmlProtocol.r,v 1.28 2009/11/04 17:01:23 burgerm Exp $ 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�0001751�0000144�00000025742�12614544635�014060� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: testLogger.r,v 1.19 2009/11/05 18:54:46 burgerm Exp $ .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�0001751�0000144�00000023205�12614544635�013737� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: exportHTML.r,v 1.12 2010/09/15 13:37:20 burgerm Exp $ 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�0001751�0000144�00000035145�12614544635�013100� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: runit.r,v 1.34 2010/09/15 13:39:09 burgerm Exp $ 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())) 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) { ##@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 ##@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.") } 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) ## 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) { ##@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) ##@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)) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/R/options.r�����������������������������������������������������������������������������������0000644�0001751�0000144�00000003023�12614544635�013420� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: options.r,v 1.2 2009/11/25 15:03:54 burgerm Exp $ .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�0001751�0000144�00000000000�12617014016�013337� 5����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/vignettes/RUnit.Rnw���������������������������������������������������������������������������0000644�0001751�0000144�00000050411�12614544635�015105� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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�0001751�0000144�00000004515�12616742143�012623� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# RUnit [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/RUnit)](http://cran.r-project.org/web/packages/RUnit) [![CRAN Downloads](http://cranlogs.r-pkg.org/badges/RUnit)](http://cran.rstudio.com/web/packages/RUnit/index.html) [![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�0001751�0000144�00000005337�12617030554�011654� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������393a5ca445f6965873eca0259a17f833 *COPYING 281d731d8ba9ab81f394e842759da685 *ChangeLog 0c8558d2b4a2dafbe3d419e9ef62a76d *DESCRIPTION 79cdb4ebb332557b1bbe6d22ed814d50 *NAMESPACE 89b6e9ff07eacb633a3a39fc106314d8 *NEWS 6f6c449932f994f1ec522abb07c80077 *R/00Init.r b4daf13e937eb14c0e09a01a5d4d7e50 *R/checkFuncs.r 2b27ad0825731bf744d03b0d830726ce *R/exportHTML.r 52af1699c74f3e57d41e3dd54b1c415f *R/html.r 00d8e5f1c87b07d7fbfa772e5e6f598d *R/htmlProtocol.r 7ac7594c426ea3d545e52dfefb2d2e1c *R/inspector.r dac8cdcb71213d0766b2e7421cb6d5e4 *R/junitProtocol.r 51b5853cf915e1284223a4f3983d4e16 *R/options.r 489c6fbeef2964f4c2d2715b330a0816 *R/runit.r 123dce1b90ecfdc3a92e7f5422cb4143 *R/testLogger.r 7666fa4f5cab063f9d80fcb29bd8a980 *R/textProtocol.r ad99ba7ff13411496d3150a0c4639b2d *README.md 727c1fe4588b65e6c51d3691d3d1f20d *build/RUnit.pdf 23be9921de8f8974985abacdb651cb5a *build/vignette.rds 0a39e751dd8135b2d4c01453941b442a *inst/doc/RUnit.R d1eae6df10a7e5dfc7ff1ced0bd99e81 *inst/doc/RUnit.Rnw e5042d19d29e20d9a48abd87d66d4417 *inst/doc/RUnit.pdf 24ce7c881152c2dcb423ed5b4778c05a *inst/examples/correctTestCase.r bd1d2d6f724f8e4ca6850777117ce771 *inst/examples/runitVirtualClassTest.r 3df11b61be3a51c4cd3f2aaa62b4c005 *inst/examples/runitc2f.r 240d6cee4f1573a2c58e479782fd2650 *inst/share/R/checkCode.r d8977975a727b9e21ecd46bde613e5d5 *inst/share/R/compareRUnitTestData.r e97e7c5dda56971910efb622ab8c71b5 *inst/unitTests/Makefile 87db5d27b6c4cc1dfd0182e6d1872113 *inst/unitTests/runalltests.R c38bdecfb8bfbc23c47fe975958e5ffd *inst/unitTests/runitHTMLProtocol.r 79fb47dfe1de6125e45a7e0d05ab9f70 *inst/unitTests/runitInspect.r 3416f47fd504913d182aaba77ffdd14d *inst/unitTests/runitJUnitProtocol.r b95015dba685cdb5ed475b4f1e24d924 *inst/unitTests/runitPlotConnection.r 470b1e9922b6f0fbc8230b6753b47e8e *inst/unitTests/runitRUnit.r c195d76dcc403c1027b7de9582dc2da3 *inst/unitTests/runitS4.r 7f3284997c1e5d05db28dcb6f559b763 *inst/unitTests/runitSetUp.r 462f1500a164ccc019d41ec536c3dbb0 *inst/unitTests/runitTearDown.r ae03e3cc47242ddd15f8f1fb22025791 *inst/unitTests/runitTestLogger.r 9b051b625a2e4340e6ba40f5fc792357 *inst/unitTests/runitTextProtocol.r fcb628af12c9233488e9de5f470988a1 *man/RUnit-internal.Rd d4b4703fc3fcde728beacff836ea7dd6 *man/RUnit-intro.Rd 5b614202c65a7a14af4f375e708e2965 *man/RUnit-options.Rd d4e342bdef2a0cf18c7bf0fdc3e40cec *man/checkFuncs.Rd 224bcae67a9855b9b25af2c898f65ba8 *man/inspect.Rd 4c6eec61cfb428733ceac28fedd255d5 *man/printHTML.trackinfo.Rd 0b1b76de7b7be6b6f9a1045b02325f29 *man/runit.Rd 1043f42f218e1d65e80558944f05258f *man/testCaseSetUp.Rd df33dce58369cb8caeee20d51e0285d3 *man/textProtocol.Rd c9e39e93f8e0b2ec462c334268e1a06a *man/tracker.Rd aad073d2afa8c98edc05a00dddc8dac7 *tests/README d1eae6df10a7e5dfc7ff1ced0bd99e81 *vignettes/RUnit.Rnw �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/build/����������������������������������������������������������������������������������������0000755�0001751�0000144�00000000000�12617014016�012426� 5����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/build/vignette.rds����������������������������������������������������������������������������0000644�0001751�0000144�00000000374�12617014016�014771� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mPJ1M6kVB_?a)/ekhm`s! ,['k"n4093!"RR1t 46ݻP#N*pXuW#MWVFzK %rTyPg= )a/FU{ NΙJ̱:s0.WRN=2l&v'7 mS\rMrM o8ľ�Rx����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/build/RUnit.pdf�������������������������������������������������������������������������������0000644�0001751�0000144�00000351217�12617014014�014171� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%PDF-1.5 % 55 0 obj << /Length 1082 /Filter /FlateDecode >> stream xX[6~_[a @vgM8NӴ3%hv{@]{mLo~0|.wΑvvK:0ܙ:1 ibJY|po<$]&scǰ^7_`$rAsǧq̭#4֎Zu蓐 & Pk7]i\m60 #z2hl^&sb z%HXՙGb7WEt6Ql w3/TӥI Q~jfySkzOi6q�:ETËƴKc3rA1crESҍI孆wH|ȸ}!+pToVIS\hǤoԊnED "t@D4Vb^! HQXǸ;\F .N-; YstJuq~AXlRx䪆3D)@PR-yB.ԟ[1dZ"Hf8XD[X'N1AH{/Q'VxKYJU.$*?1?m qn|U+$!87I�G$ǧm'59t/ד� O*5yYhPҔ[mT*ɋr% \U,1پ :`}v]l|E%Fϩ1WU);1YjӣJ&y^UPSx)OTJXp<ħV~[)g]*9DZҔ #57݁ ,F 5% C"kk.Rtڴ} b VU~_z~H"y>ix:B<yo WbǴvzu| !ݒ#\|B�;*҅LP@fD){ ڑﻓTV(.t9,d""\ƿή'6p}n5 ٩otTO` )۟-0Z5y ;�VQ "N{W]Tç~Bۣ]2`)Џ:>|8t<a]A ѐE8'Oһ̙_fg h 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 2610 /Filter /FlateDecode >> stream xڥko8{EX6zRR= M-=\;#ѶZYr(i7/ɒ !9 Ig3_,uR嫳s]'Y\' u~E3ٗʚVWA<ʉ7@y$|8[\^~ H9iS/Xfy{j 5MS-u5>-ӍaH7 ~tkrVt񶚐Qxqu[gu3%<Hhn8Hh;\4-l.*Z�_a.]H$B[MX5a!$,}[oHLW$T,^-h#@@j0PۢA/.JJLyB$yAx' 1s,MH-8]w0( 񗾻A!SL^plr{K["W,�6r{CHm ;c۶+('[3}O< y^vlr#F.0Y *Ѿپ"Iԉc5Kcu?:98ū9#/pDb}RpXafO) FHYU#B 洵A>q0d<'PSDfæ?UE3~Qƍ#~d#Cm59@<'$LRH a8 $( U˜ݜgQLoU;q:佇V:R<5lUU|U) y.+u# i+$)+xl+HKR`(+3JibpmDPE Kv=iIƦ>ciZtN'!= 0m#1FZ_fY3=fT2 Bov.t%!Q< IW.C9 4c�|QTbr.hY₇aOh7ţb#�@11Yk�.KZ9g3=TOV k11 0H˙'Ny=WZ`MuBa ^HFRFOܾP9!7ucHb�޾軉Zn > =Tslc\j+ab)"58, EIَ uqIw5n͟ޞY3 ZE 'AP,flI[kn|IlsOs! F6uqA$Z,֦ VX:v)ǮV%3�DjqMZ,-wsB?|]h:p0΀^:t&pRW(`k*c}踏('PLvѨ]+4CYVIGy@kk=o`.6p0C\ff<n΃ξp`RUӯ|Ta((-: ra5?^6K~0m稹N9դ:ANk7<_jK% ,id ?m{Œa=4u=t)܁8 Zqm8pGvNkwC,7MS<ݧ �>?o)DntSHv>Ʌ#gS"v>#'+$-u07XNW65fE6sh::M`\*F`P2qm=Cm\L3{+`xT (mgxT@dQe Bs$:#K(s3xׯ Jԁvx[7E۟\&sR`Yol`be‰ rI$U%óg1;[x MAÞJ(KT&rO%z?e27sep HE;2N(@_ YO{J4"մ@8M,x)A@DE=$ otdMb򝓇Q,JMmzB+ڃ� \][+H&!ltW̋oEV`d9k5N7H;g x㥃G"ݯAf<6]x-9:)Z?riѣDYM{} GY6pAIY3ٯQtjj|Fsr<z tFSqPO(2~t8SV7KUEÓ;-aWa#FASPjעzso6TJ| ߀-,0b[e浴zP]@ĖJQDTe~@/DD,AFR#f;Α #\(<0SyM ٬�0*S-3]\м)wbZ8X|[^�*VR#6u*G$<P6-mݷ/jn߫56#+<VC)IӬ,.ʶPgS8~s<//zZ 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 1752 /Filter /FlateDecode >> stream xYn7}߯cBq Hv\yQV*]i-˒y* {9g )RFye ʱ*fEFeVdqPp&g%bR̡`VbX4rT(1MKesP6*'/r(GʅT�"n7B*W[>FLō#O{R # %A`PAd;ɪpDkL*[*L.;&7D_L `0rgs|>cb"#YOc2,Bb|؉Ʒ 3e+t #[PBdĶbQFcV01rxx2v3l,#1'Gy'Ƃ$.b0^ N28xy!@XIzy\Pf I0`QhHxOudn$s;ԁa܄ 4V 8`/h!e8[2|TXVЃE̵8:RS5uv6SWꗓ֋pz{9\/byǤZ\z _ήɬ^^'U[]/_Tg~;^C557qeuV..g{|VƳ=r9:wMz#7w0p NxR_}.姲UuY x�I-r!2<yW^M._̾s#Jtƙc�c~^U3 w.'_vmBĶImƛi_H7_usvR})F/fr3ף7|Ƶ:&h |R(F-6{{ in;,]YspI~.%79XMO?|Mf<w9G` ܁n8SH99 \9@#Ct"tՔO@Z2DCQX\hxDnԁ|lQrs`#Բ`lp+.6$?D=QS-&UN/@QX%XncfZVJU ȬQZ]7Os#$i !:V3$|bNr$RDl[j1Q)ՠ*9ޭJdZnB]a-/{! Cc6sO%^X2zA僊ZҞYjEX+Mreq499cXW'eQжN|N-U-6: BHo6:`gtaj=}t6=.9bPQؤ/pL*i/';- Jv8[ŞDž~`vN0TXٗ۱"'3 #92j[Zju$&|t˿|0qNjO8)Q!J+,d<u\ Z TAI}J=>==BKc ؉)/l/`<l"mLx8u ry}淭{&fsV`P~Zܪ+@w$К$ANa#Na f>{"=.z wD,3NRl.?X9죔-=G= 27;_jmO0<1notooŰ V':6Ew>i2chCaZa#Ŋȧ2Ž '6Y`m!YSKH6]c_n 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 1863 /Filter /FlateDecode >> stream xXo8 _{ppkl/P%ձ^HQrlCQHfH)y̛|E27Y<[mgAϒsVGjxl Ʌ^&1UD,O<c=Ey$Fle)Vw$.B !s8 Em[捬J=b ~DqWwy+ Z՜Eqo+5>*Z ֗vԩј}7"wy Oy!7w9*eugE~Y&rK#/i(`cX9 |ܐ> b x,Hw5߉AT6~fa8'+A</"6STBb)vˑh?p7l>A>$s< G# UC+WmȜ+u=;ו:"HSX8 \H`7踦Oj?)`UBJM)Sr|C7ꟅZW>=h1p;8&mS|2rjE/;uק0 zxL.GPL;܇ۃ($Jrc&Jǰnvȶ "-Ff�?M֭,OxDJ;T)N A(VE`Qs}^(zEcQU4UOV!f~]i/Oxo bGp7/P T{Y[O9ն7(n1 j[, . KȫM$sәi,vS?|WT `wn;VNJK@rX>`_={&U#}Ɛw$7G&B?#cN~N|n2ӡ)ZX;i P Y;7K|z A97+&dxiW0&YD52M<l�n9 sHz|%qɲnxQ$t?*CUİ7"4Mk;fF vso}5W4ЙFlBQ_`cɒDiv '2>uV\[^2mnuyϷf#MR p/L&SV ;݆k"ɝoP`/nFQUMW7nDn*.Pog9j̉K{*jjByT:5a=\=c+#"kxk3 L:P^�ǚY lڑW=v<O9fہ_PRBUcZbP2yfόG87o~(+|Sox[go&"TV~=efw? 7?pּ9MFpQĄmCbԆjah(_jɅ2 tzBkqCqu)�R(g&5;kMzcU*""ȍ#?`0ZT_] \_E qFC%p!O%oN} hj1&5'L0f&km*6ZP֌A9Qc<i!A;n`$xmړ7#I8F7|kDt4~4ŖD][:+Gb[ ;1Qct=Ts~ ,u e <5<Cnu m(d +1([sYF”;8h!nHe6V[ NWB[\Qm.̻XuǕV]@VCA@&9/: >ʚBCZyڥ0R"-}@ `4(aw0|Փ endstream endobj 206 0 obj << /Length 2536 /Filter /FlateDecode >> stream xڽ]۸=C!NYI/9rEN_>m"K.%^}g8C}Y%hřۜ6o/{'MꦑmǍ<W&<7f_lo߽ SF }m9ї/<>aфN1R$ޕzP'uFgl[^ow"tN}iĎ~ժm˦&O4Ίp}<RI/t^W!0pfV8D!vRktiMk&f'! PgIu>.8 n$L+ޗ} &20HB18E ؄F^̪9<)"+ C9l}; �S>&aзQ�.aH>)8;r0Q__XGR$sm#`mMHR3BPѡȓwX Bk%DŽ~26fL&gfhCS>OƧ�F>6 xT@`8E^&`1 ؗ\R8(`;<R z6uA![?tr4aQ\ozUweVUiTw<+2bk jӪu@#"2MUA�Su^X3a%]mysheUֶ!N}X@ME>C5wc3^U@X�^$�wgfVlԾtE$Q�7c2�5BY{iaQWjAH.*4o.+GY0ZUq?Y7w{Hp {jr0W5d [hq9Wr{ћ2ћCBV{ ,Q֍n-yjS梂 Yߡ,I>K#F- kj,5KYFwES*n yR6Ih-&%7K<OPE6m-G"K}u8uC!oг (lI3 8 {@zX=ˬ~2B$vxr'koGv`Tyxa0Ī#N:�d bұ$B(%yI#$TXs@ #rli_.0繘h?|?uq7 ;ĿY c(B:Ih9++[y`>UfrxR!4BqZ,`r 8R#js]aP˛ط]]) Y/{{#ʥ=ݵ?qx^Zmvbya AA+i'+e?Mc8#^s>sߍUC./ꮲbCNb^=<Nb2D'4O7}aP<2Z^50_4%-98Btq8E>5M,Y=)uyIT_a'VMHfܖ|[q ?<$072E7,<q}cD{.lmEbDAvhT7M V;oz`~m;Tlۈ 6;@&flKw bWL>\ׂ88Eʆn 4|@?PH8aDNe汾b8CUеMHH1CbBC,uob`hUR J4pz["]hxUo{BӚ߹!Rr_'�3*߾UU\!ya~5И}ƻ+k3=\&ub �Eyi1D޽;'cR8 +'U+Hl OQk} =v k>E߿|iQ2۽jei%XRDїr '`^ 5/̝1OSS ΝS/JE^SB m :rPA9vk-�@ 5Ԇ$uQpTGQWy~&̢?:1y ཤ|uu"|M <نϔJ\nv$-F6%(k~& .Hbb;?MDu[iɳذU vѪSkV|mY0{eD W䞾fpRw|Jղ)v�iJ 5Ž5?US_SM �6 Wǣxɗ$-6S<,m2ц/P~QƯk9}'dj;U1:VG籸&O@Ln><ԍGUO#,zFs1J6"}f8\lGEui#N?8d�ȟ}A,P*'o/  endstream endobj 212 0 obj << /Length 1466 /Filter /FlateDecode >> stream xWI6WE*F=M&m�82- #K.IH#zmQ`z1j/^/~,/.+P+7Cũ!J"k/'|{W}Ryqe38(VX,Bdәl`5e@}?{\Mpl21[mYuUn "\ްn-fptH&Ut}~j Q<%sQ>q<GxJqs';UD9Ad�;FEjTJF{)B[>i2]#~/NVH'J 궿zR}NᵽD_j`Wto8CS_#A LInl= 0Йw̌ Zzmh,8 +A% mM?Il3} k&d;iZAGϸxS]UzY%U?Qf �gVL tH:a4Yϒ�#5{m?N4�8S3nE6u^͏+ܪɿrYѝq*T1:PU",u#%=Gr8g2 5rk) BgQl:&`Y>z �a W^*j9'M֮[:7 D}^]w=6Kw &*:kaBhfg R:C5+'r$u2삜ŅMcrAp,;8>!ID(+Pu p�+yy]*.Ò<k7ӗ[G@EEhѝCӒŪj^Qv(Jc)2uycG"SEb۷vB xD)Uf>Z7'jmL1(E-$|7֝u2Vg}ۦӿ-{) >%jnx_2QFu'۰'~\HD)I2>lLc tV T"&~ %5 Yd- lNڠ|CR\ݾPoUG=@ܯ\o¶)"W^@fy~$&~x"5 3 3b{X"/uF-3O3J~0n@oXJ\sX1"5HHg$aVE[ޙ]ϙY献e̺ά[ӑA S}^VED/W†C-喝:3~̸d?╮l_!Vnj2 ymM$͈%xxBI 6Q9pkp\dbϪ"7Aa͖uf:뱞NXÄ?>4Z&B6՜`ܲj4D[Tm}o U/p endstream endobj 225 0 obj << /Length 1998 /Filter /FlateDecode >> stream xXK6W2f$RCM H@ۃlkueУ eIlr*z1!g</|^>{JūTZ |_PbH6_rWTZ :oMDg>O6*,FH$ _7z#{mja!@I#o{,ZZ;ecQ9kZJfw4ʲ^ȻeIWd/j1 o,N ںa;f+ M7A (bF%U9LRkׁW2ko͡/iSem׉JBdeYeL$BʡJҏZl' waȓ߾ɼ38Ҫ #߃u{ J)GGDDR1cSTm$J\vWKҭ*8h5XYLeX|R=eߘ9͒`bm۟NYs`Yʋ#75z ]0^%tͭՙ>I(t"DzL-6K:JelL762b,l!i̅!@0E؆B+?qlf"،R667, 58 i8&00rwX }Qnr FQ Z8?4mK"PhuaQ_2~- _}ueBp@M`"؜n}xyTcc});Ŋo^@~=&RE}I-kO۷o>jRFU_~MQݼwpШ'x4$k^x2ݱYTd_f-pbPD+4,PU߲ t(m42 ~e!{S:M7áob0]8süJdZBk^'@6ԢH](m]Ș@m7F\XC.$M�OQHJ;{^w;졶Wz/Ѩ񕽙}fM_30>i<Ù؎2ARz*N-8#n/nJl<K0uq"=z-V;>1�!dLO ;jQH}pxjR=ÂpaYR~6a}1eS (3';WYG#A s(N<\.ҩEv[f> g6G{-fbsE𣥴~bwy9Zq RL)bp29C}V2}5qw>ihz}fޒzEV3?4S<Ӱ6mɰ%SLeCc_;Ꚃҙc_4t.=/NdD.FLyzMDx6=uPeҴ;ړS[vnHf t>R4 ޙ]Lcf/5䨠"?-@ `F}w6`±vWKђ%Ls4D<4$00Ӱ`Muz{+RQ(sky*}u< n_Q0 *X'eO㾟~byx$VCH}_tf _A!C?flΐݤ#ip; %h֏8lgZ|8 ueS?Q L{z*Imٕq _x+" ֺ];.PNx}+f UΉ4[lj$<ť s_C1iuc/;Y)U78SUUIe *GCȣ3Z2qO F! *:f%3" ?u}V =dy^`!hLOn:agG9PWSr'u쌝QC4pٿĭ 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 1608 /Filter /FlateDecode >> stream xYn[7+l7y HvF$C{Ecʒ,wxIEuE NEʮTɎG8Z?9V%9 <u1 М�r%d+&1 &A7bj>�D!\" P4\`F54$ `Tr0W8ȣtKpzX*F7TSE52 ͦhd| @B8=iAB sKlzH*.8Dc&&T4SW2ՠT !T J,'Lq ` ``N@a@0f`� ]KbtHQd2)X:)`Nj�Dk$|i3mDR6' ai5aEDif68-S0V#qr)?+g8gfC4tTQЀW<Wf$#h$ `TM &HV4<r3lNb!`oo0|#o(K_}qw/S0nO(Xx{fJ>FT7i,Kjޟgno {ߘSk pTqDFnétކc7|j 3w;?^ /1c7]"?}>tiwg~]w6:y1ꎍV2+}1"Yh>O ϺFii`xxY7go _ǾcNg -!Kz3U*ݡMTMONϻϦ$3JMrf/ek,bo I| e`D(č}L6.P3쌝 Z\ۍ`F @Z \7A5gAP>Pbgjb*۰R_e!FhuëѬ[a(^w<pa6%>f4C[""4Lj<Qc4gEm ;�Vt6r#`NWs+T<'؋kzV1eNɎG[L5"I$/vh! Gm&⋝0jvd%!&-!bNRר\6bObj㠄7 6vONěg;)m;vm€?~֝L_M/9:7ػ |VnGIή~#ݳq .Iݑ̟t>G;%Э32éswz~4fyBnnp%_*xfFfɗ-ZH6)[JR'$&Jo޽>%bl4AE*b9<),T-x4g'b6/6]wkN%<gSOC*quwՙt{jV%UUNػRYm`A+ JEJv;&*Q!,oTb>f<cu*5'~ x̶ *V5_4I}ո|H?)X Y.<ĸ&lip(Xl L>nS;r: eaƹq =o )'/Y[f&; 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 2690 /Length2 13639 /Length3 0 /Length 14994 /Filter /FlateDecode >> stream xڝTU6Jw,RKB@:Ewtt7ҍH7HHtYx>3yy<sR3ٙ�Ll̬�9٘BMrO@G'@ vJ8!"Icg# 4qظXٹl6s(L-6�uKcG{ #ҙ#֔fg[ o G'g- fS;[:W@6{V5?39ZOqZ[E]L̘f.t+L`'lt)!�S1�@GH�&?ӑ{l<�ZKgg{~?~TN`3˟)H$lm`g'6V`�QX�f@b%cgG;@ '}H6/ʐXd%Ԕ{)^^�&6�+ pƠa}q̕!оB0V?{7? OdžQ:k^vic[?֊ gs6-lZ4h r68)9IMb/% trKWJ)#3 0vt4@av..��Cr0!.�{gH("GO `A\� `|A<� `~A|�'+EX^;EA(  \_ pQ AƢ _$ d|A_$ yA|B/29%dcK !bg#b8P1yAcSk' | )h4/|[fܐMBw& d?�iˀ E?r-Ti38@/[y}_@(r5Xz[_8@Dh #}flϿԐ̙~I bk3?@{! h>_;+2{IAgož"px .vKK_y!xA"Br1gKG,<K./ji x Hd!-| tgݝ..9MXAt,ٙ [ֈ1ms/|d)/\Wr|.$.*nxp5]SJu<5 ʚUFzgHt0 "‹뭢6"ifR(.{YFDK2kɪ[}ڵnGΞӸsARul5wJD@�a+22L8$@"_8ʕUVrOeq@P ^`ѩh)&2#L~Kr|ñ-mKF -ivam Ŗ^*ٵ&,kkKDr7w:)49!PJaO9*y|բՎ`v8 l(ٺs|#FD&~,|~#Zb4a tx s +}\%:eϒxiaOE4CҸ4COƹ'hr{qqQQ4%~ښPs d:d79 X^//kx#<w|S*e!vm'RV6'ԏSvAp)f3]}p$pl*l=Zzt_Mv2ďC'ѯ?rqgȞE90J>ɗ1<N kcEM8}'N@'bLMr[%ARj-.փ�)ӊKWHZs1,v7sG;gBgx#~{yM3=X׮tNi}ujLtA\T7+te& NhG&PnuAOq|!Mwv{ZrNWږxW|>Lm %pM)-zлjeBڬKQ(YtEsʀ&)W 퀜uǸQ( ʻ<7}kRJQ~@ǩE_f[W;TҟiQ[ ϰ^vU) o}I`.GḴ֜ y3}pv;le[{Ut̔HMk}Sr],CcO|X7}<ꢧg8UFqHz;_1]C<mHd&5$-Ҁ 5v+R#\;f܋+ϡ%<LG du[Y,<yD&} 75a?%ƱTtp_uUt4wx>0qy08nQ{$&&>Ze (S|N`c Ȱ_#QY9<s3ʤ_ ԋtN|,?YUB+4chL9}B'V K} {ODiՅ6A"HvT[pncmvNt Dh=vhI-+Ν.Qq8&"qҽhdj$] 7:9y d̑żw%h7 .lr Lp_ޖUOVHRiҩNed4qc4~{n}\w x&`0g}sklK (n3m=L83<޷ܥ&I nu]tgQ&xcTR<w2#w.lmYo$?,jrSjmgmVDD-^N0VO\e\hX';ib.<tS:RsBY6u2L:^v8ik(-W=k91ѷL9oh7zZ< >q,~fge-ڇ]/ <O<WzXQ4]@w1K|;JygHL/ ~3c23,gIّZ#x1X%z,JxvB6G<͕`LT>{mSv6iѨ_h坕Z};:MzÝkD5Sa)aʓx  oJ&:?s {y y%ё-hTjEI*'W|Gu6þVS7 ay3DM ̵KAȑWd^U~;}0~C {=߹03(z =h?} Or]&pzt9NNKeܔ=;If@.�(uJb*+Kg՛nҠgxD7ݐOkL Fo RvFx\Y Ҕ8,'²-8e޼B&;UעǛ"'ݑQh3t ^ Ǹ/ZoSUL{s.(h9Ylvob!Iğ�X/RnSPd #o5j?vӨO!؁SuHlJ7ݸO yneT> _{LGj ڝݫ|)Amcan]5 uUR A-2N()G-7p5> (3"mTLuT`7UPDwwz X&Mҿ-96]QQkYQxgUDG"!mȵ'#NB9aih7/uJxB�uWH~I*%MM.Vd/0Ɲ p Fh~೹qG`̉ڔ߯ƻFo b+C-KBJ3}F 26`aZsH"\-c%ʶYL*rIg]C.!<pb/b" °("odi}C W0xE!TtR!Wa(}ɬEdgGӽO50 ^7`$8$DR'oʭmfIwjol҃RT�3ŠjEkOTc_" UPɪrO߆( aLb-k494d/ y=" 8Kq#h#N'*_SK"HBԢ='?4l,z)aRC-n-uEp9PiI?޳m -U,sp8fe-=x)yj}Y٘Ϫ쀥gd9ofDȐy86?C.Ph&9G9z59`ޗ 'nC9Avϻxӭv;#O[7KDGoGVaMC4"R;khoZ/Zi}[*0q j:֩-$e(Luy/Ej㸜-] e;/ZpE N:<,) Ȯ5-,2'ަy',F xXB0,<(x@cv#r|m#g򾐡YeYȽ1Q!Q@ҨٹG6jdaî6#1<.Y�cCIFocT np(I6F).. 1]h"`̕OdkaH{BOP{ʻ=;o>O_8je)/aC 9L۔[F2n!09 $5MG)�&2].ߗ%N ﳠe@$<V �iĪ.37F:י;S5i8I(7Ry=LR/0BJs1/87` ifH6,8D'/Pݠ3z!(bj'G|[ˍڵgq׃:^|p?YlgC}o)VybDr4X FI\6cl im~\cxB91 7=ښ_v$ ïl-wdkUkԐ>`̐='*6-e>QÆWֳ~|Kwϛ񭖕 <;T*t$q^lg|q༢Uw!2| &D +hWSL?Ņv༾F𦒺n*j|cMxeZ61&EkR \"4pUwh;}f<(6߾!*OK;fBְkHє=VRt& I٢$`&Qf̍/XAl 0S]xE sT>;/~hlϬ3(wR}i,A>^Hr Bo"e dy'8ÃM3_ 'gw[[+_"QvQ�$tB2_SSeV~ՎœVZ9X^Jl:Ut;B0-K6=hhtzD½j1Z%e.čHnUsp+�C'|doi`? D[旯;#4ZLz`~S>آ@9~-E.$7C@:8̽�@z|?qe d`oE>|Y@h|sn 5wtYca=Bf\l$(/O<l-ܕZq+8ѳ8Akk2KM~I8H gQ\q"F]&}2HhO觲S@/ݛ*Iϴr@ k᫠ik|\h'[ȱ]d~%5y|{ͱl?ZFPGPb |]'o_8& ^ V5x*!,GDa큈!}*:y]5zڮ\-4 .' S뻉YÌ9cCX<U4zpT=i62g) aV#ȮQՋܪ:XL՛TQ'n.+5|o%7D+SLqQU(LdSj۷0!]I[vב_AyJ0G�Eqwg :eSd~myuXy0Qe1.p)ACȕnȳFIa )ùi^FjMp@# =FI΃:Rtڞ9ǙQz|EEy1O;>p�S5 J-= 66R`9OWl= h*elr4,4$w{$ewDA5�ޱ*1A n`H#T7'J]ǔǜW1z\e{I5& [Yd[ ]ѯ*E9x.Dk͞,˾HԕWj!Ao</*332VѵTkVd`TU,yC?V뾰W_ [gW􍤄hҍhx�]l(H."Lgj xB^90�9Թlh"L |s6,eH(v,N[4~~gmZ29j۳퉄Aɀ9*~l]S;v | z-n&Nt]ӊ~pjZ gq MDZu![58jTR p5CF!%j->~WMH|[أ4cb2b#˖Nģ c7BVK_>DD LSB6�z;)cS "ZAµց a̯([$GE|P7KI8<X>\S\bh4ɢhk/O 8e)f-L4G/; k7,]Q "YI:),paD\?CHҙku4, ʸFUɠsB,t(춊*{:VLܷb$ 9b.>Zl?S~vort:^ȍ9^\:OtM.klnSmԓlH 9&MFA5 3J2J + /z׭5BalpZ S~&q'k/TR--K?"ś,ŕ"홉A#W6ٲ5"Agޘ\bs*stqAj9+hei`uucFc8P$UU CߠX/} BfƧ}7KfEEa;l&O/mO:oi:adz"Qb4M]d5rN%nj>"S 4S3ϡ:{eW ׻2Sr1J_))nx9*I]#ҫ{eY]$Z;=4_~-lB)Zb.W�+ӗZ`V-ml@o;\O$ɪ#9H6Ohwlf\{H@3+܌D'Vf S朮?;?]6BƢT汦Etf5}Gʺl`]:5VY2߆9 FF/b! u'\]ɫC ]V*uFb6sknqT9ILQ(<MjkC+]6׮i@*ZrjzG*xwV76NLʵ:g)Gsys&Dcֹ *cWG/B? w.֨|"z>3rI<)RUʲ,W T%7L1fnʜi2¢RbgúEs 4jn&TxC'zNuh]-eWbNvіD3 ƒIW}1Bgd % Ȏfj>(Y`@mEȃF 7=/g,oXHVGc3&'h*sEZdlɢCU}nV]JREؒj#$P(@uǁ6[}{>l i1mV&ViswN8#7y(k$gm,h(aIk%yz Mq<&Í.x.3�0v-7Ǹvզ!\ɤƘp~nsR<ROPmaX,a -8.Y{A"oc0j8}Phn7` S9S[_W.*nۘ[1b%wFrY0̈́)UFYc(QnOK9h**ܣydjd}ea3->!-&k� 5WSb"YݯSE_|jS6o8>-=z$QDoޑ+XF =QsUT͔;<\[* E-wkݣe%Áf9|T)K7TՑS?.T'ziGѿr3_ '1ݩ\!+zNX͊r k幣H鳴Fc^Fi}[iI*!y ?^ ϓg-nɴ`'fKj? HqG#Cpcڡʩk/ulO3Bg L @X;Zд(xJcDHNSP\LX4aJ.|9sN+Tp%MS؂FQl \%3LL#7rAbMPhfP15a>)._T8g\"$wg׋2cĈ( {R_,A.î>kmqhޡ=z:ZxY_:R_NƎe'CAn\icTVRd)g"{+%Y*ݫMnuTp|GG%*efQ<5Q7%J<j]jK-u~[]N51e6W *Na#&9x:hvvXAUY3pβ̣zTP  (3 2$(ӿD[lEOdĞ e58 耖FErސD[!6(E3<:aPg#2~Ǒ0e3P89qb'pEZ__f2˨gDv?Lz|} |?!ͯ5<%l%跗+ǒǗA=tK5beggwcZAH"ea<^W! ]�G ͟vb�~u7q&v{5&mG3n|KZ儴jw?uzX#mp#2FCX*Ah AlG$8sU%*XqѰ<fDF<(~Oi"r:x ~ *"ٻl:Rh=`o]S5]7 J>">OP" -:9@gzPhWL>븥ONį5\۟fDoBR0ww~0]x UB=iImߎD}yFFo,Gy؈0;<>clanSN#P^Zs7]w*|W=Ғf,3=p^KNCrɑ0!)8X3N7 X5O!9<3r}ZГ/K.{}'|οtE¿۶ (J;*$1 ,N3]cK$yk1e~3er}sXoׯFP5%Km@ҭ)ST_I4O:L I7},3F}0R"˽*}Cl7!8 -5_-0Swwс'-o]S2HB'+sTY'_}o6oNoSWP \}\l˫2Y'ogĠˍ\x~Sw}6pAA)MiqFW&6lI{'rK33 5WL?r-s:jŖ=9tr3~a0'O7.q6#PGkM#$McM 72(vGNBI'=6 ԥ(c7vs="U~I+_TYb뽯WGr`MM8E}4Ա{q)ߴ"gx"yWMe3B:'gCbDYVzrh^p(3h&@ή+Dm0 mE HyO-Cmx:\5Fk_ fSa7 BǂX<lx jq9E ^d A:i"r=ZǨhOryCR5}O_JmyyD¯N&:*T vT*g\m\ ȓPClBa6&k/;NsLp1R~L@_d_\^ի0VxF.~~ײg,54R`Hzw\ wIB 86xCqr[peF0c\p'x,OM-+1W� 6H5{Ċ2.1cginNbkhCA7\*mQ[Ю_xGNUvV,շ uA^:@n41FysHH*9]v£k3/b=W!?nyͻoަ^әtE-ȅEg8>56BJl0O`WK9[; ^o;<&WZ\0MQ٥Acp DWA/GsHDu3g'/y/~Tdg_հaҐW0'TLwx0>CO0V<x.2 MrS//#fvW!Čs!bʴspEgXR})0@| -4=I.j:(}I<9}{",t<?'<P0w$"ce-)|@BFRN69뾤cU:Hw|CrP>Q{ԗ\HZ|m#:Q'CWzY6~M2Sݻk=cq *#o7foHnaʠ{ dפmh*u),iw:.;b?4FQ3G>Gz"i˟W ksB3֙ rpl*-+?x1A|iF+a.١@_;68_Mvqɛ{:a;ؿAk6n_J{Aŗs+LwQT96Xhe< oCʛ+xL0%͠I"Z2f/:E(@}pDc%,_2MUXnIZh)RMjؗ_] kUy#Н=yJ/+"k!9i.{Jo,]jϡJ[߲:~O_&u^b>1%^dJ• J4Q]^Z.ÆBeSFײRGa-(#I!Dd[PVP ,B2y)499O&}EH{E*WI8#S$7yjQ-> Lx߈n`%zL-8\{ A5-FYq8҄awm+DSPL8&;tj؇ o>RoLQ_^H"CI,-w`qp,nm 4; :w tkc iOs}R-r0GkXJ58N@Y<lG5m x�hQ=lTX;Uf35l  +^APjHq?4G"t6o Lid3Y1gn9zMmM^؏N` A각7okDoa~aQ(VB m穄 :\Ӛ;FOc==;(S, /3R#զ̄:}h=3ۺdPC"?P>ˌg($3$]Vt J%-o$M ӆj (Myqz~R#7&gL6 um * U0g(NE*SQj'LRj}w9+Aol-7+ޏK!g]>i쬩w?8<cIr z~`70z F6ay-̲L֌-fl OޫS.-6Be}lar^OO}UO L}?`de~6$8QþIq8xKsCp#Q%CU~rXc;#䪨$5eDiҧ;R8<@f`{CbQʗqOg\u'_`)n$^3i̯P_'w< 7?WGFDМ]WԔo:LjA $L3rƾqt[\ d@GPFRодa|sBC}m+:]too:BY6-Pˀγ/-�`#d/E喡JZ>1j]Re `9&U{D Sιq7T]k e^ KgFABRJ$#E|ټb$oM +V톖?y1!=`7(=`4y6V&,A:ڥIazE=Uw ՏpX+鶲O/sԒ N_E"?L2%,|k &7Gej=rnr˪)"n&�k6n;RRo ^Z埲DV_bJEX8zQ3S;C_TO4R!!eo|b´6$͸N?m/z8މj5jm0#zG0՘\wA"GirFb^plR49l蒒/}8(K2g@ʳۢb0HݝdV KuuȢW+Z뇕e~a|j}g? jgqW>S4Y_~% ,_R5[ϑGY`KexD5~ZLbSS)@7Z˩asJȑ9Z_ t_@qI56!(_�V &64 K9D7{d4RĨsv\~6gHhY�T(ȅF�S6Րσ|#g+Cit= cAb:+?ER0l_5l8ޯQX; %7c^Fz9)ڙ8KT~8po_PBs!u*}\U$Ng'Y0{dă"ktaRM-#=seTVwF ts;HiD&cYjBWcç6r)(m!#FiiQB܆Q*6 Yiԋs�)IoI%v*$3ɿ-$J'Nkq76ƛ NL%+IߎjRejyl?WX~/Y`y)Ry\&Um}Oס٘NIX.d` |w#F㊒KSlVD5ϗ`ΰx[svQN◤ 1 κ@ ESkLnd≾U .x`鷩T O Аsq0te[Jx ڤ%jn"06"a0VSVf0䅭deC6([@](!0-TQnSbW3s1a{K+.>*5[4fӢy}a9YifC8h-Ri 6esc|5F(,:8}ڞH\!!uޢ1t7\SnNs κ &_-gX|qf#ԶUsN 9 aSPEm2Ί dfu), P)΋+o5{Q}Yp{F? z\pK(&%;PFǘ0czb2f7‘8̀BJBIYHu'0&XCq y};()| _ȅ$'Y: 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 18263 /Length3 0 /Length 19076 /Filter /FlateDecode >> stream xڬcteߗ.vNlWl۶qbvRm;bۨ8o}~~?1sgk8$J f& ,<�௜B`/fj47XY,p�QG/g+Z]E?%�L]w @/FUss9hkUTҖVPK*$͝mJn&@SŜ`  Oj.]��GsSm枦v@� o \�@{S[7+pW@-)9:]*I[VƮvU,Z9ҿtaj].�WsO|̀.^}st+ 7F@p64v65wq g-{cGG[v�tu1`caoK=?"mo�`a7͝U zof^�3s 8&׿.w,3ϑ?@#_9yn vf 13�9?h_5-߲[߄@  `alfۛ;rXN hjc 27_3IK7_JUol+yX#" a`0r=f߸]�ݿy3+ϕ7u0uT]vQ9;%_oWߛ{­-;Zgdgc s,kT+) u Ȉ2z cllZ:s8=òM3*$#/Bݤ; f2(C<׌^ۆ`8ܝRV1E8 s&̽(4! g9eɟ'ё[|X ^cl3W/#FOWwN꥜VZduoe7btK6"/a}feYpx֒$z Y5eU <6٫@e OT j!lfR\(1)b~W tԨe +1X<ic_U* ၥm6aTN[WYhFڧ`{ō@ڴk"払ΧO5hgF\4=h 8-s R. +P-UiѤn^7Do ?C$-7c/0t k]P0m13i!~'8!%LlS'Gע0Z,du7@}zZ L#�,c1q={) lh#ԭ'[{Tϱ1qc*g=-Ѫf̴P3 ]mZ[Y3SfI.Gy\B3W֗FҌJk8@T.W.(0̳E3HMD "|/|Y\C5S1 S3+PVwPP"vp͞m7V.aRߚVBkwP^t\A?T.LP(n"5\gQFi!/@F~Ԍ|C+` vazKz dSlePŸ\ 3jH p%lOcx_R2G.FZ,ҭ2It;ȕj?xT[o9OW%װprlt|&zHg7Nao5eGYưdcM_3כs?poXA=nڋo}jt4Pi^R2 .TFD`o#(H ;/!2e]flrŽSZZPtimY "c_w**W.>GAMa/Sۧ D _s^@&zycS1򝲹=~ÐUXUnw_>=b~-2J{>;~>]7gL (P{~遥ұ]@BLwC"ccR\՛2yզL w <ac0+l4/@q3\Ķ |{C3ԎFPoA%<*|2!fU7; kz44{Y.9=4fLG;LHƕɏ[kdUvMXhd @;2=beAtAsGRJ)c¬o~B |8|s](M4|\rsΰU[5C*٘Y]\`t'um^ʣW=\)%d7I᮱!RߣcIN1ܒq N?CҔ#f(!gI0Koܖ6\C33뷲cN0($;8}]9Zՠh/ ,%2+𪋔Xq]f7Zr,=<_fs>mZ͍z-06gM| ;Ri'v`##~)xkvBGX1Mԃk 0>xKnǥ#8sF$P6hYO9cqL<vS~1<(qD4ȒS6=[P*Q#1i0?aHq7NхnX%TXd+{azqG -Njo߂7~FLDF(6f[dAT]sYK�HĬwEK#~\lk Ĵa33!oMm-sE]x?]ݠH#lݺ)2Sַ:T5@}PaA\% |UV�*mxG`Qv?TqTw9{A^^\RKI<[7cxXjơGm| hr꡷m9 Q4`J=b3/і!E͕76T>FscgIpF-JB=tw~<c<aKzt9Lï_d򊕁$�I)$.cZ`FVɉln{aM䗍R#܈ Sh=Uh}n o۞<ˎA!FYsf_)uLaH9{lC͍arJ>$w| ks | NW[xV/�٢/7z,vOc;}Et<=ělaץJ8e-beHr[qYoqfxUS?j#I͏s*$p:GbvgWkTEiQf:!(�3 ^Aeu($wu:Y+=YN?yߎ_S5e/x\Zv,! ̯< .'Ѩ2kV0Qyng&'F5M\m[o5H]0C($ 7@!8?\܅P;f1F&K5Ԡ&%ıԍ.PDQc;STo(ۡκXZ8x%Z֪3gH n;Yl_g<PL@D qCT uH9`#f#΋!efg [{舶Zɱy{@˗a̝$ pn[zQ*.S9\a-vW�vFn I$'q i([:* ta]nLQ}[SH+MHBp`4oWF*׊*|nadY#;l_1sfljn c3{xz 3i=/r Xb׶T1{K̊̄TQH"@I8D~běy$[^9^ _KXJRiIIЏZ߯7|-w :U*fyZJ"``5:Cuǽőz J[,p‹z-a{|wFsl%{VCUws@PbByE妬. I/Rq5>ɷŅ ATR榣\2%ϜA$U(*|3l->ޞ>BۙV+*譑jʄPK 9kA %sp Huge |3kF<e|ɜV#5mtI>}UD,"|쯒0~ɾ.BkL|iscWëvk+߽ؗN E ڌoݰrG%-aͧJc tkȶ1j%8W~JNs@O&jzw-yS=D`=aTcvZ2'm 7LTB J~`V h̦%GuŵyGL %ݛ(T.o"$VOO!HMO8,DMݖC l4L?3Ȅ~`:*`;yѮŦK\N@9`v(-__1G kv;/xQ m@-I]ƒ*!1=O/锵RAy.iPCꋞWEWTsO&EUB,�23%1QxjD;P~;£ 0*~ҁ;f\ΰzPLPT7_(=k̕e?fh>&g2~NNJ4Qبb go ai{?aq W"WDx<G|! 򸥤̨nhI&`ƐY1W֛š& L5'T-P5L&F}p'b2Цߓ}lj{SзB$s_#i6YP}2,Hɮ $fiW$BPLw^B Ub`˿(5_ia3/=lX`�Kx7 ?k,./qqiWQECC&Ѓ]Ppag߲ωC,B*7OBj:N&3g$Z-MwO+qVO ~Ee6~#B-ayVGA1{`=k�'T), yk?@̊M9źHzdQOҵ>J}u ?c$oUےbq3ͦpZvXxdR\U1406my!* R JGt)<\׈& +uW56em,}l_*nʔ\FyB+YmnrI Em-<U`>~ޮW]aܵ32X]@Vlhv}?~j#ȈpxW�%@@5' GxG2#ލjo!`lӒ=3OO><d@;Reyم&8`?jtF\t0ncr$ӎdŅ6\McVT~l{}Mk^\d'�X`! bWjWLxC0 .*+#S RutSJ(N;\uͧ/=o 2^Ӑj3YKVD7SK ,>t\c~A\C."*VzOy+XEPK`F: QA mH$+\b#(+ú]Vnñ"T0m-V@ml6%W^}K/oiĒD:/nCG+d#W&Pͷ=aņdrx O$%:(-\ָƀ%w=U]3AQ">EyAz#:O:</�x0GZ:-œt'�lY^9׼RDaHl)"0&3?J}[GmEyz˳W<(|++I~jey[H\l=)%\DxQOk9ܜ<2TLLixmprPf3^\^'$a50~f?`(~Ua0USA[W -J[,@ܮ}žləBc5t'ܻg5Rq΄`& ֩LdPp)WP%f#,gZu@e[3U*qnJZ>Æ0'Bt6t[>(C,%&*ֺ$m锔0h[{(a6bpإBM_}/m,8K04 %3s(`,o궴$c\_Sο4 96Oo��O|G{-gKw@$}-N|C­Whl|?ؖ!X [њ=K#fflӓD40D_pҗb|WRH\"g T퉕F?5cZHq髊F^DSۺ� nk]11fs&l(-؅GzuLF6ZӥJ+FfQ˭w6qkaG* -@Ĺ' +AyiR9Ǐӹc3=LGmh\)]~mjg 1VE'';nL^2䡾v3Jqn/f+B e_iϠ  fW+U P7>no&xǕ2^;^AQa؏Em?pe_~3H"F MM)}s3ٲpO4GN0؜ߣWB_+M nA`r"[A>Ҩ(Bү;,KEmP,(U+my}x<7N'Sh@.jɀ~0$Qx/t"K+|Dgg s�]FKx\QQLhf_APۭ}^طݓFYY[k7BVBD('"^fDyE{ڦMVM2 zsF|6ޒ=cK8+i4[&|#m!f(QT7f*r&p7`:|y}V$ۓI2D`;c .CޡXh)m_l( eP0vE*}(Ȉ@s4Ɯ]QgѶјu3rB\4w7JȠ2# ˜֪Γk[QA )}dV24+_[~Se@գ3#5NؕtѦg4UHeRM{+y/0F࿾s2kT6p5,1`swqvz,F4w-E9S]"n^ AlrxA;;: zlQPav, _BjdigicTtl!z|M>�Δ F?b MNJ@ϸ:В9/=A[@sVk}9kU[ JGg6mQc+ܠϲ +Ov I獿 Q\(G2]~mul%t \lOV Vb? b׼ 3tщkłhAK"Ȇ>$+)}tZІ:�JWLf 瑶\_ TZ2[TɔMs?( A5PC2zC'4!\'upu4Ak^F}+; O@_&-/ﲭ$0z[:=KFS,O [\38 gUdRPS8x76 $cғrs%ш 5wy%]ϾM`qtz_x l?QuDŠg4S6|`ʝ쐃Z:bh# \>PiDr;}#XOS$|e\ 8A]Uhnʮ-v⤂sk]jF֙'[/g[^|qL|ǒϻ -W8(e~I!=(ލ5' IG=.be*dƨrW=Fvdv$WY X=Λfw.eжT�—5MBZ L^!Lr Q {Oϓ$d5!ST+Ų_8w^;J;.V{z^_ 6)Ho([<C)+pN̚**;=l8Q| @N4FOCi {o4?U;|1Ÿ) yU$ȥT!7O2݉&4fboͨ0~UT<'k\[?"?fY}x\Vμ5a~|(`MmqN{ў㛨;jLQʿ@s_)];5C\½7-q  m|,H-_Uz)T)�MXDvtz?SwH0WfDևIi$G-;EZ^﫮Hn4 1丨-pS eJeD'ɝc~)%%~HL֚ώXm n+mȻL#<=yb# XD-)E|(ijDl2!B_/{N]CUf 9 woƶd$+ C]1}ݧ@o4q3\["@No|ҬDFg,$}#sڵ0Y$Y U c[Uʛr4;NlqQ\iR J [ynlLṲm(8m¶BuD 2]>]x`Kv`(`?'v0NehH49TƙJпNOSCMpAl 0>;qt<~Uk�5kqj@.BH_^=^>㷔ك!;j\`X%DH`"w#DF(Lah&8%ol2e$"VX3(pSy䖢2UJSc͇Jb�^`B亹$;|X֫59JHq~MAA:>0 S'%]ATǎl*v)3וLM5("B'"FSîc67|gKu¸کDm1"G_v?y00_4`E_mk+(TȧK\Eqxm*l*)q@;ҦmeWx!ٽ kH},Vz{dFȽ re7r" >&&j!}Ny'! FiXZ88m:'7Ev쪙1s Xgv#Oo{;dw(i8H&%9`CS1\^@#bݯѸ57Qdd:K[$"E:-j4:Ūc[>To|'ۧ/(2MFgI]q$Eg~^ u*)tjIrO8kX͡oě- -bR!fLSqo831p&fî)>8_' ^JRp`;xe�m^@z!J3 !I<=059EN[r[4~׬A9{a:C XrIwBט]%h=FV�rWqlE:oSr�GC3HhQUYIJ0gV"loݲwL n$C=8K0 &ݎD7.t"x#vlLu둭9B} ~S(ۖ vbK2*q *>k�0πvK X9 {uYahQ'8ߵ._G2s?Q] HAbdOSfaC,Uٛ,gaxZRd&>?<>KB"r!;:K89T)/!U#]U\" ',d!`0 _&d 5v*g s<24K+v-(STy!qF5V<qIOv1iA<Mi|MQtpL<w?V�ز6k#i:$PJ`a:ؖꊾltS1y^٨wƢh/ҤʎvD݅Z>tj@/H,њ'',1'pQ:(IN%g?m 2^zeDB|zL˖ӱv9JΎ)ԃKN Qz"ָs5́Ym[dZڊՀڌ3 !qPτ sru%)d3ڬY潒cs5:i]Sgњ PZ΋'Par$7Z)`͵ кsm땄b/6E DM7z+,@] Ity3bFq> y*ܞ7^F34rh/&v{ϟ +lO>t 2r%땒uILXt(a3j=淥A|YwDa+"y9D- xUnx%DNknEx(5(]v{ԃ$L<yѼv NW-D1} )"Ւ8GV^nyYHцG8+TSz juJ}W%6ǝwje E non?OG]ӘݍDVhgI"fރA0 A˨?9\3e7.L2G]/ip4W~wK\ &mN>}^($BkwU}.%">j)[IGG14I?RHvCY5LK=zpƎ1g=p%*~ʼq'́s1Á'LY}=1E)!{_E)UԈ4lEt `}�:j&Tiq\2W `c"=uuBO_u-BU:ԯM?3ooи bdbFF@7}PcT Qc}j@qޒ?lD YR 6|t5}*uE =b4X3&#kQ9 [Ko5p-IƯ/9>#Y%^0j`u�Ibo ,b6D: eaxJGt>'u.bI'~6Ɉkr,ܔ(em~$% 6B ރu^ѶKC>Em�)<L)p͒2e "$�ݳϒ/'mkN?SIރXx1jINf}͂W/D5usP(E7{tPJ5 B[jAme"^2ApjbշA,c9rB%pW%4p)M`PT# 3(  ^jQ2 tbLf=dEKW^kt/+ k-WbH(ޡ EonAPt@Z$ iT-R}9oܢܸ>=Sןd!sKb~U<5w $sk/ lI:$!taC4~ScA5(7B܂\Rc:`_6uTȅbe s($Ѻ?u%`т4JsC Cj|= .’ar@|'iET Zn%͗9iqLĖ I1Jx {c8YJ?̊MGŵR8CvBc*TDJ5PjwG/2VQ4'bGgʼn; iWS2.g6ĆM$'aYr.�DkwFU(?T6u踭!: K[lD-O+)˖XkjD0\\29 ٖrN ΄M9}!| $beN#mTi>QOJBF˜"�5-xGCIen#@:G9Z՛ -<Fmn VZvL?@) F T˥V O>/\aߛm9t"dM-%eOY)(%{jG!]'w"otV==\`7'nIH�U^W3 x W* '?ߊxs9З\{}/gLFǧȧw0 wWkt 4L1Bc6 &a(&~{sUeg,r:<8ةܻX'<S%~)M+z C?ShF(H\1-lg)!Fb% $ )/ZeβrINAY %p$je$:9`tMiؕ}_ ߲\@c4Ƽk/f$/9)͒ˢ<nؤ r}lhxӷ(cT{Zn�"s)~-bUfN)|j\R2٭b/g=;P)].5~Աiz$̘yքƄ"ѫq-Q)6:^زe?0kۖ^6nνcAsO".tcR7~ks)E+DmxEv*9 56ɺgyXRG14[jF kFaUvx21xYM^9.qFAL?nr;-rgvѴ/G{ߍ켡J^ZUYl"(QZ'@& E`ɰ34?5<¯>F/^s:45a*Y D:&Ϋt fs BT%J#}L'M7Žd\QtKgwޟDGJ9|n/>of2IM[rAлsA,A<QlF1\cm-c{D Bd =)CW?0t[bD?7_yqff09a7^hFt7,B3i{  HL\įf1W-b>3kV5i:Lk9loՈÛr".F+HŚ?)*cvB8x@%)?ߏхJ{̕[GwH~P^Kib,h0i5˯ E8znaE3y^:19.W?*bT"'~L .846} 5/D;pLbUZG}(=F-fΎ}VDuȫ gBIP,O 2x| 5ݜZ\9+` 9Ǽ:P~!:'+UEB& mP7~bdBH|$cyȲSR-1|+u^Gv'ۚ?No) oܫ:rD^cotQMl6bck&7R5Niߔ~N)cF8(.AŸdq|Yw(߬Z= Zf["﹪T/MYo%3TJBW\D|o_0&$N<ELDt/9RWЭ۵ Ũ]^wB;(65&EO "Y#.~D<a06׷1e`x +[;1/; !kҼtVFV07Jr8/=@z=;}'EċCT`qWs>1, 5Uqe6$ >KjN5RK@6";l;9] !%ˠMpt ;_ Ic[m~}�8bj1fƕ S7`R"u?4h;!{8+>W.M#-1> !Ql*S[I{ ,+�S<nH)KOIU}ڹpn#c.O.ѹV{#& h}=.C\2y9nˋ=׹ygf %0ցd\i`iwG, 0ﮏIsL}t=ݱ^F ڜ0ѥ6uc$ƕY̷E2z>?G)7OYLBs\TGy�fWb=:Qe٬2{NG!  {h?RY7_@p.k+9l6nD 3JlAޢ�$+E6w] gxi&Aڋo_ 7T@g>!7&5|NdˢP?--/зsH00k~kch kbfTNNzP.^g5ぞŽJo?)c,ڈ7w(Ƀzs x;bGo⋱❥dq{zDoi7ɍ?f?1\ 1i$ZYąC8_ϯ اPX!/(kC@\nx) %CŬ U|c&' حvű* FœM[BH8 6#ǹ=`u(gf jZeRÝ u7,2X!'J{[]=^ ~\6{@RX{QY5GcHN<쒉Fbn ΀:}EW ( Ǔ4ݴn $x Q/9vǕ}jҤP۟Q6)Nvij8vdɌ1?:)< c_4MyN #F? i`{7~x0^VEb,/ !ey5_F+fb#m|ϔ=.ٲRQa7"}Cfҍ&$<.4X}?u-XrȷM6,?zluĨ0Ժ-&#xJrtM^-p3%%ˆ\:DE9tqym  Y:pu ZzQu)̂ 6Wk_MmHjۚ/7bd`wW{x||P`*j5ſ6]2:[Ћ_<J~h<V U٦XMJ1C'Ҥ-7Sn-\ꇹ<6F}׆|=2?;{GA y&\d@+\.JRGb$HY#y -2wޞ/-]r7q<EMhRO->SԀU5|pϴ�+t5Pٯq=+ 3!R~kT):1LdeP{&�%/T3T ]c&l<k"rDT{}4w^X0$$+8vA> +t{@(; J3lz(OgWnoi:1*4sa<C>nqrVjXsXSTeKT$-_@KC-ua8Ä6}nAOnGRM,`x {ߨʦ|5Z p�j FəP lo:cmaZuk0%w(m2T gk9(M8 >hzv](MWbf5đVV|ƑZn]FWQ pYz̊mx{"%T惙ۖ‘~Es7<ֹ*�䐣a�=h@%A)yIuSyȯB>87qYNG;p\&WB_ߍ#;En#]iqA8+|'=gǗ|`ž`Oe)IXDAʧpQʣ PAktHi8T<͖U lQت*` ma2};N:�DX?=ڬj<&+E;tXCK=Lت˔Q̝IW2\hI{`q]F^K1&"Ai=,Oi} pxK�~+co"f598bEXُJO]vgv<grq)]䟦y k$8&w|[ײX$¨Ch3$qSizwm_飾ϸab2N%BJ \7k:jF# t f3 t2"du%)ƻpEoT[C(oRՏ" <1Z_9K�s0f>"Kϙ@)sAHWw!18 ~1tDJ?}0:jG˴&u0T ּ~Ƙ&[jxnpK w;U[k J[>5cV}9lX&"=RG$1ud^%?v#]@rX_)qdoH9ְ}v'H܊7 TFl/V~uo?~]j;wH?PluJaŐ Q:WcLk PA "(]*͊3 +L08d'Xp!C6ΎhznBnk߻4vֽpQY2g [4-?ץ@k,S:.IS3,zR^tlM1&VEN!v"Csz,tͷr5T7FSng.Hd=Xke6g|472^ c%jXMq߰/M<Ly6x_c3֜2YɖۅD)YBvc�KBu|3hݝ5J^DRpMðg,m3Ip#@7P7_0EAa>[HP9�q߼7EG8ZЄb[Z?H -1@yn"$wMEP*phtxq>wAS2_yYiĊ{RnTTd{|G4&-lvޝz<3P4,Pn&t'D`nۖ/z[%m+r_~JJ# ğF#mHF X(s�`E 3!NCD סOrGnkHCb L\S6[Ю9hvUH$2Ōu.9["*@QgQ谂lCE}5lW Ϝw&z% `:M 9I^4W߀s vz=qnJ"P>4W3ni֨՜M6 mbjmilE:q."v5KY栩jׄ%x̮:2Kt _(paJ뻮!]Ă8TLG~bD iz$GfH(fa)dC{TCU '&%_#t?ر!<Yc0By{kq@ؖ[!GQEiK5st?$ 6;S@tu(#q<b+D7#@wG:??i2uIC2S K&4zTTeޱ,yD]6np &#nf@kHUg#??.w$ASYe*MAf3̗hT18a_f`VT&@dS0S-ERCsc_[Yzĕpm#EP|˫]87"R!:߉_(2>۔Ewt0]Ak! Hzm8+y{Û7%r5FO-U%`e PTcsXQ endstream endobj 256 0 obj << /Type /ObjStm /N 100 /First 913 /Length 3472 /Filter /FlateDecode >> stream x[[s6~ׯc;~t&mM4iɃ,36Jrw@Rv[۔&H߹62ɴLلa*-&옎3#ǬQ)2֌ WLyI Gb�Kϵ9Q^SDH .`A0hfC`Q11::f,3"uW)YX⭨(q ֓L"dK&Is�8E<ĜN$sL9I�8s>28qQ2.\trDy浦F`EJ{FFJ惓b>  H?dvcJ N 'Bsǐn"J0hרA(͢1$c 9 aKp,pag1 ,DGTb 4"`to)2(͒'.a)@PiJ3г[ )[HeCTF0eQ 2 CP+fA\)IWZQozWѲ`JV"{7D%`0P0SKL{B<T`x R`Yr<1Б2SaԣFo z$Ntq$-8ߩ|1Gdށ2І;83K68{<`☉oL/>r:_?}_/Ǔ/_?gﴣaO'!nj6{/$v;^=�ӹ_g9|bά@pem{^i*m U.@K\.& d&^5֬UY31d3_`I^'WIʦo=oNGO,)JExLP$r�ݳ!08]DQ=uGq۳)g[ή}9z^(B PB/z+2Ӟ XB/zЋ^,b XB/zK^*R TB/zG=rlٖ+g_Ρc9zS*T =UBOzS.tWL&?Ŀ8?¨WSVs-Zq xcظsߏ]�(  zЫ J**.]tځ E8o�EEiv@Hi3)h9%W� e@8l^ނ!P K!: fTd1b0GN(IGLہ1HX`" -ڽIZQ L(N*m A5 MHRidca(E듊|N va=<U Xq,&nQܥYFrX l0!R؁t,ZxBfpc0.0Q[DP0=h5&w:BX'a0Ќ!b-!K|`H؃v'm="VTo<@ Z#{p 9@+PJl@ LHU\a�º\uCEA2jiSK &|!P ^A51D*Lx&g=SL(G쀵'T+Cj¤A(ma7-EGZsf UhJ (+ fjeK[̞ ԬlP]CH@&B"q `\\Tsc0 *. q}dwbPn37e21nT`  BV| B iP01h#Hw`1q j^ SHkј`"}FL6}H&u=$ev_ 0aE*aѡoQ娀|$@a,F髠 BhBPna7iir8a=-@̠#6/=&aKպET`Zw5X >S@K:GrgqXW 1%M;HDo؇!b.^)o/1t ӦR`~p*pg*p*_0LNk%0 :W 5}N}*=e` }ʪIvETהYZ?mYPE-xɒ*QA"d0r0MC"DUT4Nnjql^\ %xQ` r%U ~I52V3XkҮ6mE)vJ`MR2q[dXg 01wcw?qW 5Da_69D$Oȡh]\3sCloPjעM],+nDž1]o_9)fO Xykm:dN,zFyi`/?/Cm[ݎAYB~k3ѯuT:Dt?a\uK>_ȚkMG-ss uN._T\#Q}ިMGʻѯ4 InA/u!PuNd_y~,}:iT})vK;wؗI*w$+5֐%1ָUkTױF;c Wct4-ɛڑ~}z=hGI\ļ_IX]dy+EM8!oJgc7^2oZ)o/L[g:Ua]b+N9_dA>sZ7eJQW-' oOD;h6sDC1ض"suA׵_VN6>Ѿ6glF^T;K>)[}E{I5%j^6!}17O=p>YWx=}jl|bl%b)^Mh˧<_Hz<Nf ]�:?X/CH<8<[x&_8A?'1d:LN4r(kq"NƓxu.NI3k>֒@fF}0l1ߋ8lh~aK#ٲc<q.<ob*->5s118i\,7.%V"[8Y|X7'0kXhZiׅ_'],_*<BTġdlOzϏ*yIdN)Z)Ѭo>kSnǞ<왇Q}Z7ʓĽzÃ^L/NVfUl~}J9̗Z-5N>GL?ԓ߿y[:Z\Xg{K-|Hn9nA|$|ɴ/8<Nu/""]_䛹1 )\gjw !үZᶻpCSFa46{ֽc=)AQ1f5nGCb]gO"6}@\\-o]O!luyQŴj~"U7-цj{ 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.15)/Keywords() /CreationDate (D:20151105210131-06'00') /ModDate (D:20151105210131-06'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) kpathsea version 6.2.0) >> endobj 351 0 obj << /Type /ObjStm /N 57 /First 489 /Length 2655 /Filter /FlateDecode >> stream xڽZs8B1)= mReq5Kbہ_(lVg]$" <aL aR2x%iΈ8DG%0,NL<c¹=Fd0 W:!Zs. %"/7ҊH@Whx>�Gh\ex&0d 40 IAM0CDtnD$  Q20 )<!5D/(D/e%pJpA)$ K�VkD1bB2 =ܘN"D'H $1xI,XX#@hlQ 2DDs^81Ġ1y}dq-E:W/_deuY/lNW4t tZٌ{x P(H=6c3sM؈طϙ:e|:d?ѫoQ#g;Hlܧ啭#zBSzFkzEiJ;zO3hN [fń5]҇IH\~xyjԌXȈN6f}K 2Q"O3zhIRзOG͸S:P .?bY)vuqo9g LK]ݼΊGoe?*{�ZZ)~e}q1)Ycl+w@gԩ}9,j; Ti>i-UAf սD zG#}jksYy`Si贴)�1$.]_З.:Dѕ Ɖ$}YBoG$9[ePa3ߠJ0WBVRrCa-Ms:)f30/H+I.fֱOT ٪ 4G75X{z[۶v;Wҕ,gx9'Io(BDTllb0I*pp jvMBxV{}s|ίޱh;ʤ'YYXZt6fxI[BҼ d5 l`VuE;c`'x.v `=<pg>:9?y9 3!87Xg`ަ|G>3a&D( S o_?>#yU:r =hBn@4݃Y"GVp)x^A^ t s ͓Dn-.?؈ML699k-NްKy…{6ѲkO0ղ~Z"- Y5u#3U]|QmIvCW]C5a:0vDg'aݛ;Cxt;\#};|]v"Nq6v:j.jȾv|�Ϭ٧ ܱdc6qG16np5XU"ݒxtyT~#rdlOX۴<*{p^<yIDY6�GOC./~%0^30|,NHFŢΠ@C +.Bιac( =w6tpsst{;x �ĘHc�꣠df<$G^!z4N'KX0u kA9U}j2kj,JpMivMBAZ1"3nY6C8ΖZeM¢#8} Q@n9S|~}~3kL]N ?Py]uWXDoW \Ce3bH"[̑9`z]pt*l7/t3'r>O˯l^n֛Lw})ʯ?[0vzEm_p}8`6Qpt o_HE#==bY!pkM 0_K4"<MR$̯}b`vC7MpIA4r適AIE-Gk)VHRRJUUXKSRR@Ƃ߃ѠTRNāC$vzoS{;l̊-ʐ-*JYՃ̪0IRr+pD82Bf92Bf92C]ƄdVM2ZAk)2(d6 ٸ$85a@2k¬&L̚q�ad$ަvME%%,Qq 1J{#h6w�OTbp3cB 7^=Rnc7b[!"YBfedV#IRb+pD8`FfUO*6 0�d{(pvf"wZy{g%(ڛhOJۏi}jn]{)ۧj-@;i}hkhhhOP4ۏs^8#~S_'gB;q~. ̳b:j>lz\ #V_XϠ6?t7ke 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 [<970B7DA2B0DF4520E17ED75E1979B8F4> <970B7DA2B0DF4520E17ED75E1979B8F4>] /Length 871 /Filter /FlateDecode >> stream xRG#""83NmJ*Ue>٤}V:HTU|SJ)uJZNR&6bT%s6 mFoN;r m7V;bn ;anmn=.;s].[_0e~K=湫n>G>!0`2/(巠1^vOm2q +6OONs0s(L,a q1˘\5\wFs {l-S܅7^@3sOO bc Z^_^=22K` X[êU\Fku~DaNCCCw#! | qE-S:FQ CBBBb'k]h]h]h]923_(\(\ơ4a1Ke\EKBKBKBhIh]B#B#BUb\@BBBq.VSU U U Ey(H(H(H(H(HF,tkE.ZC|=ߣ~:|r-;3}~H?6<yjz-_eES҇d[яdwAV;n^ Fp8 'a9cpGK3Έ|.g08)d2˘\5\ b7q qw} n%eL!Ʒc=CY{t֌`>_9Z=9Zup-G~WM^ ɭɭIIIZyK/Z+)&&&&&&&?RhRhRhRhRhjm?N׭ endstream endobj startxref 118312 %%EOF ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/DESCRIPTION�����������������������������������������������������������������������������������0000644�0001751�0000144�00000001265�12617030554�013046� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Package: RUnit Version: 0.4.31 Date: 2015-11-05 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: 2015-11-06 03:01:34 UTC; m044910 Repository: CRAN Date/Publication: 2015-11-06 05:49:48 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/ChangeLog�������������������������������������������������������������������������������������0000644�0001751�0000144�00000120705�12614544635�013122� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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�0001751�0000144�00000000000�12614544635�012116� 5����������������������������������������������������������������������������������������������������ustar �hornik��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RUnit/man/RUnit-intro.Rd����������������������������������������������������������������������������0000644�0001751�0000144�00000003720�12614544635�014601� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: RUnit-intro.Rd,v 1.6 2009/11/05 19:32:48 burgerm Exp $ \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�0001751�0000144�00000016150�12614544635�014464� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: checkFuncs.Rd,v 1.17 2009/11/25 15:25:40 burgerm Exp $ \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�0001751�0000144�00000022426�12614544635�013554� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: runit.Rd,v 1.23 2009/11/25 15:23:12 burgerm Exp $ \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) runTestFile(absFileName, useOwnErrorHandler = TRUE, testFuncRegexp = "^test.+", rngKind = "Marsaglia-Multicarry", rngNormalKind = "Kinderman-Ramage", verbose = getOption("RUnit")$verbose) } \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.} } \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.} \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{Knig}{Koenig}, Klaus \enc{Jnemann}{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�0001751�0000144�00000003067�12614544635�015266� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: RUnit-internal.Rd,v 1.5 2009/04/16 09:13:31 burgerm Exp $ \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�0001751�0000144�00000003045�12614544635�015143� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: testCaseSetUp.Rd,v 1.2 2009/11/05 20:12:04 burgerm Exp $ \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�0001751�0000144�00000004506�12614544635�015144� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: RUnit-options.Rd,v 1.2 2009/11/25 15:23:35 burgerm Exp $ \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�0001751�0000144�00000007432�12614544635�014046� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: tracker.Rd,v 1.15 2009/11/04 17:06:56 burgerm Exp $ \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�0001751�0000144�00000015512�12614544635�015117� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: textProtocol.Rd,v 1.16 2009/11/05 19:46:28 burgerm Exp $ \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�0001751�0000144�00000005376�12614544635�016220� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: printHTML.trackinfo.Rd,v 1.13 2009/11/05 19:46:28 burgerm Exp $ \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�0001751�0000144�00000005212�12614544635�014052� 0����������������������������������������������������������������������������������������������������ustar �hornik��������������������������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: inspect.Rd,v 1.11 2009/11/05 19:46:28 burgerm Exp $ \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} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������