Changeset 10 for trunk/epic

Show
Ignore:
Timestamp:
08/18/08 17:21:15 (4 years ago)
Author:
fumanchu
Message:

Changed assert_values to assert_column. Changed record_asserter(varname1 text, varname2 text, colnames text) to assert_rows(source text, expected text). Added pass, fail, todo, skip and finish functions.

Location:
trunk/epic
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/epic/epic.sql

    r9 r10  
    4848     
    4949    1. ALWAYS RAISE EXCEPTION at the end of test procs to rollback! Even if 
    50         the test passes, RAISE EXCEPTION '[OK]'. 
     50        the test passes, RAISE EXCEPTION '[OK]'. You may instead PERFORM the 
     51        Epic functions test.pass(), test.fail(errmsg), test.todo(msg) and 
     52        test.skip(msg). 
    5153    2. Put your test in the "test" schema. 
    5254    3. Start the name of your test with "test_". 
     
    8082 
    8183  -- ALWAYS RAISE EXCEPTION at the end of test procs to rollback! 
    82   RAISE EXCEPTION '[OK]'; 
    83 END; 
    84 $$ LANGUAGE plpgsql; 
    85  
    86  
    87 Test helper functions 
    88 --------------------- 
     84  PERFORM test.pass(); 
     85END; 
     86$$ LANGUAGE plpgsql; 
     87 
     88 
     89Assertion functions 
     90------------------- 
    8991 
    9092Epic.sql includes some functions to make tests easier to write (and shorter). 
     
    102104    * test.assert_less_than(elem_1 anyelement, elem_2 anyelement) 
    103105    * test.assert_less_than_or_equal(elem_1 anyelement, elem_2 anyelement) 
    104     * test.assert_values(source text, expected anyarray, colname text): 
    105         Raises an exception if SELECT column FROM source != expected. 
     106    * test.assert_rows(row_1 text, row_2 text): 
     107        Raises an exception if the SELECT statement row_1 != the SELECT statement row_2. 
     108    * test.assert_column(call text, expected anyarray[, colname text]): 
     109        Raises an exception if SELECT colname FROM call != expected. 
    106110     
    107     * test.assert_raises(call text, errm text, state text): Raises an 
    108         exception if 'SELECT * FROM [call];' does not raise errm 
     111    * test.assert_raises(call text, errm text, state text):  
     112        Raises an exception if 'SELECT * FROM [call];' does not raise errm 
    109113        (if provided) or state (if provided). 
    110  
    111 Some return dynamic SQL: 
    112      
    113     * test.record_asserter(varname1 text, varname2 text, colnames text): 
    114         Returns EXECUTE-able SQL to assert equal fields for the two records. 
    115         LOOP over its results and PERFORM each one. 
    116114 
    117115 
     
    217215  modulename      text; 
    218216  output_record   test.results%ROWTYPE; 
     217  splitpoint      int; 
    219218BEGIN 
    220219  SELECT module INTO modulename FROM test.testnames WHERE name = testname; 
     
    224223    EXECUTE 'SELECT * FROM test.' || testname || '();'; 
    225224  EXCEPTION WHEN OTHERS THEN 
    226     IF SQLERRM LIKE '[%]' THEN 
    227       INSERT INTO test.results (name, module, result) 
    228         VALUES (testname, modulename, SQLERRM) 
     225    IF SQLSTATE = 'P0001' AND SQLERRM LIKE '[%]%' THEN 
     226      splitpoint := position(']' in SQLERRM); 
     227      INSERT INTO test.results (name, module, result, errcode, errmsg) 
     228        VALUES (testname, modulename, substr(SQLERRM, 1, splitpoint), 
     229                CASE WHEN SQLERRM LIKE '[FAIL]%' THEN SQLSTATE ELSE '' END, 
     230                btrim(substr(SQLERRM, splitpoint + 1))) 
    229231        RETURNING * INTO output_record; 
    230232      RETURN output_record; 
     
    272274    END LOOP; 
    273275  END LOOP; 
     276END; 
     277$$ LANGUAGE plpgsql; 
     278 
     279 
     280CREATE OR REPLACE FUNCTION test.finish(result text, errmsg text) RETURNS VOID AS $$ 
     281-- Use this to finish a test. Raises the given result as an exception (for rollback). 
     282DECLARE 
     283  msg        text; 
     284BEGIN 
     285  msg := '[' || result || ']'; 
     286  IF errmsg IS NOT NULL THEN 
     287    msg := msg || ' ' || errmsg; 
     288  END IF; 
     289  RAISE EXCEPTION '%', msg; 
     290END; 
     291$$ LANGUAGE plpgsql; 
     292 
     293 
     294CREATE OR REPLACE FUNCTION test.pass(msg text) RETURNS VOID AS $$ 
     295-- Use this to finish a successful test. Raises exception '[OK] msg'. 
     296BEGIN 
     297  PERFORM test.finish('OK', msg); 
     298END; 
     299$$ LANGUAGE plpgsql; 
     300CREATE OR REPLACE FUNCTION test.pass() RETURNS VOID AS $$ 
     301-- Use this to finish a successful test. Raises exception '[OK]'. 
     302BEGIN 
     303  PERFORM test.finish('OK', NULL); 
     304END; 
     305$$ LANGUAGE plpgsql; 
     306 
     307 
     308CREATE OR REPLACE FUNCTION test.fail(msg text) RETURNS VOID AS $$ 
     309-- Use this to finish a failed test. Raises exception '[FAIL] msg'. 
     310BEGIN 
     311  PERFORM test.finish('FAIL', msg); 
     312END; 
     313$$ LANGUAGE plpgsql; 
     314CREATE OR REPLACE FUNCTION test.fail() RETURNS VOID AS $$ 
     315-- Use this to finish a failed test. Raises exception '[FAIL]'. 
     316BEGIN 
     317  PERFORM test.finish('FAIL', NULL); 
     318END; 
     319$$ LANGUAGE plpgsql; 
     320 
     321 
     322CREATE OR REPLACE FUNCTION test.todo(msg text) RETURNS VOID AS $$ 
     323-- Use this to abort a test as 'todo'. Raises exception '[TODO] msg'. 
     324BEGIN 
     325  PERFORM test.finish('TODO', msg); 
     326END; 
     327$$ LANGUAGE plpgsql; 
     328CREATE OR REPLACE FUNCTION test.todo() RETURNS VOID AS $$ 
     329-- Use this to abort a test as 'todo'. Raises exception '[TODO]'. 
     330BEGIN 
     331  PERFORM test.finish('TODO', NULL); 
     332END; 
     333$$ LANGUAGE plpgsql; 
     334 
     335 
     336CREATE OR REPLACE FUNCTION test.skip(msg text) RETURNS VOID AS $$ 
     337-- Use this to skip a test. Raises exception '[SKIP] msg'. 
     338BEGIN 
     339  PERFORM test.finish('SKIP', msg); 
     340END; 
     341$$ LANGUAGE plpgsql; 
     342CREATE OR REPLACE FUNCTION test.skip() RETURNS VOID AS $$ 
     343-- Use this to skip a test. Raises exception '[SKIP]'. 
     344BEGIN 
     345  PERFORM test.finish('SKIP', NULL); 
    274346END; 
    275347$$ LANGUAGE plpgsql; 
     
    444516 
    445517 
    446  
    447 CREATE OR REPLACE FUNCTION test.record_asserter(varname1 text, varname2 text, colnames text) RETURNS SETOF text AS $$ 
    448 -- Returns EXECUTE-able SQL to assert equal fields for the two records. 
     518CREATE OR REPLACE FUNCTION test.assert_rows(source text, expected text) RETURNS VOID AS $$ 
     519-- Asserts that two sets of rows have equal values. 
    449520-- 
    450 -- Pass the variable *names* (not the records themselves) as the first 
    451 -- two arguments, and a comma-delimited list of column names to compare. 
     521-- Both arguments should be SELECT statements yielding a single row or a set of rows. 
     522-- Neither source nor expected need to be sorted. Either may include a trailing semicolon. 
    452523-- 
    453524-- Example: 
    454525--  
    455 --    SELECT INTO old * FROM table WHERE id = 1; 
    456 --    SELECT INTO new * FROM table WHERE id = 2; 
    457 --    FOR assertion in 
    458 --      SELECT * FROM test.record_asserter('old', 'new', 'first, last, city') 
    459 --    LOOP 
    460 --      PERFORM assertion; 
    461 --    END LOOP; 
     526--    PERFORM test.assert_row('SELECT first, last, city FROM table1', 
     527--                            'SELECT ROW(''Davy'', ''Crockett'', NULL)'); 
     528DECLARE 
     529  rec     record; 
     530BEGIN 
     531  FOR rec in EXECUTE rtrim(source, ';') || ' EXCEPT ' || rtrim(expected, ';') 
     532  LOOP 
     533    RAISE EXCEPTION 'Record: % from: % not found in: %', rec, source, expected; 
     534  END LOOP; 
     535   
     536  FOR rec in EXECUTE rtrim(expected, ';') || ' EXCEPT ' || rtrim(source, ';') 
     537  LOOP 
     538    RAISE EXCEPTION 'Record: % from: % not found in: %', rec, expected, source; 
     539  END LOOP; 
     540END; 
     541$$ LANGUAGE plpgsql IMMUTABLE; 
     542 
     543 
     544CREATE OR REPLACE FUNCTION test.assert_column(call text, expected anyarray, colname text) RETURNS VOID AS $$ 
     545-- Raises an exception if SELECT colname FROM call != expected. 
    462546-- 
    463 DECLARE 
    464   i             integer:=1; 
    465   colname       text; 
    466   colnames_arr  text[]; 
    467 BEGIN 
    468   --TODO: IF colnames IS NULL grab colnames from type 
    469    
    470   colnames_arr := string_to_array(colnames, ','); 
    471   FOR i IN array_lower(colnames_arr, 1)..array_upper(colnames_arr, 1) 
    472   LOOP 
    473     colname := quote_ident(trim(both ' ' from colnames_arr[i])); 
    474     RETURN NEXT 'PERFORM test.assert_equal(' || 
    475                  quote_ident(varname1) || '.' || colname || ', ' || 
    476                  quote_ident(varname2) || '.' || colname || ');'; 
    477   END LOOP; 
    478   RETURN; 
    479 END; 
    480 $$ LANGUAGE plpgsql IMMUTABLE; 
    481  
    482  
    483 CREATE OR REPLACE FUNCTION test.assert_values(source text, expected anyarray, colname text) RETURNS VOID AS $$ 
    484 -- Raises an exception if SELECT column FROM source != expected. 
     547-- If colname is NULL or omitted, the first column of call's output will be used. 
    485548-- 
    486 -- colname should be the name of the column in source to compare. 
    487 -- If NULL, it will be taken from the first column of source's output. 
    488 -- 
    489 -- source can be any table, view, or procedure that returns records. 
    490 --  
    491 -- expected MUST be an array of the same type as colname. 
    492 -- Neither source nor expected need to be sorted. 
     549-- 'call' can be any table, view, or procedure that returns records. 
     550--  
     551-- 'expected' MUST be an array of the same type as colname. 
     552-- Neither call nor expected need to be sorted. 
    493553--  
    494554-- Example: 
    495 --    PERFORM test.assert_values( 
     555--    PERFORM test.assert_column( 
    496556--      'get_favorite_user_ids(' || user_id || ');', 
    497557--      ARRAY[24, 10074, 87321], 'user_id'); 
     
    502562  firstname     text; 
    503563BEGIN 
    504   -- Dump the source into a temp table 
     564  -- Dump the call output into a temp table 
    505565  IF colname IS NULL THEN 
    506566    EXECUTE 'CREATE TEMPORARY TABLE _test_assert_values_base AS ' || 
    507       'SELECT * FROM ' || source || ';'; 
     567      'SELECT * FROM ' || call || ';'; 
    508568    SELECT INTO firstname a.attname 
    509569      FROM pg_class c LEFT JOIN pg_attribute a ON c.oid = a.attrelid 
     
    513573      AND a.attnum >= 1 
    514574      ORDER BY a.attnum; 
    515     EXECUTE 'ALTER TABLE _test_assert_values_base RENAME ' || firstname || ' TO result;'; 
     575    EXECUTE 'ALTER TABLE _test_assert_values_base RENAME ' || firstname || ' TO _assert_values_result;'; 
    516576  ELSE 
    517577    EXECUTE 'CREATE TEMPORARY TABLE _test_assert_values_base AS ' || 
    518       'SELECT ' || colname || ' AS result FROM ' || source || ';'; 
     578      'SELECT ' || colname || ' AS _assert_values_result FROM ' || call || ';'; 
    519579  END IF; 
    520580   
     
    525585  FOR i IN array_lower(expected, 1)..array_upper(expected, 1) 
    526586  LOOP 
    527     EXECUTE 'INSERT INTO _test_assert_values_expected (result) VALUES (' || quote_literal(expected[i]) || ');'; 
     587    IF expected[i] IS NULL THEN 
     588      EXECUTE 'INSERT INTO _test_assert_values_expected (_assert_values_result) VALUES (NULL);'; 
     589    ELSE 
     590      EXECUTE 'INSERT INTO _test_assert_values_expected (_assert_values_result) VALUES (' 
     591              || quote_literal(expected[i]) || ');'; 
     592    END IF; 
    528593  END LOOP; 
    529594   
     
    532597  BEGIN 
    533598    FOR record IN EXECUTE '(SELECT * FROM _test_assert_values_base EXCEPT ALL 
    534                      SELECT * FROM _test_assert_values_expected)' 
     599                            SELECT * FROM _test_assert_values_expected)' 
    535600    LOOP 
    536       RAISE EXCEPTION 'result: % not in array: %', record.result, expected; 
     601      RAISE EXCEPTION 'result: % not in array: %', record._assert_values_result, expected; 
    537602    END LOOP; 
    538603     
    539604    FOR record IN EXECUTE '(SELECT * FROM _test_assert_values_expected EXCEPT ALL 
    540                      SELECT * FROM _test_assert_values_base)' 
     605                            SELECT * FROM _test_assert_values_base)' 
    541606    LOOP 
    542       RAISE EXCEPTION 'element: % not in source: %', record.result, source; 
     607      RAISE EXCEPTION 'element: % not in call: %', record._assert_values_result, call; 
    543608    END LOOP; 
    544609  EXCEPTION WHEN OTHERS THEN 
     
    553618$$ LANGUAGE plpgsql; 
    554619 
    555 CREATE OR REPLACE FUNCTION test.assert_values(source text, expected anyarray) RETURNS VOID AS $$ 
    556 -- Implicit column version of assert_values 
    557 BEGIN 
    558   PERFORM test.assert_values(source, expected, NULL); 
    559 END; 
    560 $$ LANGUAGE plpgsql; 
     620CREATE OR REPLACE FUNCTION test.assert_column(call text, expected anyarray) RETURNS VOID AS $$ 
     621-- Implicit column version of assert_column 
     622BEGIN 
     623  PERFORM test.assert_column(call, expected, NULL); 
     624END; 
     625$$ LANGUAGE plpgsql; 
  • trunk/epic/test/test_asserts.sql

    r9 r10  
    300300 
    301301 
    302 CREATE OR REPLACE FUNCTION test.test_record_asserter() RETURNS VOID AS $$ 
    303 -- Assert the correct operation of test.record_asserter 
    304 -- module: test_asserts 
    305 DECLARE 
    306   assertion    text; 
    307   assertions   text[]; 
    308   old          record; 
    309   new          record; 
    310 BEGIN 
    311   FOR assertion in 
    312     SELECT * FROM test.record_asserter('old', 'new', 'first, last, city') 
    313   LOOP 
    314     assertions := assertions || assertion; 
    315   END LOOP; 
    316    
    317   IF assertions <> ARRAY['PERFORM test.assert_equal("old"."first", "new"."first");', 
    318                          'PERFORM test.assert_equal("old"."last", "new"."last");', 
    319                          'PERFORM test.assert_equal("old".city, "new".city);'] THEN 
    320     RAISE EXCEPTION 'record_asserter did not return the proper SQL. %', assertions; 
    321   END IF; 
    322    
    323   -- Now just for fun, execute the returned SQL. 
    324   CREATE TEMPORARY TABLE _test_user (first text, last text, city text); 
    325   INSERT INTO _test_user VALUES ('Michael', 'Stonebraker', 'New York'); 
    326   SELECT INTO old * FROM _test_user WHERE city = 'New York'; 
    327   SELECT INTO new * FROM _test_user WHERE city = 'New York'; 
    328   FOR i IN array_lower(assertions, 1)..array_upper(assertions, 1) 
    329   LOOP 
    330     PERFORM assertions[i]; 
    331   END LOOP; 
    332    
    333   RAISE EXCEPTION '[OK]'; 
    334 END; 
    335 $$ LANGUAGE plpgsql; 
    336  
    337  
    338 CREATE OR REPLACE FUNCTION test.test_assert_values() RETURNS VOID AS $$ 
    339 -- Assert the correct operation of test.assert_values 
     302CREATE OR REPLACE FUNCTION test.test_assert_rows() RETURNS VOID AS $$ 
     303-- Assert the correct operation of test.assert_rows 
    340304-- module: test_asserts 
    341305DECLARE 
    342306  failed     bool; 
    343307BEGIN 
    344   PERFORM test.assert_values( 
     308  -- Tautology 
     309  PERFORM test.assert_rows( 
     310    'SELECT oid, proname FROM pg_proc', 
     311    'SELECT oid, proname FROM pg_proc'); 
     312  -- Almost a tautology ;) 
     313  -- Note the trailing semicolon in the first arg. 
     314  PERFORM test.assert_rows( 
     315    'SELECT tablename FROM pg_tables;', 
     316    'SELECT relname FROM pg_class where relkind = ''r'''); 
     317   
     318  -- ...and an assertion that should fail 
     319  failed := false; 
     320  BEGIN 
     321    PERFORM test.assert_rows( 
     322      'SELECT * FROM generate_series(1, 10)',  
     323      'SELECT * FROM generate_series(1, 5)'); 
     324  EXCEPTION WHEN OTHERS THEN 
     325    failed := true; 
     326    IF SQLERRM = 'Record: (6) from: SELECT * FROM generate_series(1, 10) not found in: SELECT * FROM generate_series(1, 5)' THEN 
     327      NULL; 
     328    ELSE 
     329      RAISE EXCEPTION 'test.assert_rows() did not raise the correct error. Raised: %', SQLERRM; 
     330    END IF; 
     331  END; 
     332  IF NOT failed THEN 
     333    PERFORM test.fail('test.assert_rows() did not fail.'); 
     334  END IF; 
     335   
     336  PERFORM test.pass(); 
     337END; 
     338$$ LANGUAGE plpgsql; 
     339 
     340 
     341CREATE OR REPLACE FUNCTION test.test_assert_column() RETURNS VOID AS $$ 
     342-- Assert the correct operation of test.assert_column 
     343-- module: test_asserts 
     344DECLARE 
     345  failed     bool; 
     346BEGIN 
     347  -- Test an assertion that should pass 
     348  PERFORM test.assert_column( 
    345349    'generate_series(1, 10);', 
    346350    ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 
    347    
     351  -- explicit colname version 
     352  PERFORM test.assert_column( 
     353    'generate_series(1, 10);', 
     354    ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 
     355    'generate_series'); 
     356   
     357  -- ...and an assertion that should fail 
    348358  failed := false; 
    349359  BEGIN 
    350     PERFORM test.assert_values('generate_series(1, 10);', ARRAY[1, 2]); 
     360    PERFORM test.assert_column('generate_series(1, 10);', ARRAY[1, 2]); 
    351361  EXCEPTION WHEN OTHERS THEN 
    352362    failed := true; 
     
    354364      NULL; 
    355365    ELSE 
    356       RAISE EXCEPTION 'test.assert_values() did not raise the correct error. Raised: %', SQLERRM; 
     366      RAISE EXCEPTION 'test.assert_column() did not raise the correct error. Raised: %', SQLERRM; 
    357367    END IF; 
    358368  END; 
    359369  IF NOT failed THEN 
    360     RAISE EXCEPTION 'test.assert_values() did not fail.'; 
    361   END IF; 
    362    
    363   RAISE EXCEPTION '[OK]'; 
    364 END; 
    365 $$ LANGUAGE plpgsql; 
     370    PERFORM test.fail('test.assert_column() did not fail.'); 
     371  END IF; 
     372   
     373  RAISE EXCEPTION '[OK]'; 
     374END; 
     375$$ LANGUAGE plpgsql;