Changeset 10 for trunk/epic
- Timestamp:
- 08/18/08 17:21:15 (4 years ago)
- Location:
- trunk/epic
- Files:
-
- 2 modified
-
epic.sql (modified) (12 diffs)
-
test/test_asserts.sql (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/epic/epic.sql
r9 r10 48 48 49 49 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). 51 53 2. Put your test in the "test" schema. 52 54 3. Start the name of your test with "test_". … … 80 82 81 83 -- ALWAYS RAISE EXCEPTION at the end of test procs to rollback! 82 RAISE EXCEPTION '[OK]';83 END; 84 $$ LANGUAGE plpgsql; 85 86 87 Test helperfunctions88 ------------------- --84 PERFORM test.pass(); 85 END; 86 $$ LANGUAGE plpgsql; 87 88 89 Assertion functions 90 ------------------- 89 91 90 92 Epic.sql includes some functions to make tests easier to write (and shorter). … … 102 104 * test.assert_less_than(elem_1 anyelement, elem_2 anyelement) 103 105 * 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. 106 110 107 * test.assert_raises(call text, errm text, state text): Raises an108 exception if 'SELECT * FROM [call];' does not raise errm111 * test.assert_raises(call text, errm text, state text): 112 Raises an exception if 'SELECT * FROM [call];' does not raise errm 109 113 (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.116 114 117 115 … … 217 215 modulename text; 218 216 output_record test.results%ROWTYPE; 217 splitpoint int; 219 218 BEGIN 220 219 SELECT module INTO modulename FROM test.testnames WHERE name = testname; … … 224 223 EXECUTE 'SELECT * FROM test.' || testname || '();'; 225 224 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))) 229 231 RETURNING * INTO output_record; 230 232 RETURN output_record; … … 272 274 END LOOP; 273 275 END LOOP; 276 END; 277 $$ LANGUAGE plpgsql; 278 279 280 CREATE 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). 282 DECLARE 283 msg text; 284 BEGIN 285 msg := '[' || result || ']'; 286 IF errmsg IS NOT NULL THEN 287 msg := msg || ' ' || errmsg; 288 END IF; 289 RAISE EXCEPTION '%', msg; 290 END; 291 $$ LANGUAGE plpgsql; 292 293 294 CREATE OR REPLACE FUNCTION test.pass(msg text) RETURNS VOID AS $$ 295 -- Use this to finish a successful test. Raises exception '[OK] msg'. 296 BEGIN 297 PERFORM test.finish('OK', msg); 298 END; 299 $$ LANGUAGE plpgsql; 300 CREATE OR REPLACE FUNCTION test.pass() RETURNS VOID AS $$ 301 -- Use this to finish a successful test. Raises exception '[OK]'. 302 BEGIN 303 PERFORM test.finish('OK', NULL); 304 END; 305 $$ LANGUAGE plpgsql; 306 307 308 CREATE OR REPLACE FUNCTION test.fail(msg text) RETURNS VOID AS $$ 309 -- Use this to finish a failed test. Raises exception '[FAIL] msg'. 310 BEGIN 311 PERFORM test.finish('FAIL', msg); 312 END; 313 $$ LANGUAGE plpgsql; 314 CREATE OR REPLACE FUNCTION test.fail() RETURNS VOID AS $$ 315 -- Use this to finish a failed test. Raises exception '[FAIL]'. 316 BEGIN 317 PERFORM test.finish('FAIL', NULL); 318 END; 319 $$ LANGUAGE plpgsql; 320 321 322 CREATE OR REPLACE FUNCTION test.todo(msg text) RETURNS VOID AS $$ 323 -- Use this to abort a test as 'todo'. Raises exception '[TODO] msg'. 324 BEGIN 325 PERFORM test.finish('TODO', msg); 326 END; 327 $$ LANGUAGE plpgsql; 328 CREATE OR REPLACE FUNCTION test.todo() RETURNS VOID AS $$ 329 -- Use this to abort a test as 'todo'. Raises exception '[TODO]'. 330 BEGIN 331 PERFORM test.finish('TODO', NULL); 332 END; 333 $$ LANGUAGE plpgsql; 334 335 336 CREATE OR REPLACE FUNCTION test.skip(msg text) RETURNS VOID AS $$ 337 -- Use this to skip a test. Raises exception '[SKIP] msg'. 338 BEGIN 339 PERFORM test.finish('SKIP', msg); 340 END; 341 $$ LANGUAGE plpgsql; 342 CREATE OR REPLACE FUNCTION test.skip() RETURNS VOID AS $$ 343 -- Use this to skip a test. Raises exception '[SKIP]'. 344 BEGIN 345 PERFORM test.finish('SKIP', NULL); 274 346 END; 275 347 $$ LANGUAGE plpgsql; … … 444 516 445 517 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. 518 CREATE OR REPLACE FUNCTION test.assert_rows(source text, expected text) RETURNS VOID AS $$ 519 -- Asserts that two sets of rows have equal values. 449 520 -- 450 -- Pass the variable *names* (not the records themselves) as the first451 -- 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. 452 523 -- 453 524 -- Example: 454 525 -- 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)'); 528 DECLARE 529 rec record; 530 BEGIN 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; 540 END; 541 $$ LANGUAGE plpgsql IMMUTABLE; 542 543 544 CREATE 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. 462 546 -- 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. 485 548 -- 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. 493 553 -- 494 554 -- Example: 495 -- PERFORM test.assert_ values(555 -- PERFORM test.assert_column( 496 556 -- 'get_favorite_user_ids(' || user_id || ');', 497 557 -- ARRAY[24, 10074, 87321], 'user_id'); … … 502 562 firstname text; 503 563 BEGIN 504 -- Dump the sourceinto a temp table564 -- Dump the call output into a temp table 505 565 IF colname IS NULL THEN 506 566 EXECUTE 'CREATE TEMPORARY TABLE _test_assert_values_base AS ' || 507 'SELECT * FROM ' || source|| ';';567 'SELECT * FROM ' || call || ';'; 508 568 SELECT INTO firstname a.attname 509 569 FROM pg_class c LEFT JOIN pg_attribute a ON c.oid = a.attrelid … … 513 573 AND a.attnum >= 1 514 574 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;'; 516 576 ELSE 517 577 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 || ';'; 519 579 END IF; 520 580 … … 525 585 FOR i IN array_lower(expected, 1)..array_upper(expected, 1) 526 586 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; 528 593 END LOOP; 529 594 … … 532 597 BEGIN 533 598 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)' 535 600 LOOP 536 RAISE EXCEPTION 'result: % not in array: %', record. result, expected;601 RAISE EXCEPTION 'result: % not in array: %', record._assert_values_result, expected; 537 602 END LOOP; 538 603 539 604 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)' 541 606 LOOP 542 RAISE EXCEPTION 'element: % not in source: %', record.result, source;607 RAISE EXCEPTION 'element: % not in call: %', record._assert_values_result, call; 543 608 END LOOP; 544 609 EXCEPTION WHEN OTHERS THEN … … 553 618 $$ LANGUAGE plpgsql; 554 619 555 CREATE OR REPLACE FUNCTION test.assert_ values(sourcetext, expected anyarray) RETURNS VOID AS $$556 -- Implicit column version of assert_ values557 BEGIN 558 PERFORM test.assert_ values(source, expected, NULL);559 END; 560 $$ LANGUAGE plpgsql; 620 CREATE OR REPLACE FUNCTION test.assert_column(call text, expected anyarray) RETURNS VOID AS $$ 621 -- Implicit column version of assert_column 622 BEGIN 623 PERFORM test.assert_column(call, expected, NULL); 624 END; 625 $$ LANGUAGE plpgsql; -
trunk/epic/test/test_asserts.sql
r9 r10 300 300 301 301 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 302 CREATE OR REPLACE FUNCTION test.test_assert_rows() RETURNS VOID AS $$ 303 -- Assert the correct operation of test.assert_rows 340 304 -- module: test_asserts 341 305 DECLARE 342 306 failed bool; 343 307 BEGIN 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(); 337 END; 338 $$ LANGUAGE plpgsql; 339 340 341 CREATE OR REPLACE FUNCTION test.test_assert_column() RETURNS VOID AS $$ 342 -- Assert the correct operation of test.assert_column 343 -- module: test_asserts 344 DECLARE 345 failed bool; 346 BEGIN 347 -- Test an assertion that should pass 348 PERFORM test.assert_column( 345 349 'generate_series(1, 10);', 346 350 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 348 358 failed := false; 349 359 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]); 351 361 EXCEPTION WHEN OTHERS THEN 352 362 failed := true; … … 354 364 NULL; 355 365 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; 357 367 END IF; 358 368 END; 359 369 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]'; 374 END; 375 $$ LANGUAGE plpgsql;
