From f89a67bf6078e1b321ecf493f8be588fa804cf3b Mon Sep 17 00:00:00 2001
From: root < Previous Section: The Four Step Program
+to Using utPLSQL | Next Section:
+Build Test Packages >
+ UTL_FILE lets you read and write files accessible from the server on
+which your database is running. So, theoretically, you could use UTL_FILE
+to write right over your tablespace data files, control files and so on.
+That is, of course, a very bad idea. Server security requires the ability
+to place restrictions on where you can read and write your files.
+ UTL_FILE implements this security by limiting access to files that reside
+in one of the directories specified in the init.ora file (parameter initialization
+file) for the database instance on which UTL_FILE is running.
+ When you call UTL_FILE.FOPEN to open a file, you must specify both the
+location and the name of the file, in separate arguments. This file location
+is then checked against the list of accessible directories.
+ The format of the parameter for file access in the init.ora file is:
+
+utl_file_dir = <directory>
+
+Include a parameter for utl_file_dir for each directory you want to make
+accessible for UTL_FILE operations. The following entries, for example,
+enable four different directories in Unix:
+
+utl_file_dir = /tmp
+ Some observations on working with and setting up accessible directories
+with UTL_FILE:
+ Access is not recursive through subdirectories. If the following lines
+were in your init.ora file, for example,
+
+utl_file_dir = c:\group\dev1
+ Do not include the following entry in Unix systems:
+
+utl_file_dir = .
+
+This would allow you to read/write on the current directory in the operating
+system.
+ Do not enclose the directory names within single or double quotes.
+ In the UNIX environment, a file created by UTL_FILE.FOPEN has as its
+owner the shadow process running the Oracle instance. This is usually the
+oracle owner. If you try to access these files outside of UTL_FILE, you
+will need to have the correct privileges (or be logged in as oracle) to
+access or change these files.
+ You should not end your directory name with a delimiter, such as the
+forward slash in Unix. The following specification of a directory will
+result in problems when trying to read from or write to the directory:
+
+utl_file_dir = /tmp/orafiles/
+
+After you modify your parameter initialization file, you will need to stop
+and then restart your database instance.
+ Test UTL_FILE Access
+ If you have never before used or relied on UTL_FILE, you should write
+a simple test to verify that UTL_FILE is now working. You can use the code
+shown below (after changing your directory names and names for existing
+and new files) to make sure you've got it running properly.
+ < Previous Section: A "Test Run" with utPLSQL
+| Next Section: Examples >
+
+
+
+
+
+
+
+Authors: Steven Feuerstein,
+Chris Rimmer
+
+Copyright 2000-2001, all rights
+reserved
+
+[ Home | Getting
+Started | Build Test Packages
+| Examples | User
+Guide | Release Notes | Document
+Map ]
+
+
+
+
+Administrative Topics
+
+
+Configuring UTL_FILE
+
+
+Join the Project Team
+
+
+Reporting Bugs and Enhancement Requests
+
+
+Components
+
+
+File Descriptions
+
+
+Administrative Topics
+
+
+Configuring UTL_FILE
+If you want utPLSQL to automatically recompile your test packages, you
+will need to make sure that UTL_FILE is enabled in your database (this
+allows you to read/write operating system files). The database initialization
+parameter file (aka, the "init.ora" file) must have at least one utl_file_dir
+parameter in it for this to work. Here is some background and guidelines
+for working with UTL_FILE:
+
utl_file_dir = /ora_apps/hr/time_reporting
+
utl_file_dir = /ora_apps/hr/time_reporting/log
+
utl_file_dir = /users/test_area
+
+To bypass server security and allow read/write access to all directories,
+you can use this special syntax:
+
+utl_file_dir = *
+
+You should not use this option on production systems. In a development
+system, this entry certainly makes it easier for developers to get up and
+running on UTL_FILE and test their code. You should, however, only allow
+access to a few specific directories when you move the application to production.
+
utl_file_dir = c:\group\prod\oe
+
utl_file_dir = c:\group\prod\ar
+
+then you would not be able to open a file in the c:\group\prod\oe\reports
+subdirectory.
+
+
+SET SERVEROUTPUT ON
+
+DECLARE
+ fid UTL_FILE.FILE_TYPE;
+ v VARCHAR2(32767);
+ PROCEDURE recNgo (str IN VARCHAR2)
+ IS
+ BEGIN
+ DBMS_OUTPUT.PUT_LINE ('UTL_FILE error ' || str);
+
+
+ UTL_FILE.FCLOSE (fid);
+ END;
+BEGIN
+ /* Change the directory name to one to which you at least
+ || THINK you have read/write access.
+ */
+ fid := UTL_FILE.FOPEN ('e:\demo', 'existing_file', 'R');
+ UTL_FILE.GET_LINE (fid, v);
+ dbms_output.put_line (v);
+
+ UTL_FILE.FCLOSE (fid);
+
+
+ fid := UTL_FILE.FOPEN ('e:\demo', 'new_file', 'W');
+
+ UTL_FILE.PUT_LINE (fid, v);
+
+ UTL_FILE.FCLOSE (fid);
+EXCEPTION
+ WHEN UTL_FILE.INVALID_PATH
+ THEN recNgo ('invalid_path');
+ WHEN UTL_FILE.INVALID_MODE
+ THEN recNgo ('invalid_mode');
+ WHEN UTL_FILE.INVALID_FILEHANDLE
+ THEN recNgo ('invalid_filehandle');
+ WHEN UTL_FILE.INVALID_OPERATION
+ THEN recNgo ('invalid_operation');
+ WHEN UTL_FILE.READ_ERROR
+ THEN recNgo ('read_error');
+ WHEN UTL_FILE.WRITE_ERROR
+ THEN recNgo ('write_error');
+ WHEN UTL_FILE.INTERNAL_ERROR
+ THEN recNgo ('internal_error');
+END;
+/
+If an error occurs, it will be displayed on your screen (note: the "set
+serveroutput on" is not required for UTL_FILE to work, but simply to display
+any errors which might occur).
+
+
+Join the utPLSQL Project Team
+
+To take part in the utPLSQL project, please join
+our email distribution group. Go to www.egroups.com,
+join, and subscribe to the utPLSQL list. You will be including in ongoing
+communication about utPLSQL via eGroups.
+
+
+Reporting Bugs and Enhancement Requests
+
+To identify the version of utPLSQL you are running,
+you can execute the following program in SQL*Plus:
+
+SQL> set serveroutput on
+
+SQL> exec dbms_output.put_line (utPLSQL.version)
+
+You can also look inside the utPLSQL package (utPLSQL.pkb)
+and check the value of the g_version private variable.
+
+
+Components
+
+Here are the various components of utPLSQL:
+
+
+Tables
+All tables are created by the tables.sql (or updated by the tablesupg.sql)
+file.
+
+
+
+
+
+
+
+
+ut_suite
+
+
+
+table
+
+
+
+Persistent storage of test suites defined.
+
+
+
+
+
+ut_package
+
+
+
+table
+
+
+
+Persistent storage of packages to be run within
+a suite.
+
+
+
+
+
+ut_test
+
+
+
+table
+
+
+
+Definition of an individual unit test.
+
+
+
+
+
+ut_testcase
+
+
+
+table
+
+
+
+Definition of a test case within a unit test.
+
+
+
+
+ut_config
+
+table
+
+Holds
+configuration information for each user
+
+
+ut_assertion
+
+table
+
+Contains
+the definitions of the various assertion routines in the utAssert package.
+This table will be used (potentially) by GUI front ends to utPLSQL.
+
+Packages
+All packages are created the code.sql file.
+
+
+
+
+
+
+
+
+utPLSQL
+
+
+
+package
+
+
+
+Main test package.
+
+
+
+
+utConfig
+
+package
+
+API to ut_config table
+
+
+
+
+utSuite
+
+
+
+package
+
+
+
+API to ut_suite table.
+
+
+
+
+
+utPackage
+
+
+
+package
+
+
+
+API to ut_ package table.
+
+
+
+
+
+utTest
+
+
+
+package
+
+
+
+API to ut_test table.
+
+
+
+
+
+utTestcase
+
+
+
+package
+
+
+
+API to ut_testcase table.
+
+
+
+
+
+utAssert
+
+
+
+package
+
+
+
+Collection of assertion routines available to test
+the results of your programs.
+
+
+
+
+
+utResult
+
+
+
+package
+
+
+
+API to the results array that is populated by calls
+to the utAssert assertions.
+
+
+
+
+utGen
+
+
+
+package
+
+
+
+Generates a starting point for test packages for
+your own package.
+
+
+File Descriptions
+
+
+
+
diff --git a/documentation/src/advanced.html b/documentation/src/advanced.html
new file mode 100644
index 000000000..11a36b06c
--- /dev/null
+++ b/documentation/src/advanced.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+Authors: Steven Feuerstein,
+Chris Rimmer
+
+Copyright 2000-2001, all rights
+reserved
+
+[ Home | Getting
+Started | Build Test Packages
+| Examples | User
+Guide | Release Notes | Document
+Map ]
+
+
+
+
+Advanced Topics
+
+
+Building Your Own Test Engine
+
+
+Analyze Test Statistics
+
+
+
diff --git a/documentation/src/authors.txt b/documentation/src/authors.txt
new file mode 100644
index 000000000..1e7fded7a
--- /dev/null
+++ b/documentation/src/authors.txt
@@ -0,0 +1,5 @@
+# This contains a list of authors and their
+# email address, separated by a comma
+#
+Steven Feuerstein,steven@stevenfeuerstein.com
+Chris Rimmer,c@24.org.uk
diff --git a/documentation/src/build_docs.pl b/documentation/src/build_docs.pl
new file mode 100755
index 000000000..1d3055e34
--- /dev/null
+++ b/documentation/src/build_docs.pl
@@ -0,0 +1,132 @@
+#!/usr/bin/perl -w
+#
+# This perl script builds the documentation for utPLSQL
+# $Id$
+#
+
+use strict;
+
+#The directory the output goes into
+my $OUTDIR = "..";
+
+#Holds the map
+my @map;
+
+#Holds the top navigation string
+my $nav = "";
+
+#Read the map file
+open MAP, "map.txt" or die "Cannot open map.txt";
+my ($filename,$desc,$section);
+while (
-· - Modify -implementation if ieqminus to avoid duplicate column
+-· - Change +
-· - Add -utAssert2.fileExists
+you can still use your prefix-based approaches).-· - Compile -utAssert2 with AUTHID CURRENT_USER for Oracle8i and above.
+-· - 2.0.7.2 -Fix index creation for ut_assertion table.
+-· - 2.0.7.2 -Fix foreign key definition in ut_argument.
+-· - 2.0.7.2 -Fix foreign key definition in uta_eq.
+-· - Fix -to utgen.pkb to allow generation of procedure bodies when no grid is used.
- --· - Allow -developers to turn off display of successful results (utResult.include_successes)
+-· - Allow +
-· - Add +
-· - Add +
-· - Implement -test suite execution in utPLSQL2.
+-· - Implement +
-· - Improved +
-· - Revamp +
-· - Create -stand alone utverify procedure.
+-· - Add +
-· - utAssert.eqfile -now flags problems when opening files.
+-· +
utPLSQL.addtest ('ut_betwnstr');
-utPLSQL +utPLSQL will no longer add the prefix for you. This means that you may need to change -your calls to addtest -- or remove them entirely and rely on auto-registration.
- +your calls to addtest -- or remove them entirely and rely on auto-registration. + +![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Test an Entire Package
-API | Next Section: Use Non-Default Prefix >
-
+
SQL> exec utplsql.showconfig
+SQL> exec utconfig.showconfig
=============================================================
utPLSQL Configuration for SCOTT
Directory: e:\openoracle\utplsql\utinstall\examples
diff --git a/documentation/src/started.html b/documentation/src/started.html
index 9522e4ed2..4c5299df2 100644
--- a/documentation/src/started.html
+++ b/documentation/src/started.html
@@ -1,34 +1,7 @@
-
-
-
-
-
- utPLSQL - Getting started
-
-
-
-![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting Started | Build -Test Packages | Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Home | Next
-Section: Glossary and Requirements >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Use Non-Default Prefix
-| Next Section: User Guide >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Test a Function |
-Next
-Section: Put Test Code in Same Package >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Test a Procedure |
-Next
-Section: Test an Entire Package API >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Cross-Reference by Assertion
-Type | Next Section: Test a Function >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: How to build a test package
-| Next Section: Advanced Topics >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User Guide | Release -Notes | Document Map ] -
< Previous Section: Create and Run a Test Suite
-| Next Section: utPLSQL Package >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: utResult Package |
-Next
-Section: utGen Package >
-
-
-
This package contains the following procedures and functions:
-
+
+
This package contains the following procedures and functions:
+
PROCEDURE ut_BETWNSTR IS+ utAssert offers a wide (and ever expanding) set of assertion programs that +allow you to efficiently (a) test the outcome of your unit test and (b) report +the results of that test to utPLSQL. You should review +Common Assertion Parameters and Behavior before using any specific assertion +program. It is also possible to build your own assertion +routine. Note: all utAssert assertions are defined in the ut_assertion +table, as well as actually coded in the utAssert package. +
BEGIN
utAssert.eq (
'Typical valid usage',
BETWNSTR(
STRING_IN => 'abcdefg'
,
START_IN => 3
,
END_IN => 5
),
'cde'
);
END;
| msg_in | +A message to be displayed if the assertion +fails. This is the first argument and is mandatory, because the tests need +to be self documenting. | +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| check_this_in | +The value to be checked.. If a Boolean expression, +this will usually include the invocation of the method being tested, resulting +in a single line of code for the entire test case. | +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| against_this_in | +For assert_eq, the assertion routine will +check the check_this_in value against the against_this_in value. This parameter +should be the certifiably correct value. | +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| null_ok_in | +TRUE if a NULL value should be interpreted +as a successful test, FALSE if NULL indicates failure. | +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise_exc_in | +TRUE if it is OK for the assertion routine +to allow an exception to be propagated out unhandled. | +
| -msg_in - | - --A message -to be displayed if the assertion fails. This is the first argument and -is mandatory, because the tests need to be self documenting. - | -
| -check_this_in - | - --The value -to be checked.. If a Boolean expression, this will usually include the -invocation of the method being tested, resulting in a single line of code -for the entire test case. - | -
| -against_this_in - | - --For assert_eq, -the assertion routine will check the check_this_in value against the against_this_in -value. This parameter should be the certifiably correct value. - | -
| -null_ok_in - | - --TRUE if -a NULL value should be interpreted as a successful test, FALSE if NULL -indicates failure. - | -
| -raise_exc_in - | - --TRUE if -it is OK for the assertion routine to allow an exception to be propagated -out unhandled. - | -
To request results immediately, call the showResults procedure: -
PROCEDURE utAssert.showresults;-This a session-level setting, and must be reset each time you connect to -Oracle. You can turn off showing of results by calling the noShowResults -program: + +
To request results immediately, call the showResults procedure:
PROCEDURE utAssert.showresults;+ This a session-level setting, and must be reset each time you connect to +Oracle. You can turn off showing of results by calling the noShowResults program:
PROCEDURE utAssert.noshowresults;-To determine the current status of this setting, you can call the following -function: -
FUNCTION utAssert.showing_results return boolean;-Here is an example of a simple script that calls an assertion routine and -immediately shows the results: -
set serveroutput on size 1000000
-BEGIN
- utAssert.showresults;
- utAssert.eq (
- 'Test of betwn',
- str.betwn ('this is a string', 3, 7),
- 'this is a pipe' );
-END;
-/
-And here is the result of running this script:
-FAILURE: "Unnamed Test" -: Test of betwn; expected "this is a pipe", got "is is"- -
PROCEDURE utAssert.this (- -
msg_in IN VARCHAR2,- -
check_this_in IN BOOLEAN,- -
null_ok_in IN BOOLEAN := FALSE,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -Use utAssert.this when you have a Boolean expression -that you want to check, as in:. - -
BEGIN- -
...- -
utAssert.this (- -
'Boolean function result',- -
is_valid_account (my_account)- -
);- -You can also use this assertion to register a failure, -most usually in an exception section, as in: - -
EXCEPTION- -
WHEN OTHERS- -
THEN - utAssert.this (- -
SQLERRM,- -
FALSE);- -Generally, you should avoid utAssert.this and instead -use a specialized assertion routine, documented below. Most of the assertions -give you the ability check for equality (of scalars, such as strings, or -more complex data structures like tables, pipes and files): does the data -generated by my code match the expected value(s)? - -
PROCEDURE utAssert.isnotnull ( - msg_in IN VARCHAR2, - check_this_in IN VARCHAR2, - null_ok_in IN BOOLEAN := FALSE, - raise_exc_in IN BOOLEAN := FALSE -); - -PROCEDURE utAssert.isnull ( - msg_in IN VARCHAR2, - check_this_in IN VARCHAR2, - null_ok_in IN BOOLEAN := FALSE, - raise_exc_in IN BOOLEAN := FALSE -); - -PROCEDURE utAssert.isnotnull ( - msg_in IN VARCHAR2, - check_this_in IN BOOLEAN, - null_ok_in IN BOOLEAN := FALSE, - raise_exc_in IN BOOLEAN := FALSE -); - -PROCEDURE utAssert.isnull ( - msg_in IN VARCHAR2, - check_this_in IN BOOLEAN, - - null_ok_in IN BOOLEAN := FALSE, - raise_exc_in IN BOOLEAN := FALSE -);- -Use these assertions when -you simply want to check if a scalar expression (string, date, number and -Boolean are supported) is NULL or NOT NULL, as in: - -
BEGIN- -
...- -
utAssert.isNULL (- -
'Should be nothing left',- -
TRANSLATE (digits_in_string, 'A1234567890', 'A')- -
);- -
Here is the header for the scalar equality check assertion: -
PROCEDURE utAssert.eq (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2|BOOLEAN|DATE|NUMBER,- -
against_this_in IN VARCHAR2|BOOLEAN|DATE|NUMBER,- -
null_ok_in IN BOOLEAN := FALSE,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -If the two values are equal, your code gets a green -light. Otherwise, utAssert writes the test results to the utResult package, -resulting in a red light for the test. - -If NULL values are considered value for this test, -pass TRUE for null_ok_in. If you want the assertion to raise an exception -on failure and stop the test from proceeding, pass TRUE for raise_exc_in. - -Here is an example of using the utAssert.eq program: - -
PROCEDURE ut_emp_dept_lookuprowcount - IS- -
l_rowcount1 PLS_INTEGER; - l_rowcount2 PLS_INTEGER; - BEGIN - -- Run baseline code. - SELECT COUNT (*) - INTO l_rowcount1 - FROM employee - WHERE department_id = 30;- -
- -
-- Compare to program call: - l_rowcount2 := - te_employee.emp_dept_lookuprowcount (30);- -
- -
-- Test results - utassert.eq ( - 'Successful EMP_DEPT_LOOKUPROWCOUNT', - l_rowcount2, - l_rowcount1 - ); - END;- -
PROCEDURE utAssert.eqtable (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2,- -
against_this_in IN VARCHAR2,- -
check_where_in IN VARCHAR2 := NULL,- -
against_where_in IN VARCHAR2 := NULL,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -where check_this_in and against_this_in are the -names of tables or views. You can supply an optional WHERE clause to restrict -the rows you wish to compare. Here is an example that calls eqTable twice, -to test two different conditions. - -
PROCEDURE ut_del1 -IS - fdbk PLS_INTEGER; -BEGIN - /* Delete that finds now rows. */ - - EXECUTE IMMEDIATE ' - DELETE FROM ut_DEL1 - WHERE employee_id = -1 - '; - te_employee.del (-1, rowcount_out => fdbk);- -
-- Test results
- utassert.eqtable ('Delete rows', 'EMPLOYEE', 'ut_DEL1');
- /* Successful delete */
-
- EXECUTE IMMEDIATE '
- DELETE FROM ut_DEL1
- WHERE employee_id between 7800 and 7899
- ';
-
- FOR rec IN (SELECT *
- FROM employee
- WHERE employee_id BETWEEN 7800 AND 7899)
- LOOP
- te_employee.del (
- rec.employee_id,
- rowcount_out => fdbk
- );
- END LOOP;
-
- -- Test results
- utassert.eqtable ('Delete rows', 'EMPLOYEE', 'ut_DEL1');
- ROLLBACK;
-EXCEPTION
- WHEN OTHERS
- THEN
- utassert.this (
- 'DEL1 exception ' || SQLERRM,
- SQLCODE = 0
- );
-END;
-
-PROCEDURE utAssert.eqtabcount (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2,- -
against_this_in IN VARCHAR2,- -
check_where_in IN VARCHAR2 := NULL,- -
against_where_in IN VARCHAR2 := NULL,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -where check_this_in and against_this_in are the -names of tables or views. As in utAssert.eqtable, you can supply an optional -WHERE clause to restrict the rows you wish to compare. The following test -will compare the number of rows in the CD_COLLECTION and UT_TEST_5_1 tables -where the given condition holds: -
utassert.eqtabcount('Test 5.1: Insert new rows',
-
-'CD_COLLECTION',- -
'UT_TEST_5_1',- -
'ARTIST = ''The Fall''',- -
'ARTIST = ''The Fall''');- - -
PROCEDURE utAssert.eqquery (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2,- -
against_this_in IN VARCHAR2,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -If you want the assertion to raise an exception -on failure and stop the test from proceeding, pass TRUE for raise_exc_in. - -Here is an example of using eqQuery: - -
PROCEDURE ut_upd1 -IS -BEGIN - /* Update 3 columns by ID */ - - EXECUTE IMMEDIATE ' - UPDATE ut_UPD1 SET - FIRST_NAME = ''SILLY'', - HIRE_DATE = trunc (SYSDATE+100), - COMMISSION = 5000 - WHERE - EMPLOYEE_ID = 7600 - '; - te_employee.upd ( - 7600, - first_name_in => 'SILLY', - commission_in => 5000, - hire_date_in => TRUNC (SYSDATE + 100), - rowcount_out => fdbk - ); - -- Test results (audit fields are different so do a query) - utassert.eqquery ( - 'Update three columns', - 'select first_name, commission, hire_date from EMPLOYEE', - 'select first_name, commission, hire_date from ut_upd1' - ); - ROLLBACK; -END;- -
SELECT fixed_value -FROM DUAL;-Unfortunately, if the query returns multiple values or the wrong value -we will only be told that the test has failed with no details. This -is where utAssert.eqqueryvalue comes to the rescue. The procedure -is declared as follows: -
PROCEDURE utAssert.eqqueryvalue (- -
msg_in IN VARCHAR2,- -
check_query_in IN VARCHAR2,- -
against_value_in IN VARCHAR2|NUMBER|DATE,- -
raise_exc_in IN BOOLEAN := FALSE- -
);-Where check_query_in is the query in question and against_value_in is the -value to check it against. If the query returns more than one value, -the resulting error message will tell you this. Similarly, if the -query returns the wrong value, the message will state the expected and -obtained values. The following call compares the maximum value found -in a table against a given number value: -
utAssert.eqqueryvalue('Maximum value test',
-
-'SELECT MAX(MEMORY)- -
FROM COMPUTERS- -
WHERE OS IN (''Linux'', ''Unix'')',
-
-256);-Obviously this should only return a single value, but if it returns something -other than 256, we'll know about it. -
PROCEDURE utAssert.eqfile (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2,- -
check_this_dir_in IN VARCHAR2,- -
against_this_in IN VARCHAR2,- -
against_this_dir_in IN VARCHAR2 := NULL,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -If you want the assertion to raise an exception -on failure and stop the test from proceeding, pass TRUE for raise_exc_in. - -You must specify the directory containing the "check -this" file; if you do not specify a directory for the "against this" file, -the "check this" directory will be used. - -Here is an example of using eqFile (see ut_DEPARTMENT2file.pkg -in the Examples directory for the full implementation): - -
PROCEDURE ut_DEPARTMENT2FILE IS -BEGIN - DEPARTMENT2FILE ( - LOC => 'c:\temp', - FILE => 'department.dat', - DELIM => '***' - ); - - utAssert.eqfile ( - 'Test of DEPARTMENT2FILE', - 'department.dat', - 'c:\temp', - 'department.tst', - 'c:\temp' - ); -END ut_DEPARTMENT2FILE;- -
PROCEDURE utAssert.eqpipe (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2,- -
against_this_in IN VARCHAR2,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -If you want the assertion to raise an exception -on failure and stop the test from proceeding, pass TRUE for raise_exc_in. - -To check the contents of a pipe based on the execution -of code, you will need to populate a pipe against which to test equality. -The employee_pipe.pkg file in the Examples directory contains a demonstration -of the kind of code you might write to do this. - -This package contains all of the unit test code -within the same package. Here is my unit test program, which relies on -the utAssert.eqpipe program: - -
PROCEDURE ut_fillpipe IS
- stat PLS_INTEGER;
-BEGIN
- emptypipe ('emps');
- emptypipe ('emps2');
-
- fillpipe ('emps');
-
- /* Direct filling of pipe. */
+ To determine the current status of this setting, you can call the following
+function: FUNCTION utAssert.showing_results return boolean;
+ Here is an example of a simple script that calls an assertion routine and
+immediately shows the results: set serveroutput on size 1000000
BEGIN
utAssert.showresults;
utAssert.eq (
'Test of betwn',
str.betwn ('this is a string', 3, 7),
'this is a pipe' );
END;
/
+ And here is the result of running this script: FAILURE: "Unnamed Test"
: Test of betwn; expected "this is a pipe", got "is is"
+
+ Generic "Assert This" Assertion Procedure
+ This most generic assertion program simply says "assert this" and passes
+a Boolean expression. It is used by all the other assertion routines, which
+construct a Boolean expression from their specific values and logic.
+ PROCEDURE utAssert.this (
+ msg_in IN VARCHAR2,
+ check_this_in IN BOOLEAN,
+ null_ok_in IN BOOLEAN := FALSE,
+ raise_exc_in IN BOOLEAN := FALSE
+ );
+ Use utAssert.this when you have a Boolean expression that you want to check,
+as in:. BEGIN
+ ...
+ utAssert.this (
+ 'Boolean function result',
+ is_valid_account (my_account)
+ );
+ You can also use this assertion to register a failure, most usually in
+an exception section, as in: EXCEPTION
+ WHEN OTHERS
+ THEN
utAssert.this (
+ SQLERRM,
+ FALSE);
+ Generally, you should avoid utAssert.this and instead use a specialized
+assertion routine, documented below. Most of the assertions give you the
+ability check for equality (of scalars, such as strings, or more complex
+data structures like tables, pipes and files): does the data generated by
+my code match the expected value(s)?
+ Check for NULL and NOT NULL Values
+ You can check to see if a value is NULL or is NOT NULL with the following
+assertions: PROCEDURE utAssert.isnotnull (
msg_in IN VARCHAR2,
check_this_in IN VARCHAR2,
null_ok_in IN BOOLEAN := FALSE,
raise_exc_in IN BOOLEAN := FALSE
);
PROCEDURE utAssert.isnull (
msg_in IN VARCHAR2,
check_this_in IN VARCHAR2,
null_ok_in IN BOOLEAN := FALSE,
raise_exc_in IN BOOLEAN := FALSE
);
PROCEDURE utAssert.isnotnull (
msg_in IN VARCHAR2,
check_this_in IN BOOLEAN,
null_ok_in IN BOOLEAN := FALSE,
raise_exc_in IN BOOLEAN := FALSE
);
PROCEDURE utAssert.isnull (
msg_in IN VARCHAR2,
check_this_in IN BOOLEAN,
null_ok_in IN BOOLEAN := FALSE,
raise_exc_in IN BOOLEAN := FALSE
);
+ Use these assertions when you simply want to check if a scalar expression
+(string, date, number and Boolean are supported) is NULL or NOT NULL, as
+in:
+
+ BEGIN
+ ...
+ utAssert.isNULL (
+ 'Should be nothing left',
+ TRANSLATE (digits_in_string, 'A1234567890', 'A')
+ );
+
+ Check Equality of Scalar Values
+ If you need to compare two dates or two strings or two numbers or two Booleans,
+use the utAssert.eq assertion program.
+Here is the header for the scalar equality check assertion:
PROCEDURE utAssert.eq (
+ msg_in IN VARCHAR2,
+ check_this_in IN VARCHAR2|BOOLEAN|DATE|NUMBER,
+ against_this_in IN VARCHAR2|BOOLEAN|DATE|NUMBER,
+ null_ok_in IN BOOLEAN := FALSE,
+ raise_exc_in IN BOOLEAN := FALSE
+ );
+ If the two values are equal, your code gets a green light. Otherwise, utAssert
+writes the test results to the utResult package, resulting in a red light
+for the test. If NULL values are considered value for this test, pass TRUE
+for null_ok_in. If you want the assertion to raise an exception on failure
+and stop the test from proceeding, pass TRUE for raise_exc_in. Here is an
+example of using the utAssert.eq program: PROCEDURE ut_emp_dept_lookuprowcount
IS
+ l_rowcount1 PLS_INTEGER;
l_rowcount2 PLS_INTEGER;
BEGIN
-- Run baseline code.
SELECT COUNT (*)
INTO l_rowcount1
FROM employee
WHERE department_id = 30;
+
+ -- Compare to program call:
l_rowcount2 :=
te_employee.emp_dept_lookuprowcount (30);
+
+ -- Test results
utassert.eq (
'Successful EMP_DEPT_LOOKUPROWCOUNT',
l_rowcount2,
l_rowcount1
);
END;
+
+ Check Equality of DatabaseTables
+ If your test performs DML operations (update, insert or delete), you will
+need to check your results in a database table. You could do this by querying
+the results into local variables and then calling utAssert.eq to check those
+values against your expected data. That can be a very laborious process,
+so utAssert offers the eqtable and equerry assertion routines to streamline
+the process. Both these procedures use the MINUS SQL operator to essentially
+"subtract" the contents of one table (query) from the other. If anything
+is left, then the two tables (queries) are not the same and the test is given
+a red light. As you can probably see, the structure of the two tables (queries)
+must be identical for this assertion to work properly. The utAssert.eqtable
+allows you to compare the contents of your data table (changed by your code)
+against another table, which you can preset with the data you expect to see
+after the test. Here is the header for eqtable: PROCEDURE utAssert.eqtable (
+ msg_in IN VARCHAR2,
+ check_this_in IN VARCHAR2,
+ against_this_in IN VARCHAR2,
+ check_where_in IN VARCHAR2 := NULL,
+ against_where_in IN VARCHAR2 := NULL,
+ raise_exc_in IN BOOLEAN := FALSE
+ );
+ where check_this_in and against_this_in are the names of tables or views.
+You can supply an optional WHERE clause to restrict the rows you wish to
+compare. Here is an example that calls eqTable twice, to test two different
+conditions. PROCEDURE ut_del1
IS
fdbk PLS_INTEGER;
BEGIN
/* Delete that finds now rows. */
EXECUTE IMMEDIATE '
DELETE FROM ut_DEL1
WHERE employee_id = -1
';
te_employee.del (-1, rowcount_out => fdbk);
+ -- Test results
utassert.eqtable ('Delete rows', 'EMPLOYEE', 'ut_DEL1');
/* Successful delete */
EXECUTE IMMEDIATE '
DELETE FROM ut_DEL1
WHERE employee_id between 7800 and 7899
';
FOR rec IN (SELECT *
FROM employee
WHERE employee_id BETWEEN 7800 AND 7899)
LOOP
te_employee.del (
rec.employee_id,
rowcount_out => fdbk
);
END LOOP;
-- Test results
utassert.eqtable ('Delete rows', 'EMPLOYEE', 'ut_DEL1');
ROLLBACK;
EXCEPTION
WHEN OTHERS
THEN
utassert.this (
'DEL1 exception ' || SQLERRM,
SQLCODE = 0
);
END;
+
+ Check Equality of Table Counts
+ If your tests simply produce the right number of rows in a table but not
+a fixed set of values, you will not be able to use
+utAssert.eqtable above. However, utAssert.eqtabcount allows you to simply
+test that the numbers of rows are equal. The declaration of the procedure
+is as follows: PROCEDURE utAssert.eqtabcount (
+ msg_in IN VARCHAR2,
+ check_this_in IN VARCHAR2,
+ against_this_in IN VARCHAR2,
+ check_where_in IN VARCHAR2 := NULL,
+ against_where_in IN VARCHAR2 := NULL,
+ raise_exc_in IN BOOLEAN := FALSE
+ );
+ where check_this_in and against_this_in are the names of tables or views.
+As in utAssert.eqtable, you can supply an optional WHERE clause to restrict
+the rows you wish to compare. The following test will compare the number
+of rows in the CD_COLLECTION and UT_TEST_5_1 tables where the given condition
+holds: utassert.eqtabcount('Test 5.1: Insert new rows',
+ 'CD_COLLECTION',
+ 'UT_TEST_5_1',
+ 'ARTIST = ''The Fall''',
+ 'ARTIST = ''The Fall''');
- FOR rec IN (SELECT *
- FROM employee)
- LOOP
- DBMS_PIPE.RESET_BUFFER;
- DBMS_PIPE.PACK_MESSAGE (rec.EMPLOYEE_ID);
- DBMS_PIPE.PACK_MESSAGE (rec.LAST_NAME);
- DBMS_PIPE.PACK_MESSAGE (rec.FIRST_NAME);
- DBMS_PIPE.PACK_MESSAGE (rec.MIDDLE_INITIAL);
- DBMS_PIPE.PACK_MESSAGE (rec.JOB_ID);
- DBMS_PIPE.PACK_MESSAGE (rec.MANAGER_ID);
- DBMS_PIPE.PACK_MESSAGE (rec.HIRE_DATE);
- DBMS_PIPE.PACK_MESSAGE (rec.SALARY);
- DBMS_PIPE.PACK_MESSAGE (rec.COMMISSION);
- DBMS_PIPE.PACK_MESSAGE (rec.DEPARTMENT_ID);
- DBMS_PIPE.PACK_MESSAGE (rec.CHANGED_BY);
- DBMS_PIPE.PACK_MESSAGE (rec.CHANGED_ON);
-
- stat := DBMS_PIPE.SEND_MESSAGE ('emps2', 0);
- END LOOP;
-
- /* Compare the two */
- utassert.eqpipe (
- 'Two employee pipes', 'emps', 'emps2');
-
-END ut_fillpipe;
-
-Since I have stored my unit
-test logic with my source code package, I would run my test as follows:
-
-SQL> exec utplsql.test ('employee_pipe', samepackage_in=>TRUE)
-FAILURE: "employee_pipe"
-fillpipe: Pipes equal? Compared "emps" against "emps2"
-
-/* Direct access to collections */- -
PROCEDURE utAssert.eqcoll (- -
msg_in IN VARCHAR2,- -
check_this_in IN VARCHAR2, /* pkg1.coll */- -
against_this_in IN VARCHAR2, /* pkg2.coll */- -
eqfunc_in IN VARCHAR2 := NULL,- -
check_startrow_in IN PLS_INTEGER := NULL,- -
check_endrow_in IN PLS_INTEGER := NULL,- -
against_startrow_in IN PLS_INTEGER := NULL,- -
against_endrow_in IN PLS_INTEGER := NULL,- -
match_rownum_in IN BOOLEAN := FALSE,- -
null_ok_in IN BOOLEAN := TRUE,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -
- -
/* API based access to collections */- -
PROCEDURE utAssert.eqcollapi (- -
msg_in IN VARCHAR2,- -
check_this_pkg_in IN VARCHAR2,- -
against_this_pkg_in IN VARCHAR2,- -
eqfunc_in IN VARCHAR2 := NULL,- -
countfunc_in IN VARCHAR2 := 'COUNT',- -
firstrowfunc_in IN VARCHAR2 := 'FIRST',- -
lastrowfunc_in IN VARCHAR2 := 'LAST',- -
nextrowfunc_in IN VARCHAR2 := 'NEXT',- -
getvalfunc_in IN VARCHAR2 := 'NTHVAL',- -
check_startrow_in IN PLS_INTEGER := NULL,- -
check_endrow_in IN PLS_INTEGER := NULL,- -
against_startrow_in IN PLS_INTEGER := NULL,- -
against_endrow_in IN PLS_INTEGER := NULL,- -
match_rownum_in IN BOOLEAN := FALSE,- -
null_ok_in IN BOOLEAN := TRUE,- -
raise_exc_in IN BOOLEAN := FALSE- -
);- -where the eqcoll-specific parameters are as follows: - -
| Parameter | - -Description | -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -msg_in - | - --The message to be displayed if the test failes - | -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -check_this_in - | - --The name of the collection to be checked. Format: -package.collection. In other words, the collection must be defined in a -package specification. Use eqCollAPI (and check_this_pkg_in) if you want -to hide the declaration of your collection in your package body (recommended). - | -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -against_this_in - | - -
-The name of the collection to be checked against.
+Asserting Query Equality+ The utAssert.eqquery allows you to compare the data returned by two queries +(strings that are contained in the check_this_in and against_this_in parameters). +In this case, you specify the full SELECT statements for each query as the +parameters. By using equery, you may be able to avoid constructing a separate +table with preset data.PROCEDURE utAssert.eqquery (+ msg_in IN VARCHAR2,+ check_this_in IN VARCHAR2,+ against_this_in IN VARCHAR2,+ raise_exc_in IN BOOLEAN := FALSE+ );+ If you want the assertion to raise an exception on failure and stop the +test from proceeding, pass TRUE for raise_exc_in. Here is an example of +using eqQuery: PROCEDURE ut_upd1+ + Check Query Equality against a Single Value+ Often we will wish to test the result of a query against a single value rather +than another query as in utAssert.eqquery above. +It is possible to get around this problem by using a trivial query of the +form:SELECT fixed_value+ Unfortunately, if the query returns multiple values or the wrong value we +will only be told that the test has failed with no details. This is where +utAssert.eqqueryvalue comes to the rescue. The procedure is declared as +follows: PROCEDURE utAssert.eqqueryvalue (+ msg_in IN VARCHAR2,+ check_query_in IN VARCHAR2,+ against_value_in IN VARCHAR2|NUMBER|DATE,+ raise_exc_in IN BOOLEAN := FALSE+ );+ Where check_query_in is the query in question and against_value_in is the +value to check it against. If the query returns more than one value, the +resulting error message will tell you this. Similarly, if the query returns +the wrong value, the message will state the expected and obtained values. + The following call compares the maximum value found in a table against a +given number value: utAssert.eqqueryvalue('Maximum value test',
+ 'SELECT MAX(MEMORY)+ FROM COMPUTERS+ WHERE OS IN (''Linux'', ''Unix'')',
+ 256);+ Obviously this should only return a single value, but if it returns something +other than 256, we'll know about it. + Check Equality of Files+ Many programs generate output to operating system files; alternatively, +you might write data to a file simply to test results. Use the eqfile assertion +for either of these scenarios. This procedure uses PL/SQL's UTL_FILE package +to compare the contents of two different files. Note: If you have not used +UTL_FILE in the past, you must configure + it before it can be used -- by utPLSQL or by your own code. UTL_FILE must +be allowed accss to either or both of the directories you specify (this involves +setting the utl_file_dir database parameter).PROCEDURE utAssert.eqfile (+ msg_in IN VARCHAR2,+ check_this_in IN VARCHAR2,+ check_this_dir_in IN VARCHAR2,+ against_this_in IN VARCHAR2,+ against_this_dir_in IN VARCHAR2 := NULL,+ raise_exc_in IN BOOLEAN := FALSE+ );+ If you want the assertion to raise an exception on failure and stop the +test from proceeding, pass TRUE for raise_exc_in. You must specify the directory +containing the "check this" file; if you do not specify a directory for the +"against this" file, the "check this" directory will be used. Here is an +example of using eqFile (see ut_DEPARTMENT2file.pkg in the Examples directory +for the full implementation): PROCEDURE ut_DEPARTMENT2FILE IS+ + Check Equality of Database Pipes+ Database pipes offer a handy mechanism for passing data between different +sessions connected to the RDBMS. It is important to know that pipes are being +filled properly; use the eqpipe to check this condition. With the eqpipe +procedure, you compare the contents of two different pipes.PROCEDURE utAssert.eqpipe (+ msg_in IN VARCHAR2,+ check_this_in IN VARCHAR2,+ against_this_in IN VARCHAR2,+ raise_exc_in IN BOOLEAN := FALSE+ );+ If you want the assertion to raise an exception on failure and stop the +test from proceeding, pass TRUE for raise_exc_in. To check the contents +of a pipe based on the execution of code, you will need to populate a pipe +against which to test equality. The employee_pipe.pkg file in the Examples +directory contains a demonstration of the kind of code you might write to +do this. This package contains all of the unit test code within the same +package. Here is my unit test program, which relies on the utAssert.eqpipe +program: PROCEDURE ut_fillpipe IS+ Since I have stored my unit test logic with my source code package, I would +run my test as follows: SQL> exec utplsql.test ('employee_pipe', samepackage_in=>TRUE)
+
+Check Equality of Collections+ Collections are as close as you come to arrays in PL/SQL. They are very +useful for managing lists of information, but can be difficult to debug and +maintain. With the eqcoll and eqcollAPI procedures, you can compare the +contents of two different arrays. Use the eqColl procedure when you want +to compare two collections that are defined in the specification of a package. +Use the eqCollAPI procedure when you want to compare two collections that +are defined in the body of a package, with programs defined in the specification +(an API) to access and manipulate the collections. The collection equality +check headers are:/* Direct access to collections */+ PROCEDURE utAssert.eqcoll (+ msg_in IN VARCHAR2,+ check_this_in IN VARCHAR2, /* pkg1.coll */+ against_this_in IN VARCHAR2, /* pkg2.coll */+ eqfunc_in IN VARCHAR2 := NULL,+ check_startrow_in IN PLS_INTEGER := NULL,+ check_endrow_in IN PLS_INTEGER := NULL,+ against_startrow_in IN PLS_INTEGER := NULL,+ against_endrow_in IN PLS_INTEGER := NULL,+ match_rownum_in IN BOOLEAN := FALSE,+ null_ok_in IN BOOLEAN := TRUE,+ raise_exc_in IN BOOLEAN := FALSE+ );+ + /* API based access to collections */+ PROCEDURE utAssert.eqcollapi (+ msg_in IN VARCHAR2,+ check_this_pkg_in IN VARCHAR2,+ against_this_pkg_in IN VARCHAR2,+ eqfunc_in IN VARCHAR2 := NULL,+ countfunc_in IN VARCHAR2 := 'COUNT',+ firstrowfunc_in IN VARCHAR2 := 'FIRST',+ lastrowfunc_in IN VARCHAR2 := 'LAST',+ nextrowfunc_in IN VARCHAR2 := 'NEXT',+ getvalfunc_in IN VARCHAR2 := 'NTHVAL',+ check_startrow_in IN PLS_INTEGER := NULL,+ check_endrow_in IN PLS_INTEGER := NULL,+ against_startrow_in IN PLS_INTEGER := NULL,+ against_endrow_in IN PLS_INTEGER := NULL,+ match_rownum_in IN BOOLEAN := FALSE,+ null_ok_in IN BOOLEAN := TRUE,+ raise_exc_in IN BOOLEAN := FALSE+ );+ where the eqcoll-specific parameters are as follows: +
and the eqcollAPI-specific parameters are as follows:
-
The parameters common to both eqColl and eqCollAPI
-are as follows
-
Here is an example of a script that uses utAssert.eqColl -(taken from filepath1.pkg in the Examples directory): - PROCEDURE ut_setpath -IS + +
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] - < Previous Section: utPLSQL Package | Next
-Section: utResult Package >
- utConfig Package@@ -132,13 +104,13 @@
SQL> exec utconfig.showconfig
=============================================================
utPLSQL Configuration for SCOTT
Directory: /apps/utplsql/code
@@ -147,7 +119,7 @@
And here is an example of calling showConfig for a different schema:
-SQL> exec utconfig.showconfig ('COMP')
+ |
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: utAssert Package |
-Next
-Section: Define Test Suites>
-
+
- -
| - - | -
Authors: Steven Feuerstein, Chris Rimmer
- -Copyright 2000-2001, all rights reserved
- -[ Home | Getting Started | Build Test Packages | Examples | User Guide -| Release Notes | Document Map -]
- -< Previous Section: User Guide | Next Section: utConfig Package >
-
The utPLSQL package offers the following capabilities:
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: utConfig Package |
-Next
-Section: utAssert Package >
-
+
![]() |
-
-Authors: Steven Feuerstein, -Chris Rimmer - -Copyright 2000-2001, all rights -reserved --[ Home | Getting -Started | Build Test Packages -| Examples | User -Guide | Release Notes | Document -Map ] -
< Previous Section: Examples | Next
-Section: Test a Procedure >
-
+
+BEGIN + Some.Example(Code); +END; ++ +
+The utPLSQL documentation is built from a series of simple HTML files in the +src directory. These files have none of the navigation bars, logos or +next/previous links which appear in the final documentation. They are also +stripped of font, color and style information at compile-time to let the +stylesheet (utplsql.css) determine the overall look-and-feel.
+ +Unlike the code (at the time of writing), the documentation is held with CVS. So to make changes, you should set yourself up with access to the utPLSQL CVS repository. If you don't have the privileges, mail one of the Project Admins. Check out the latest version and you're ready to go.
+ +As described above, the base files in the src directory are used to generate the final +product. To add new documentation to one of these files, or to correct errors, +simply edit it. It makes sense to follow the layout that is already present in the +file, adding links at the top of the page where appropriate. The HTML should be +kept simple, so use the relevant header styles (<H1>...<H6>) to label your sections +and subsections, use <pre> to mark sections of code etc. Let the stylesheet +do the work of setting the colors and fonts.
+ +If you are adding an entirely new file to the documentation, use the file
+template.html as a basis. This contains the relevant comment
+lines which tell the scripts which parts of the file to use
+(see the note below). Finally, you will need to
+edit map.txt to ensure that the file is linked from the other pages in
+the documentation. This is described in the next section.
The files are compiled into a documentation set situated in the top-level +documentation directory using the 2 control files, map.txt and authors.txt. +The first of these is the driving file, giving a list of the files to be +included. The second gives a list of authors to be included in the copyright +notice on each page and referenced in the Meta tags.
+ +
The format of map.txt is as follows:
++ # Any line starting with a # is + # considered a comment + # + index.html,Home* + started.html,Getting Started* + another.html,Further Docs + another2.html,Yet more docs+ +
Each line consists of the filename to be included and the title of the page, +separated with a comma. Any file whose title is followed by an asterisk is +considered the start of a new section. This means a link to the file will +appear in the navigation bar at the top of each page and it will appear in +bold in the document map. Note that the document map itself does not appear +in map.txt, but is always added at the end and is considered a new section. +This page is entirely generated at compile-time.
+ +To add a new page to the documentation, simply add it to this file in the +correct position. Note that generally you will not be adding a new section!
+ +
The format of authors.txt is as follows:
++ # Again, lines starting # are ignored + # + Steven Feuerstein,steven@stevenfeuerstein.com + Chris Rimmer,c@24.org.uk + A N Other,ano@ther.net+ +
Each line in this file simply gives the name of the author and their email + address, separated with a comma. So if you've made a contribution and your name + is not listed, add it!
+ +The 2 Perl scripts used to build the documentation are clean_html.pl and + build_docs.pl.
+ +The first of these simply strips HTML files down to the basics, removing +everything from the header and removing Javascript, fonts, color etc. It +requires the HTML::TagFilter module which in turn also requires the +HTML::Parser and HTML::Tagset modules (all available from search.cpan.org).
+ +
The second script goes through each file listed in map.txt, cleans it using +the previous script and then adds logos, navigation bars, next/previous links, +copyright information etc. The resulting files are put in the top-level +documentation directory.
+ +NOTE: Anything within a source file before the
+<!-- Begin utPLSQL Body --> comment line and after the
+<!-- End utPLSQL Body --> comment line is ignored.
This package contains the following procedures and functions:
+
+
The problem with attempting to test output in PL/SQL is that there is a single +DBMS_OUTPUT buffer. When your test is run, there may already be output in the +buffer from other tests, or from the tested code. So what state should you +leave it in once you have finished? Perhaps you want all the output created by +your tested code to end up in the buffer as if it had been run normally (i.e. +not from within utPLSQL), or maybe you want only the text that was in the +buffer before you started to be left.
+ +This package attempts to allow to do any of these. There is a flag in the +package to determine whether text pulled from the output buffer should be +saved. This is set with 'save' and 'nosave' and returned by +'saving'. Data is pulled from the buffer using 'extract', +while the procedure 'replace' puts any saved data back into the output +buffer.
+ +The intent is that it is used like this:
+ ++PROCEDURE ut_my_test IS +BEGIN + + --Pull out any text already in the output buffer + utoutput.save; + utoutput.extract; + + --Your testing code here, with saving turned on or off as you see fit + + --Put text back in the output buffer + utoutput.replace; + +END; ++ +
So to start with, we save any text already in the buffer. We then carry out +our testing. If we want the output generated by the testing to end up back in +the output buffer, we turn on saving. Finally, we put the saved text back.
+ +The three routines for handling the save flag are:
++PROCEDURE save; + +PROCEDURE nosave; + +FUNCTION saving RETURN BOOLEAN; ++ +
Quite simply, 'save' turns the flag on, 'nosave' turns it off and 'saving' +returns its current value.
+ +There are 4 versions of the extract routine to get text from the output buffer:
+ ++FUNCTION extract ( + buffer_out OUT DBMS_OUTPUT.CHARARR, + max_lines_in IN INTEGER := NULL, + save_in IN BOOLEAN := saving +) RETURN INTEGER; + +PROCEDURE extract ( + buffer_out OUT DBMS_OUTPUT.CHARARR, + max_lines_in IN INTEGER := NULL, + save_in IN BOOLEAN := saving +); + +FUNCTION extract( + max_lines_in IN INTEGER := NULL, + save_in IN BOOLEAN := saving +) RETURN INTEGER; + +PROCEDURE extract( + max_lines_in IN INTEGER := NULL, + save_in IN BOOLEAN := saving +); ++ +
The function versions return the number of lines extracted from the +DBMS_OUTPUT buffer. The other parameters are used as follows: +
The replace procedure takes no parameters:
++PROCEDURE replace; ++
It simply puts the saved text back into the DBMS_OUTPUT buffer. Note that the buffer is emptied at this point.
+ +The nextLine function makes it easy to check output line-by-line as it +simply extracts and returns the next line of output:
+ ++FUNCTION nextLine( + raise_exc_in BOOLEAN := TRUE, + save_in BOOLEAN := saving +) RETURN VARCHAR2; ++ +
The raise_exc_in flag determines if the function should throw the exception utOutput.EMPTY_OUTPUT_BUFFER when asked for the next line from an empty buffer. If no exception is thrown, NULL is returned. As with extract, the save_in flag simply overrides the global save flag setting. +
+ +This function simply counts the number of lines present in the output buffer:
+ ++FUNCTION count RETURN INTEGER; ++ +
Note that the output itself is left untouched.
+ + + + From d1fb20e54cc4fe6390e32bfeef3c881099d667ee Mon Sep 17 00:00:00 2001 From: chrisrimmerFUNCTION utConfig.tester RETURN VARCHAR2;-
PROCEDURE utConfig.settester (username_in IN VARCHAR2 := USER);-
PROCEDURE utConfig.showconfig (username_in IN VARCHAR2 := NULL);@@ -134,8 +134,8 @@
You might consider putting the the call to utConfig.setdir into your login.sql so that it is run automatically, each time your start up SQL*Plus -- if you are always working from the same directory. -
FUNCTION utConfig.dir (username_in IN VARCHAR2 := NULL)
RETURN VARCHAR2;
-SQL> exec utconfig.setPrefix ('t_', 'ANALYSIS');
-FUNCTION utConfig.prefix (username_in IN VARCHAR2 := NULL)
RETURN VARCHAR2;
uPLSQL currently does not support the use of a suffix, or combination of
suffix and prefix, to identify test packages and unit test procedures.
-SQL> exec utConfig.registerTest (TRUE)Note: if you are using automatic unit test detection, any calls to utPLSQL.addtest in the setup procedure will be ignored. -
FUNCTION utGen.pkgString RETURN VARCHAR2;-
To complement the utOutput package, these +assertions allow you to easily compare collections of the type +DBMS_OUTPUT.CHARARR. Unlike the eqcoll and + eqcollapi assertions, this allows the comparison of locally defined +collections. The procedures are declared as follows:
+ ++PROCEDURE eqoutput ( + msg_in IN VARCHAR2, + check_this_in IN DBMS_OUTPUT.CHARARR, + against_this_in IN DBMS_OUTPUT.CHARARR, + ignore_case_in IN BOOLEAN := FALSE, + ignore_whitespace_in IN BOOLEAN := FALSE, + null_ok_in IN BOOLEAN := TRUE, + raise_exc_in IN BOOLEAN := FALSE +); + +PROCEDURE eqoutput ( + msg_in IN VARCHAR2, + check_this_in IN DBMS_OUTPUT.CHARARR, + against_this_in IN VARCHAR2, + line_delimiter_in IN CHAR := NULL, + ignore_case_in IN BOOLEAN := FALSE, + ignore_whitespace_in IN BOOLEAN := FALSE, + null_ok_in IN BOOLEAN := TRUE, + raise_exc_in IN BOOLEAN := FALSE +); ++ +
The first version simply compares two collections, whereas the second compares a collection against a delimited string. The delimiter +can be specified by the line_delimiter_in parameter. If NULL is passed in (which is the default) then the lines are delimited by carriage returns. +Thus to test a collection mybuff which should look like:
+ ++ mybuff(0) := 'Zidane'; + mybuff(1) := 'Ronaldo'; + mybuff(2) := 'Kahn'; ++ +
we could pass in parameters:
+ ++ check_this_in => 'Zidane|Ronaldo|Kahn'; + line_delimiter_in => '|'; ++ +
or:
+ ++ check_this_in => +'Zidane +Ronaldo +Kahn'; + line_delimiter_in => NULL; ++ +
There are also the following flags to modify the way that the line-by-line comparisons are carried out:
+ +Finally, note that only the text itself is compared. These assertions do not care about how the records within the collections are numbered.
In the current version of utPLSQL (2.0.9.1) use of this package is virtually impossible with +utPLSQL tracing turned on. The reason for this is that this facility writes output using +DBMS_OUTPUT every time an assertion is called.
+The three routines for handling the save flag are:
From 9ec09b60b5efd6400aff2e5f75828a21ae359e42 Mon Sep 17 00:00:00 2001 From: chrisrimmerSuppose, for example, that I have created a stand alone function called betwnStr (a variation on SUBSTR that returns a sub-string based on a starting -and ending location) that is stored in betwnstr.sf: +and ending location) that is stored in betwnstr.sf(1):
CREATE OR REPLACE FUNCTION betwnStr (
string_in IN VARCHAR2,
start_in IN INTEGER,
end_in IN INTEGER
)
RETURN VARCHAR2
IS
BEGIN
RETURN (
SUBSTR (
string_in,
start_in,
end_in – start_in + 1
)
);
END;@@ -226,12 +226,12 @@
So if I am going to test the stand-alone procedure, betwnstr, my test -package specification, saved in ut_betwnstr.pks, +package specification, saved in ut_betwnstr.pks(1), will look like this:
CREATE OR REPLACE PACKAGE ut_betwnstr
IS
PROCEDURE ut_setup;
PROCEDURE ut_teardown;
PROCEDURE ut_betwnstr;
END ut_betwnstr;
/-
Now let's build the package body, saved in ut_betwnstr.pkb. In this very simple +
Now let's build the package body, saved in ut_betwnstr.pkb(1). In this very simple case, I don't have to set up any data structures and I do not, therefore, have to tear anything down. My teardown procedure can be empty (but it must be present). So I have:
@@ -365,7 +365,7 @@Then run your test package within the utPLSQL testing framework by calling utPLSQL.test:
-SQL> exec utplsql.test ('betwnstr', recompile_in => FALSE)
+SQL> exec utplsql.test ('betwnstr', recompile_in => FALSE)
That second parameter in the call to utplsql.test, "recompile_in => FALSE", tells utPLSQL that you have already @@ -403,10 +403,10 @@
Call the utConfig.setdir program to tell -utPLSQL the location of your source code. Suppose that I stored all my code in e:\utplsql\testall. Then I would make this +utPLSQL the location of your source code. Suppose that I stored all my code in e:\utplsql\testall. Then I would make this call in SQL*Plus:
-SQL> exec utplsql.setdir ('e:\utplsql\testall')
+SQL> exec utplsql.setdir ('e:\utplsql\testall')
You could also put this program call in your login.sql file (see the login_sample.sql file) so that you don't have to type that code every @@ -431,6 +431,12 @@
1. This file is to be found in the Examples directory of the utPLSQL distribution.
+ From ca9bcff179206e879185a1c00541cb10b0c30c3f Mon Sep 17 00:00:00 2001 From: chrisrimmerThe format of the parameter for file access in the init.ora file is:
-utl_file_dir = <directory> +utl_file_dir = <directory>Include a parameter for utl_file_dir for each directory you want to make accessible for UTL_FILE operations. The following entries, for example, @@ -176,226 +176,6 @@
| -ut_suite - | - --table - | - --Persistent storage of test suites defined. - | -
| -ut_package - | - --table - | - --Persistent storage of packages to be run within -a suite. - | -
| -ut_test - | - --table - | - --Definition of an individual unit test. - | -
| -ut_testcase - | - --table - | - --Definition of a test case within a unit test. - | -
| ut_config | - -table | - -Holds -configuration information for each user | -
| ut_assertion | - -table | - -Contains -the definitions of the various assertion routines in the utAssert package. -This table will be used (potentially) by GUI front ends to utPLSQL. | -
| -utPLSQL - | - --package - | - --Main test package. - | -
| utConfig | - -package | - -API to ut_config table | -
| -utSuite - | - --package - | - --API to ut_suite table. - | -
| -utPackage - | - --package - | - --API to ut_ package table. - | -
| -utTest - | - --package - | - --API to ut_test table. - | -
| -utTestcase - | - --package - | - --API to ut_testcase table. - | -
| -utAssert - | - --package - | - --Collection of assertion routines available to test -the results of your programs. - | -
| -utResult - | - --package - | - --API to the results array that is populated by calls -to the utAssert assertions. - | -
| -utGen - | - --package - | - --Generates a starting point for test packages for -your own package. - | -
-- diff --git a/documentation/src/map.txt b/documentation/src/map.txt index b6159124d..6e8d2b1ca 100644 --- a/documentation/src/map.txt +++ b/documentation/src/map.txt @@ -14,7 +14,6 @@ admin.html,Administrative Topics buildpack.html,Build Test Packages* howto.html,How to build a test package testrun.html,A "Test Run" with utPLSQL -advanced.html,Advanced Topics examples.html,Examples* xref.html,Cross-reference by Assertion Type testproc.html,Test a Procedure From d20339f8ac99080eda01629a19f234e4021e990b Mon Sep 17 00:00:00 2001 From: chrisrimmer-Build Your Own Test Engine
- --Analyze Test Run Statistics
-
CREATE OR REPLACE PACKAGE <prefix><package>+
CREATE OR REPLACE PACKAGE <prefix><package>
IS-
PROCEDURE <prefix>setup;+
PROCEDURE <prefix>setup;-where <prefix> is the unit test prefix and <package> +where <prefix> is the unit test prefix and <package> is the name of the package (or stand alone program) to be tested. The default naming convention is that your test package and all utPLSQL programs, including the setup procedure, have a prefix of "ut_", as in: -
CREATE OR REPLACE PACKAGE ut_<program>+
CREATE OR REPLACE PACKAGE ut_<program>
IS@@ -68,8 +68,8 @@
Here is an example of such a procedure (see Examples\ut_te_employee.pkb -for the full implementation): +
Here is an example of such a procedure (see the file ut_te_employee.pkb +in the Examples directory of the utPLSQL distribution for the full implementation):
PROCEDURE ut_setup IS BEGIN diff --git a/documentation/src/testrun.html b/documentation/src/testrun.html index c321baace..7cca678bc 100644 --- a/documentation/src/testrun.html +++ b/documentation/src/testrun.html @@ -9,7 +9,7 @@effort, to show you how it all hangs together. I've got a "hangnail" in my PL/SQL development work, called SUBSTR. This function bothers me and I want to take care of it. What's the problem? SUBSTR is great when you -know the starting location of a string and number of characters you want.In +know the starting location of a string and number of characters you want. In many situations, though, I have the start and end locations and I need to figure out the number of characters I then want. Is it:
mystring := SUBSTR (full_string, 5, 17); -- start and end? Nah...@@ -124,8 +124,6 @@
END;-[ENDFOREACH]-END ut_str;From 6cb147d96a58b1aa0bc4fe1fa7aa8f2b3a65e250 Mon Sep 17 00:00:00 2001 From: chrisrimmerDate: Thu, 25 Jul 2002 10:21:56 +0000 Subject: [PATCH 019/143] Removed Cross-Reference Page (since it only had 2 links) and references to it --- documentation/src/examples.html | 7 +------ documentation/src/map.txt | 1 - documentation/src/xref.html | 24 ------------------------ 3 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 documentation/src/xref.html diff --git a/documentation/src/examples.html b/documentation/src/examples.html index 1cc6b6a8d..575a06ef1 100644 --- a/documentation/src/examples.html +++ b/documentation/src/examples.html @@ -8,12 +8,7 @@
We learn best by following the examples of those who have gone before us. So you will find in this document sample test packages and different approaches to using utPLSQL to test your PL/SQL code like it has never -been tested before! The Cross-reference allows you to easily find the example -that demonstrates the use of a particular kind of utAssertion assertion -routine (compare pipes or tables or files or...). -
-Cross-Reference by Assertion Type
- +been tested before!Test a Procedure
diff --git a/documentation/src/map.txt b/documentation/src/map.txt index 6e8d2b1ca..9ee6d8661 100644 --- a/documentation/src/map.txt +++ b/documentation/src/map.txt @@ -15,7 +15,6 @@ buildpack.html,Build Test Packages* howto.html,How to build a test package testrun.html,A "Test Run" with utPLSQL examples.html,Examples* -xref.html,Cross-reference by Assertion Type testproc.html,Test a Procedure testfunc.html,Test a Function testapi.html,Test an Entire Package API diff --git a/documentation/src/xref.html b/documentation/src/xref.html deleted file mode 100644 index fdd1ed643..000000000 --- a/documentation/src/xref.html +++ /dev/null @@ -1,24 +0,0 @@ - -- - - -Cross-Reference by Assertion Type
- -The examples are organized by the type of program element being tested --- and from which package. But they also demonstrate other features of -and handy techniques to use with utPLSQL. These examples, in particular, -show how to use the different kinds of assertion programs. These features -are described below. Click on a link to go to the example that highlights -the feature. -
Check equality of database tables -
Check equality of database pipes -
Check equality of files -
Check equality of collections -
Testing Impact on Objects -
Set up and Tear Down Test Data Structures - - - - From 61e3421ad8a51e173b1a3ec7534212c61971835c Mon Sep 17 00:00:00 2001 From: chrisrimmer
Date: Thu, 25 Jul 2002 10:30:58 +0000 Subject: [PATCH 020/143] Minor tweaks --- documentation/src/testproc.html | 55 +++++++++++++++++---------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/documentation/src/testproc.html b/documentation/src/testproc.html index 62c1324b2..9fb608cf6 100644 --- a/documentation/src/testproc.html +++ b/documentation/src/testproc.html @@ -43,8 +43,8 @@ After compiling my code cleanly, I generate my test package: -
SQL> SET SERVEROUTPUT ON FORMAT WRAPPED -SQL> exec utGen.testpkg ('calc_secs_between ') +SQL> SET SERVEROUTPUT ON FORMAT WRAPPED +SQL> exec utGen.testpkg ('calc_secs_between ') CREATE OR REPLACE PACKAGE ut_calc_secs_between IS PROCEDURE ut_setup; @@ -72,16 +72,16 @@PROCEDURE ut_CALC_SECS_BETWEEN IS BEGIN CALC_SECS_BETWEEN ( - DATE1 => '' + DATE1 => '' , - DATE2 => '' + DATE2 => '' , - SECS => '' + SECS => '' ); utAssert.this ( 'Test of CALC_SECS_BETWEEN', - '<boolean expression>' + '<boolean expression>' ); END ut_CALC_SECS_BETWEEN; @@ -93,7 +93,7 @@
for package spec and body, ut_calc_secs_between.pks and ut_calc_secs_between.pkb, which I do as follows: -
SQL> exec utGen.testpkg ('calc_secs_between ', output_type_in => utGen.c_file)+SQL> exec utGen.testpkg ('calc_secs_between ', output_type_in => utGen.c_file)By conforming to this standard, utPLSQL can automatically compile this code before each test. I now edit the ut_calc_secs_between @@ -104,11 +104,11 @@secs PLS_INTEGER; BEGIN CALC_SECS_BETWEEN ( - DATE1 => SYSDATE + DATE1 => SYSDATE , - DATE2 => SYSDATE + DATE2 => SYSDATE , - SECS => secs + SECS => secs ); utAssert.eq ( @@ -118,11 +118,11 @@
); CALC_SECS_BETWEEN ( - DATE1 => SYSDATE + DATE1 => SYSDATE , - DATE2 => SYSDATE+1 + DATE2 => SYSDATE+1 , - SECS => secs + SECS => secs ); utAssert.eq ( @@ -135,17 +135,17 @@
and now I can run my test: -
SQL> exec utplsql.test ('calc_secs_between') +SQL> exec utplsql.test ('calc_secs_between') . -> SSSS U U CCC CCC EEEEEEE SSSS SSSS -> S S U U C C C C E S S S S -> S U U C C C C E S S -> S U U C C E S S -> SSSS U U C C EEEE SSSS SSSS -> S U U C C E S S -> S U U C C C C E S S -> S S U U C C C C E S S S S -> SSSS UUU CCC CCC EEEEEEE SSSS SSSS +> SSSS U U CCC CCC EEEEEEE SSSS SSSS +> S S U U C C C C E S S S S +> S U U C C C C E S S +> S U U C C E S S +> SSSS U U C C EEEE SSSS SSSS +> S U U C C E S S +> S U U C C C C E S S +> S S U U C C C C E S S S S +> SSSS UUU CCC CCC EEEEEEE SSSS SSSS . SUCCESS: "calc_secs_between"@@ -174,6 +174,7 @@the value returned by the procedure. Instead, I must check to see how many rows are left in the table. Fortunately, I have another dynamic SQL utility to help me out here, one that returns the count of rows in any table: +(Note that you could also use utAssert.eqqueryvalue here.)
Suppose I have my basic sting package, containing (for now at least) just a single function:/*file tabcount.sf */ CREATE OR REPLACE FUNCTION tabcount ( @@ -197,8 +198,8 @@As you can see, my calls to str.betwn are embedded right within calls to -utAssert.eq and utAssert.isNULL, making my test code -So I will generate a package to test truncit and then modify the package body: -
SQL> SET SERVEROUTPUT ON FORMAT WRAPPED -SQL> exec utGen.testpkg ('truncit', output_type_in => utGen.c_file)+SQL> SET SERVEROUTPUT ON FORMAT WRAPPED +SQL> exec utGen.testpkg ('truncit', output_type_in => utGen.c_file)To run my test, I need to truncate a table. That is an irreversible action, so I will create a "temporary" table in @@ -226,9 +227,9 @@PROCEDURE ut_TRUNCIT IS BEGIN TRUNCIT ( - TAB => 'temp_emp' + TAB => 'temp_emp' , - SCH => USER + SCH => USER ); utAssert.eq ( From 3309c73536b0aa4d15a06d0d5eb6da104d5ee859 Mon Sep 17 00:00:00 2001 From: chrisrimmer
Date: Thu, 25 Jul 2002 10:54:11 +0000 Subject: [PATCH 021/143] Removed the (empty) composite function section --- documentation/src/testfunc.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/documentation/src/testfunc.html b/documentation/src/testfunc.html index 700be0829..d5d9d63fa 100644 --- a/documentation/src/testfunc.html +++ b/documentation/src/testfunc.html @@ -15,7 +15,7 @@
-The function returns a non-scalar value, +The function returns a non-scalar value, such as an object or a collection. In this case, you will need to call the function and then evaluate the contents of the returned structure. @@ -87,9 +87,7 @@END ut_str; /
-Testing a Composite Function
+utAssert.eq and utAssert.isNULL, making my test code compact. From 5d5fc29955f2fb229de9fa1b89f252dea9c0b495 Mon Sep 17 00:00:00 2001 From: chrisrimmerDate: Thu, 25 Jul 2002 12:16:44 +0000 Subject: [PATCH 022/143] Minor Tweaks --- documentation/src/samepack.html | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/documentation/src/samepack.html b/documentation/src/samepack.html index 0d38a57d6..e354ba8f7 100644 --- a/documentation/src/samepack.html +++ b/documentation/src/samepack.html @@ -12,13 +12,14 @@ the package specification and body. We look at two examples:
- -Testing a simple string function
- -Testing the population of a collection +Testing a simple string function +- +Testing the population of a collection
-Testing a simple string function
+ + Testing a simple string function/*file str.pks */ @@ -67,22 +68,22 @@-PL/SQL procedure successfully completed. -SQL> exec utPLSQL.test ('str', samepackage_in=>TRUE) +SQL> exec utPLSQL.test ('str', samepackage_in => TRUE) . -> SSSS U U CCC CCC EEEEEEE SSSS SSSS -> S S U U C C C C E S S S S -> S U U C C C C E S S -> S U U C C E S S -> SSSS U U C C EEEE SSSS SSSS -> S U U C C E S S -> S U U C C C C E S S -> S S U U C C C C E S S S S -> SSSS UUU CCC CCC EEEEEEE SSSS SSSS +> SSSS U U CCC CCC EEEEEEE SSSS SSSS +> S S U U C C C C E S S S S +> S U U C C C C E S S +> S U U C C E S S +> SSSS U U C C EEEE SSSS SSSS +> S U U C C E S S +> S U U C C C C E S S +> S S U U C C C C E S S S S +> SSSS UUU CCC CCC EEEEEEE SSSS SSSS . SUCCESS: "str"
-Testing the population of a collection
++ Testing the population of a collection
Collections are very useful structures, but they can be difficult to analyze and compare. utPLSQL provides the utAssert.eqColl and utAssert.eqCollAPI programs to help you do this. From 76c4cf2d52994858ab83ef29fb31fecf7c7a38ea Mon Sep 17 00:00:00 2001 From: chrisrimmerDate: Thu, 25 Jul 2002 12:18:18 +0000 Subject: [PATCH 023/143] Removed links to Example files Misc other tweaks --- documentation/src/testapi.html | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/documentation/src/testapi.html b/documentation/src/testapi.html index fa507efeb..ebea4a8d7 100644 --- a/documentation/src/testapi.html +++ b/documentation/src/testapi.html @@ -19,8 +19,8 @@ Designer, RevealNet's PL/Generator and a variety of IDE (integrated development environment) tools.
Suppose, then, that I used PL/Generator to generate a table encapsulation -package for the employee table. It would look like the code found in te_employee.pks -and te_employee.pkb (being rather +package for the employee table. It would look like the code found in te_employee.pks +and te_employee.pkb(1) (being rather lengthy, we will not reproduce it in the documentation. If you take a look, you will see that their are dozens of programs in the API, which means that you would have lots of work to do in building your unit test cases. @@ -38,7 +38,8 @@
has also been working on generator utilities for a number of years. One of these utilities, currently "code named" GenX, came in very handy for creating a test package for his PL/Generator-generated encapsulation packages. -
Using CGML (Code Generation Markup Language), Steven created a template +
Using CGML (Code Generation Markup Language), Steven created a template +(See te_utpkg.gdr in the Examples directory of the utPLSQL distribution) that reads information from the data dictionary and defines the setup, teardown and at least a good starting point for the unit test procedures. Here is the template logic for the setup procedure: @@ -70,8 +71,8 @@
in this fragment. If you are interested in pursuing these sorts of genreation opportunities and would like to check out GenX, drop a note to Steven Feuerstein. -
Here is a portion of the generated logic (found in ut_te_employee.pks -and ut_te_employee.pkb), the +
Here is a portion of the generated logic (found in ut_te_employee.pks +and ut_te_employee.pkb"(1)), the program that tests the delete operation in the encapsulation package:
PROCEDURE ut_del1 IS @@ -83,7 +84,7 @@From 43eb8d2dcd866a1da34608165db2fe38696e45df Mon Sep 17 00:00:00 2001 From: chrisrimmerDELETE FROM ut_DEL1 WHERE employee_id = -1 '; - te_employee.del (-1, rowcount_out => fdbk); + te_employee.del (-1, rowcount_out => fdbk); -- Test results utassert.eqtable ('Delete rows', 'EMPLOYEE', 'ut_DEL1'); /* Successful delete */ @@ -99,7 +100,7 @@
LOOP te_employee.del ( rec.employee_id, - rowcount_out => fdbk + rowcount_out => fdbk ); END LOOP; @@ -146,8 +147,8 @@
if not impossible to verify success or notice failure.
So I decided that the best way to run my unit tests for DML operations was to create a separate test table for each unit test. As a consequence, -my setup procedure for the te_employee -package looks like this: +my setup procedure for the te_employee package looks like this: +(See ut_te_employee.pkb in the Examples directory of the utPLSQL distribution)
PROCEDURE ut_setup IS BEGIN @@ -260,7 +261,9 @@This is a very simple test suite definition. I rely on all defaults, but @@ -37,16 +37,16 @@Again, I use dynamic SQL, but enclose each DROP TABLE statement inside its own exception section so that if for any reason the DROP fails, I continue on in an attempt to get as much done as possible. - +
+Footnotes
+1. These files are in the Examples directory of the utPLSQL distribution. From 3833a5d9d03524d0ab6b13527cbce7ca5667eb15 Mon Sep 17 00:00:00 2001 From: chrisrimmerDate: Thu, 25 Jul 2002 12:24:57 +0000 Subject: [PATCH 024/143] Minor tweaks --- documentation/src/prefix.html | 10 +++++----- documentation/src/suite.html | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/documentation/src/prefix.html b/documentation/src/prefix.html index 62fef785e..0766380f3 100644 --- a/documentation/src/prefix.html +++ b/documentation/src/prefix.html @@ -13,17 +13,17 @@ you get the picture).
Since this prefix is not hard-coded into utPLSQL, you can very easily specify your own prefix. You can do this when you run a test, as in: -
SQL> utPLSQL.test ('te_employee', prefix_in => 'test_');+SQL> utPLSQL.test ('te_employee', prefix_in => 'test_');You can also specify a prefix when you add a package to a test suite, as in: -SQL> utPackage.add ('mysuite', 'mypackage' prefix_in => 'test_');+SQL> utPackage.add ('mysuite', 'mypackage' prefix_in => 'test_');Of course, when you specify a non-default prefix, you must also build your test package using that prefix. If you plan to generate a starting point for your package with utGen, be sure to specify your prefix at that point, as in: -SQL> utGen.testpkg('mypackage' prefix_in => 'test_');-For an example of a package with a non-default prefix, check out test_te_employee.pks -and test_te_employee.pkb. +SQL> utGen.testpkg('mypackage' prefix_in => 'test_');+For an example of a package with a non-default prefix, check out test_te_employee.pks +and test_te_employee.pkb (Both to be found in the Examples directory of the utPLSQL distribution). diff --git a/documentation/src/suite.html b/documentation/src/suite.html index 3906abaab..d4f652072 100644 --- a/documentation/src/suite.html +++ b/documentation/src/suite.html @@ -18,9 +18,9 @@-- Add packages for testing utpackage.add ( - 'PLVision', 'PLVstr', dir_in => 'e:\openoracle\utplsql\examples'); + 'PLVision', 'PLVstr', dir_in => 'e:\openoracle\utplsql\examples'); utpackage.add ( - 'PLVision', 'PLVdate', dir_in => 'e:\openoracle\utplsql\examples'); + 'PLVision', 'PLVdate', dir_in => 'e:\openoracle\utplsql\examples'); END; /
utpackage.add ('PLVision', 'PLVstr', - dir_in => 'e:\openoracle\utplsql\examples', - seq_in => 1, - samepackage_in => TRUE + dir_in => 'e:\openoracle\utplsql\examples', + seq_in => 1, + samepackage_in => TRUE ); utpackage.add ('PLVision', 'PLVdate', - dir_in => 'e:\openoracle\utplsql\examples', - seq_in => 2, - samepackage_in => TRUE + dir_in => 'e:\openoracle\utplsql\examples', + seq_in => 2, + samepackage_in => TRUE ); END; /
Date: Thu, 25 Jul 2002 14:20:50 +0000 Subject: [PATCH 025/143] Added documentation on utPLSQL trace functionality --- documentation/src/utoutput.html | 2 +- documentation/src/utplsql.html | 136 ++++++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 6 deletions(-) diff --git a/documentation/src/utoutput.html b/documentation/src/utoutput.html index a85d19bb3..330e8ba90 100644 --- a/documentation/src/utoutput.html +++ b/documentation/src/utoutput.html @@ -79,7 +79,7 @@ Outline of Usage
Warning
In the current version of utPLSQL (2.0.9.1) use of this package is virtually impossible with -utPLSQL tracing turned on. The reason for this is that this facility writes output using +utPLSQL tracing turned on. The reason for this is that this facility writes output using DBMS_OUTPUT every time an assertion is called.
Saving Output
diff --git a/documentation/src/utplsql.html b/documentation/src/utplsql.html index e1a755824..79d3d9473 100644 --- a/documentation/src/utplsql.html +++ b/documentation/src/utplsql.html @@ -40,6 +40,16 @@utPLSQL Package
To run a test for a single package, use the utPLSQL.test procedure:
-PROCEDURE utPLSQL.test (
package_in IN VARCHAR2,
samepackage_in IN BOOLEAN := FALSE,
prefix_in IN VARCHAR2 := NULL,
recompile_in IN BOOLEAN := TRUE,
dir_in IN VARCHAR2 := NULL,
suite_in in VARCHAR2 := NULL,
owner_in IN VARCHAR2 := NULL,
reset_results_in IN BOOLEAN := TRUE ,
from_suite_in IN BOOLEAN := FALSE,
subprogram_in IN VARCHAR2 := '%',
per_method_setup_in IN BOOLEAN := FALSE
);+
PROCEDURE utPLSQL.test ( + package_in IN VARCHAR2, + samepackage_in IN BOOLEAN := FALSE, + prefix_in IN VARCHAR2 := NULL, + recompile_in IN BOOLEAN := TRUE, + dir_in IN VARCHAR2 := NULL, + suite_in in VARCHAR2 := NULL, + owner_in IN VARCHAR2 := NULL, + reset_results_in IN BOOLEAN := TRUE , + from_suite_in IN BOOLEAN := FALSE, + subprogram_in IN VARCHAR2 := '%', + per_method_setup_in IN BOOLEAN := FALSE +);
where the parameters are defined as follows:
If the test fails at some point, you will see output like this:
-FAILURE: "betwnstr"
BETWNSTR: IS NULL: NULL start
BETWNSTR: End larger than string length; expected "cdeg", got "cdefg"+
FAILURE: "betwnstr" +BETWNSTR: IS NULL: NULL start +BETWNSTR: End larger than string length; expected "cdeg", got "cdefg"
PROCEDURE utPLSQL.testsuite (
suite_in IN VARCHAR2,
recompile_in IN BOOLEAN := TRUE,
reset_results_in IN BOOLEAN := TRUE
);+
PROCEDURE utPLSQL.testsuite ( + suite_in IN VARCHAR2, + recompile_in IN BOOLEAN := TRUE, + reset_results_in IN BOOLEAN := TRUE + per_method_setup_in in BOOLEAN := FALSE + );
where suite_in is the name of the suite and recompiled_in determines the auto compilation behavior. @@ -278,7 +307,18 @@
Use the utPLSQL.addtest procedure to register a unit test.
-PROCEDURE utPLSQL.addtest (
NAME_IN IN VARCHAR2,
utprefix_in IN VARCHAR2,
iterations_in IN PLS_INTEGER := 1
);
PROCEDURE utPLSQL.addtest (
package_in IN VARCHAR2,
NAME_IN IN VARCHAR2,
utprefix_in IN VARCHAR2,
iterations_in IN PLS_INTEGER := 1
);+
PROCEDURE utPLSQL.addtest ( + NAME_IN IN VARCHAR2, + utprefix_in IN VARCHAR2, + iterations_in IN PLS_INTEGER := 1 + ); + + PROCEDURE utPLSQL.addtest ( + package_in IN VARCHAR2, + NAME_IN IN VARCHAR2, + utprefix_in IN VARCHAR2, + iterations_in IN PLS_INTEGER := 1 + );
where
@@ -303,7 +343,22 @@Here is a setup procedure that sets up a series of tests for a query-only encapsulation of the employee table:
-CREATE OR REPLACE PACKAGE BODY ut_te_employee
IS
PROCEDURE ut_setup
IS
BEGIN
utplsql.addtest ('UT_EMP_DEPT_LOOKUPROWCOUNT'); utplsql.addtest ('UT_EMP_JOB_LOOKUPROWCOUNT'); utplsql.addtest ('UT_EMP_MGR_LOOKUPROWCOUNT'); utplsql.addtest ('UT_HIRE_DATE$VAL'); utplsql.addtest ('UT_I_EMPLOYEE_NAME$ROW'); utplsql.addtest ('UT_I_EMPLOYEE_NAME$VAL'); utplsql.addtest ('UT_ONEROW'); utplsql.addtest ('UT_PKYROWCOUNT'); utplsql.addtest ('UT_ROWCOUNT'); utplsql.addtest ('UT_SALARY$VAL');END;+
CREATE OR REPLACE PACKAGE BODY ut_te_employee
+IS
+ PROCEDURE ut_setup
+ IS
+ BEGIN
+ utplsql.addtest ('UT_EMP_DEPT_LOOKUPROWCOUNT');
+ utplsql.addtest ('UT_EMP_JOB_LOOKUPROWCOUNT');
+ utplsql.addtest ('UT_EMP_MGR_LOOKUPROWCOUNT');
+ utplsql.addtest ('UT_HIRE_DATE$VAL');
+ utplsql.addtest ('UT_I_EMPLOYEE_NAME$ROW');
+ utplsql.addtest ('UT_I_EMPLOYEE_NAME$VAL');
+ utplsql.addtest ('UT_ONEROW');
+ utplsql.addtest ('UT_PKYROWCOUNT');
+ utplsql.addtest ('UT_ROWCOUNT');
+ utplsql.addtest ('UT_SALARY$VAL');
+ END;
Once you have placed your addtest programs into your test package's setup procedure, you are ready to build your own unit tests.
@@ -315,6 +370,77 @@FUNCTION utPLSQL.version RETURN VARCHAR2+
These routines are very simple and take no arguments:
+ ++PROCEDURE trc; + +PROCEDURE notrc; + +FUNCTION tracing RETURN BOOLEAN; ++ +
The procedures trc and notrc are used to turn tracing on and off +respectively. The function tracing returns TRUE if tracing is currently turned +on and FALSE otherwise. This facility is useful when writing code in utPLSQL +(the framework itself, not your test code). An example of the output generated +is:
+ ++Initialized utPLSQL session... +Setpkg to Lottery +Package and program = ut_Lottery +Same package? N +Is package? Y +Prefix = ut_ +Recompiling ut_Lottery in +Runprog of ut_SETUP +Package and program = ut_Lottery.ut_SETUP +Same package? N +Is package? Y +Prefix = ut_ +Addtest +Package and program = Lottery.UT_DRAW +Same package? N +Override? Y +Prefix = ut_ +Runprog of UT_DRAW +Package and program = ut_Lottery.UT_DRAW +Same package? N +Is package? Y +Prefix = ut_ +. +> FFFFFFF AA III L U U RRRRR EEEEEEE +> F A A I L U U R R E +> F A A I L U U R R E +> F A A I L U U R R E +> FFFF A A I L U U RRRRRR EEEE +> F AAAAAAAA I L U U R R E +> F A A I L U U R R E +> F A A I L U U R R E +> F A A III LLLLLLL UUU R R EEEEEEE +. +FAILURE: "Lottery" +. +> Individual Test Case Results: +> +FAILURE - EQ "Test of DRAW" Expected "01 02 05 27 43 49" and got "02 04 27 28 31 33" +> +> +> Errors recorded in utPLSQL Error Log: +> +> NONE FOUND +Runprog of ut_TEARDOWN +Package and program = ut_Lottery.ut_TEARDOWN +Same package? N +Is package? Y +Prefix = ut_ + +PL/SQL procedure successfully completed. ++ From b994627c112f30d2dcb947047855572c14dd696c Mon Sep 17 00:00:00 2001 From: chrisrimmer
To make it as easy as possible for you to run your tests, utPLSQL stores @@ -149,9 +174,9 @@
SQL> exec utconfig.setdir ('e:\demo\utplsql');
+SQL> exec utconfig.setdir ('e:\demo\utplsql');
or, with the specification of a non-current schema:
-SQL> exec utconfig.setdir ('e:\demo\utplsql', 'ANALYSIS');
+SQL> exec utconfig.setdir ('e:\demo\utplsql', 'ANALYSIS');
Note that this directory must be accessible through UTL_FILE.
You might consider putting the the call to utConfig.setdir into your login.sql so that it is run automatically, each time your start up SQL*Plus @@ -175,9 +200,9 @@
SQL> exec utconfig.setPrefix ('tst#');
+SQL> exec utconfig.setPrefix ('tst#');
or, with the specification of a non-current schema:
-SQL> exec utconfig.setPrefix ('t_', 'ANALYSIS');
+SQL> exec utconfig.setPrefix ('t_', 'ANALYSIS');
SQL> exec utConfig.registerTest (TRUE)+
SQL> exec utConfig.registerTest (TRUE)Note: if you are using automatic unit test detection, any calls to utPLSQL.addtest in the setup procedure will be ignored. + +
You can return the current registration mode using the following function: +
FUNCTION registeringtest (username_in IN VARCHAR2 := NULL) + RETURN BOOLEAN;+This returns TRUE if the registration mode has been set to manual and FALSE otherwise. + +
utsuite.addpkg (-
'PLVision', 'PLVstr', dir_in => 'e:\utplsql');+
'PLVision', 'PLVstr', dir_in => 'e:\utplsql');
utsuite.addpkg (-
'PLVision', 'PLVdate', dir_in => 'e:\utplsql');+
'PLVision', 'PLVdate', dir_in => 'e:\utplsql');
@@ -290,7 +322,7 @@
utplsql.testsuite (-
'PLVision', recompile_in => FALSE);+
'PLVision', recompile_in => FALSE);
END;@@ -315,7 +347,7 @@
SQL> exec utconfig.autocompile (FALSE, 'SCOTT')+
SQL> exec utconfig.autocompile (FALSE, 'SCOTT')This program updates the ut_config table with your information and then commits the setting. @@ -331,6 +363,17 @@
You can set the delimiter to be used in V2 procedure names using the following procedure: +
PROCEDURE setdelimiter ( + delimiter_in IN VARCHAR2, + username_in IN VARCHAR2 := NULL +);+while the current delimiter can be obtained by the function: +
FUNCTION delimiter (username_in IN VARCHAR2 := NULL) + RETURN VARCHAR2;+ From d44293ef8f30f8032706966576552757aabecbd6 Mon Sep 17 00:00:00 2001 From: chrisrimmer
The following procedures turn on or off the display of success messages. In other words, +when turned on (as is the default) a message will be displayed for each successful assertion. +The specifications are as follows: +
+procedure include_successes; +procedure ignore_successes;+ From ed51d6a97f499949c5bc5bd0ff8b62cc4d30ce01 Mon Sep 17 00:00:00 2001 From: chrisrimmer
| utGen.testpkg | +utGen.testpkg (basic version) | Generate skeleton test packages | ||||||
|
+ utGen.testpkg (grid version) + utGen.testpkg_from_file + utGen.testpkg_from_string + |
+
+ Generate skeleton test packages with test cases | +|||||||
| utGen.pkgstring | @@ -29,8 +40,8 @@
| utRecEq.add | +Add a record type comparison function | +
| utRecEq.compile | +Compile a package's record type comparison functions | +
| utRecEq.rem | +Remove record type comparison functions | +
This package (created by Dan Spencer) allows the creation of functions to +allow the comparison of record types based on tables or views (%ROWTYPE +records in other words). They are generated by the add procedure: + +
PROCEDURE add( + pkg_name_in IN ut_package.name%TYPE, + record_in IN ut_receq.name%TYPE, + rec_owner_in IN ut_receq.created_by%TYPE := USER +);+ +The pkg_name_in parameter contains the name of a tested package you wish to +associate with this record type. Note that this package name should already +exist in the ut_package table. The record_in parameter contains the name of +the view or table whose record type is to be compared. The final (optional) +parameter contains the name of the schema in which the table or view exists. +It defaults to the current user. + +
The generated function will be named EQ_{Record_Schema_}Record_Name. The +schema is only inserted when the record type is not within the current one. The +function will return TRUE if the two records are identical on a field-by-field +comparison and FALSE otherwise. Note that NULL fields are considered +equal.
+ +The details of the EQ_* functions and their association with tested packages held in two tables: +
This routine recompiles all the EQ_* functions associated with a given package: + +
PROCEDURE compile(pkg_name_in IN ut_package.name%TYPE);+ +when autocompiling is turned on, this is called by +utplsql.test or utplsql.testsuite before the test packages themselves are recompiled. + +
To remove record comparison functions, use the following: + +
PROCEDURE rem( + name_in IN ut_receq.name%TYPE, + rec_owner_in IN ut_receq.created_by%TYPE := USER + for_package_in IN BOOLEAN := FALSE +);+ +If for_package_in is FALSE, then name_in is taken to refer to a record type to +remove, with rec_owner_in specifying the schema the record type is in. All package associations for this record type are removed and the EQ_* function is dropped. + +
On the other hand, if for_package_in is TRUE, then name_in is taken to refer +to a package. In this case, all the package's associations are removed. If no +other package is associated with a given record, then the EQ_* function is +dropped. (Note that the rec_owner_in parameter is ignored here).
+ + + + From 2394b97a11939e28c897bd4daf9080cb70811696 Mon Sep 17 00:00:00 2001 From: chrisrimmerFinally, note that only the text itself is compared. These assertions do not care about how the records within the collections are numbered.
+Finally, note that only the text itself is compared. These assertions do +not care about how the records within the collections are numbered.
+ +The following assertions (created by Raji) check that a named database +object exists or does not exist: + +
PROCEDURE objExists ( + msg_in IN VARCHAR2, + check_this_in IN VARCHAR2, + null_ok_in IN BOOLEAN := FALSE, + raise_exc_in IN BOOLEAN := FALSE +); + +PROCEDURE objnotExists ( + msg_in IN VARCHAR2, + check_this_in IN VARCHAR2, + null_ok_in IN BOOLEAN := FALSE, + raise_exc_in IN BOOLEAN := FALSE +);+ +In both cases, the check_this_in parameter gives the name of the object to +check for. So passing 'MYTHING' will check if the MYTHING object exists. This +is assumed to be in the current schema. To check for objects in a schema other +than the current one, simply add the name of the schema, separated by a dot. +So passing 'ANOTHER.THATTHING' will check for the existence of the THATTHING +object in the ANOTHER schema. +
-- Registers the results in the utResult databank.
utresult.report (msg_in);
ELSE
utplsql.pl (msg_in);
END IF;
IF showing_results AND register_in
THEN
-- Show the results of the test more recently run.The most important statement to include in your assertion routine is the -call to utResult.report, which will log the results of the test. + call to utResult.report, which will log the results of the test. + + From 0337aea12bda93a85c57af3e823e34bb680006dd Mon Sep 17 00:00:00 2001 From: chrisrimmer
utresult.showlast;
END IF;
IF raise_exc_in
THEN
RAISE test_failure;
END IF;
END IF;
END;
+ PROCEDURE testpkg_from_table ( + package_in IN VARCHAR2, + program_in IN VARCHAR2 := '%', + samepackage_in IN BOOLEAN := FALSE, + prefix_in IN VARCHAR2 := NULL, + schema_in IN VARCHAR2 := NULL, + output_type_in IN PLS_INTEGER := c_screen, + dir_in IN VARCHAR2 := NULL, + date_format_in IN VARCHAR2 + := 'MM/DD/YYYY' + ); ++This procedure is based on the testpkg_from_string procedure. +You can fill this table with test cases using the Windows frontend utgen.exe provided in the utplsql framework.
+
+
+
+
+
diff --git a/documentation/utgen_function.jpg b/documentation/utgen_function.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..499bf3c28d7c0f481603483e60725cf824eb3bb8
GIT binary patch
literal 104138
zcmdqIcT`l%wl~^H&N&MxL6Im(6r>d-iwG!47LhDTKr*c$AUR1^P@+hVO_C-fNpfm(
z21(t-20FZjd!KXnK4+Z!?)S#`#~rU73&$F(s^*-(8EV$7D%>P)0YsyrqOJnM!@~nT
z1O9<<(;y`fAwB^C0X`w{hmeqvh?s(e82C|>lao@=Qq$4VQq$7XGhJq;XJBKbrDeIw
z!p6bD#l=O>%+1Tq$$OcTi}PX-JfJHv5iu1B2^A*;Ed%HO@ekJwq9wsQ#J_@%#|^qf
zi-%8(hieCMfk1eK0Jj$m|M|nagiio)NkU3S4m7By0bRnw$G-&d4+sFX_6NR$2xtlE
zu8Q3wqSvt?=5}Qee;fCOgh#Qwg;941!F%iZt3XmRCT12^Hoj}u`ELkFNJ>e|$lg}E
zudJe~rmms)_=Q$r)m7gp9bws!Vz?jEl_y}W&b-UWw*z7GqJPxz3Sl$?^9mX)28
zo0tE!;9EsyRdo%twywUlt-YhO>qmFb@W|-c_{8KCd~s=cWp!D@UtJsj52*@@6+^yCsTQ!L});^_4dq+n(ul^+tW%8P6jpCq@Ja
zy`L!NLk1|fAkUo782_a8!Ot%p8*J=+eh$QO*21}~!x9$DneP?owCFWr)Q1{xp6HWv
zL~2UtsZvmtt_L4H-L<~%_GwD?M+y1zPH=Q8(%HCt NG*qB>Om3Hx_EVT9K7vpblz~DH@X)
zbnb~3oCg7>6| 3g$e4H5GzpITdFGnUw((L8wp_YLLaXFY^Rz3i?=uGKI`h+$$E`Y1
z58M_&gqRD?pEZ2y5J2-*D|GToJO}5by1cBS@>Zo--3-qYmnOAm`ApeA7YZ
zIiE2D)_akejd|0Jbcp`hzHQ5+0ZVe!g3S0!1AbKhMZ|qQjXnEKAU@n@2$LF&HSfTI
zQv98ukRL~J$atUwV31$`P)i2@(fItyC{%5kiC}!iviK^bM^l5OVWKFTZ6+6Yt
zG#QZ>VH|g@@6g~k*v%B(C42k{I3Akl;P8HD_i>xV*MZBgD2smOD$!8$Ug}Mlb~$25
z>;?+?eBl$=j#JoSmcW+nGX@>ytHBI9cM1P*JpY!2%uW1DqVj~J{rHaJIaYoZ2ZEYo
z=*GG=0fro!&5sU%eW;vQ6UM-QoUee$R*DU?U&+OR;;R=#&rX5E`opqzT}$+nN1r^<
z->I-#ShHz6D$KdmqP83~PoZIgYtAfd)nW^tGv_}SWtIDj*%SPNPNHUqtw>RcyP#K-
z-G;ww^$K#7tup7=+C^p5QR=vLX^wyOMXeae@4Dz~4oj!nVl|0hRY^A+*Pfr;iy&&9
z`N4eU%lVh`nAuX6E@8~^fGlummUioyQ(#zLcCnxQ!RYQ0IeYJfFGgqk&$7Uk6kuI$
z)(px|TR(JuA0U8aD)-1mIky@KRoWf5R1=L#PC1TUXHHIvj(b@h`y)`3mE<*vMn!!1
zD-f~P@*ZV>F
5V4>$WIC36hbViVPB!
zoC`#f2uRML0+K;;k#mqJARr*11j!&dQ{
(x&3)?$$(XowE#fw|@{&@zCB5`R?sc0Lf8J
z(VXNq(<^1GrB}O$FGSeI8SoI3L;j&lrX-HWZBa8d>BpEngo|
Z14``)0D6E
@=+K)(+(sdE^|p^Y2Rb3vq5)hD3rJ$w^_Zmalw&FYKwxH$A+CES)G9SeOn6&
z#5&KfDuU8WkP&sD6?5P9=>VS?vr$c$lI%NUqiw>;`9_?1e8bzVo&*uWg~Jzsvp{G8
zeiQctAuQ{+Z>1O~wH?h$(i5C-t=GP5GcFKgVy2xCyjgG1j=Rg>?{{