Example #1
0
/*
 * A shared method to test proper activation during IdleTracker_Init()
 * or a direct call to IdleTracker_ActivateProcess() during regular
 * activation of an idle process.
 */
static void
CheckForActivation(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(0 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	EventVersion oldVersion = *CurrentVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to false as we want to verify that it gets set to true
	 * once the testFunc() call returns
	 */
	isProcessActive = false;

	assert_true(MySessionState->activeProcessCount == 0);

	testFunc();

	assert_true(activationVersion == *CurrentVersion);
	assert_true(deactivationVersion == 0);
	assert_true(isProcessActive == true);
	assert_true(MySessionState->activeProcessCount == 1);
}
/*
 * Checks if RunawayCleaner_RunawayCleanupDoneForProcess prevents multiple cleanup
 * for a single runaway event by properly updating beginCleanupRunawayVersion and
 * endCleanupRunawayVersion
 */
void
test__RunawayCleaner_RunawayCleanupDoneForProcess__PreventsDuplicateCleanup(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, CLEANUP_COUNTDOWN /* pinCount */, 12345 /* vmem */);

	static fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	*latestRunawayVersion = 10;
	/*
	 * Some imaginary cleanup begin/end event version. The idea is to ensure
	 * that once the RunawayCleaner_RunawayCleanupDoneForProcess call returns
	 * we will have both set to latestRunawayVersion
	 */
	beginCleanupRunawayVersion = *latestRunawayVersion;
	endCleanupRunawayVersion = 0;

	/* Make sure the cleanup goes through */
	vmemTrackerInited = true;
	isProcessActive = true;

	RunawayCleaner_RunawayCleanupDoneForProcess(false /* ignoredCleanup */);
	/* cleanupCountdown should be adjusted */
	assert_true(MySessionState->cleanupCountdown == CLEANUP_COUNTDOWN - 1);

	assert_true(beginCleanupRunawayVersion == endCleanupRunawayVersion);
	assert_true(beginCleanupRunawayVersion == *latestRunawayVersion);

	/* Second call shouldn't change anything */
	RunawayCleaner_RunawayCleanupDoneForProcess(false /* ignoredCleanup */);
	/* cleanupCountdown is unchanged */
	assert_true(MySessionState->cleanupCountdown == CLEANUP_COUNTDOWN - 1);
}
/*
 * Checks if RunawayCleaner_RunawayCleanupDoneForProcess reactivates the runaway detector
 * once all the processes of the runaway session are done cleaning
 */
void
test__RunawayCleaner_RunawayCleanupDoneForProcess__ReactivatesRunawayDetection(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static EventVersion fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	/*
	 * Set beginCleanupRunawayVersion to latestRunawayVersion and endCleanupRunawayVersion
	 * to a smaller value to simulate an ongoing cleanup
	 */
	beginCleanupRunawayVersion = *latestRunawayVersion;
	endCleanupRunawayVersion = 1;

	/* Valid isRunawayDetector is necessary for Assert */
	static uint32 fakeIsRunawayDetector = 1;
	isRunawayDetector = &fakeIsRunawayDetector;

	/* Just an active process that never became idle */
	activationVersion = 0;
	deactivationVersion = 0;
	isProcessActive = true;

	/* Make sure the cleanup goes through */
	vmemTrackerInited = true;

	RunawayCleaner_RunawayCleanupDoneForProcess(false /* ignoredCleanup */);
	/* The cleanupCountdown must be decremented as we cleaned up */
	assert_true(MySessionState->cleanupCountdown == 1);
	/* We updated the endCleanupRunawayVersion to indicate that we finished cleanup */
	assert_true(endCleanupRunawayVersion == beginCleanupRunawayVersion);

	/* The runaway detector promotion should be disabled as we still have 1 QE unclean */
	assert_true(*isRunawayDetector == 1);

	/*
	 * Fake a ongoing cleanup by making endCleanupRunawayVersion < beginCleanupRunawayVersion
	 * so that we can execute cleanup one more time, marking all QEs clean
	 */
	endCleanupRunawayVersion = 1;

	/*
	 * cleanupCountdown should reach 0, and immediately afterwards should be set to
	 * CLEANUP_COUNTDOWN_BEFORE_RUNAWAY
	 */
	RunawayCleaner_RunawayCleanupDoneForProcess(false /* ignoredCleanup */);
	assert_true(MySessionState->cleanupCountdown == CLEANUP_COUNTDOWN_BEFORE_RUNAWAY);
	/* Runaway detector should be re-enabled */
	assert_true(*isRunawayDetector == 0);
}
Example #4
0
/*
 * A shared method to test proper deactivation during IdleTracker_Shutdown()
 * or a direct call to IdleTracker_DeactivateProcess() during regular
 * deactivation of an active process when a proper cleanup was done, throwing
 * and elog(ERROR,...) and therefore the call would never return to the calling
 * function
 */
static void
CheckForDeactivationWithProperCleanup(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(1 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	EventVersion oldVersion = *CurrentVersion;
	/* Ensure we have a pending runaway event */
	EventVersion fakeLatestRunawayVersion = *CurrentVersion - 1;
	latestRunawayVersion = &fakeLatestRunawayVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to true as we want to verify that it gets set to false
	 * once the testFunc() call returns
	 */
	isProcessActive = true;

	assert_true(MySessionState->activeProcessCount == 1);

	/*
	 * Deactivation must call RunawayCleaner_StartCleanup before finishing deactivation
	 * to check for cleanup requirement for any pending runaway event. The mocked method
	 * is throwing an exception here, which would block execution of code following the call
	 * site
	 */
	will_be_called_with_sideeffect(RunawayCleaner_StartCleanup, &_ExceptionalCondition, NULL);

	PG_TRY();
	{
		testFunc();
		assert_false("Expected an exception");
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();

	assert_true(activationVersion == 0);
	assert_true(deactivationVersion == *CurrentVersion);
	assert_true(isProcessActive == false);
	assert_true(MySessionState->activeProcessCount == 0);
}
/*
 * Checks if RunawayCleaner_StartCleanup() does not start cleanup if
 * the current session is not a runaway
 */
void
test__RunawayCleaner_StartCleanup__IgnoresNonRunaway(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 2 /* pinCount */, 0 /* vmem */);

	static fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	beginCleanupRunawayVersion = 0;

	RunawayCleaner_StartCleanup();

	/* Cleanup shouldn't have begun */
	assert_true(beginCleanupRunawayVersion != *latestRunawayVersion);
}
/*
 * Checks if RunawayCleaner_StartCleanup() does not execute a duplicate
 * cleanup for the same runaway event that it already started cleaning up
 */
void
test__RunawayCleaner_StartCleanup__IgnoresDuplicateCleanup(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 0 /* vmem */);

	static fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	beginCleanupRunawayVersion = *latestRunawayVersion;

	/*
	 * As we are not providing IsCommitInProgress, the call itself verifies
	 * that we are not attempting any cleanup
	 */
	RunawayCleaner_StartCleanup();
}
/*
 * Checks if RunawayCleaner_RunawayCleanupDoneForProcess reactivates a process
 * if the deactivation process triggers cleanup for a pending runaway event
 */
void
test__RunawayCleaner_RunawayCleanupDoneForProcess__UndoDeactivation(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	/*
	 * Set beginCleanupRunawayVersion to latestRunawayVersion and endCleanupRunawayVersion
	 * to a smaller value to simulate an ongoing cleanup
	 */
	beginCleanupRunawayVersion = *latestRunawayVersion;
	endCleanupRunawayVersion = 1;

	/* Valid isRunawayDetector is necessary for Assert */
	static uint32 fakeIsRunawayDetector = 1;
	isRunawayDetector = &fakeIsRunawayDetector;

	/* Make sure we became idle after a pending runaway event */
	activationVersion = 1;
	deactivationVersion = *latestRunawayVersion + 1;

	/* Make sure the cleanup goes through */
	vmemTrackerInited = true;
	isProcessActive = false;

	/* We must undo the idle state */
	will_be_called(IdleTracker_ActivateProcess);

	RunawayCleaner_RunawayCleanupDoneForProcess(false /* ignoredCleanup */);
	/* The cleanupCountdown must be decremented as we cleaned up */
	assert_true(MySessionState->cleanupCountdown == 1);
	/* We updated the endCleanupRunawayVersion to indicate that we finished cleanup */
	assert_true(endCleanupRunawayVersion == beginCleanupRunawayVersion);
}
/*
 * Checks if RunawayCleaner_RunawayCleanupDoneForSession reactivates the runaway detector
 */
void
test__RunawayCleaner_RunawayCleanupDoneForSession__ResetsRunawayFlagAndReactivateRunawayDetector(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static EventVersion fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	/*
	 * Satisfy asserts
	 */
	beginCleanupRunawayVersion = *latestRunawayVersion;
	endCleanupRunawayVersion = beginCleanupRunawayVersion;
	MySessionState->cleanupCountdown = CLEANUP_COUNTDOWN_BEFORE_RUNAWAY;

	*isRunawayDetector = 1;
	MySessionState->runawayStatus = RunawayStatus_PrimaryRunawaySession;
	RunawayCleaner_RunawayCleanupDoneForSession();

	assert_true(MySessionState->runawayStatus == RunawayStatus_NotRunaway);
	/* Runaway detector should be re-enabled */
	assert_true(*isRunawayDetector == 0);
}
/*
 * Checks if RunawayCleaner_RunawayCleanupDoneForProcess ignores duplicate cleanup
 * for a single runaway event
 */
void
test__RunawayCleaner_RunawayCleanupDoneForProcess__IgnoresDuplicateCalls(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	/*
	 * Set beginCleanupRunawayVersion and endCleanupRunawayVersion to
	 * latestRunawayVersion which should make the function call a no-op
	 */
	beginCleanupRunawayVersion = *latestRunawayVersion;
	endCleanupRunawayVersion = *latestRunawayVersion;

	/* Make sure the cleanup goes through */
	vmemTrackerInited = true;
	isProcessActive = true;

	RunawayCleaner_RunawayCleanupDoneForProcess(false /* ignoredCleanup */);
	/* Nothing got cleaned */
	assert_true(MySessionState->cleanupCountdown == 2);
}
/*
 * Checks if RunawayCleaner_StartCleanup() ignores cleanup if interrupts are held off
 */
void
test__RunawayCleaner_StartCleanup__IgnoresCleanupInHoldoffInterrupt(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static EventVersion fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	/*
	 * Set beginCleanupRunawayVersion to less than *latestRunawayVersino
	 * to trigger a cleanup
	 */
	beginCleanupRunawayVersion = 1;
	endCleanupRunawayVersion = 1;

	/* Make sure the cleanup goes through */
	vmemTrackerInited = true;
	isProcessActive = true;

	CritSectionCount = 0;
	InterruptHoldoffCount = 1;
	RunawayCleaner_StartCleanup();

	assert_true(beginCleanupRunawayVersion == *latestRunawayVersion);
	/* Cleanup is done, without ever throwing an ERROR */
	assert_true(endCleanupRunawayVersion == beginCleanupRunawayVersion);

	/*
	 * cleanupCountdown is decremented by 1 as there was no error, and therefore
	 * the cleanup is done within the same call of RunawayCleaner_StartCleanup
	 */
	assert_true(MySessionState->cleanupCountdown == 1);

	InterruptHoldoffCount = 0;
}
/*
 * Checks if RunawayCleaner_RunawayCleanupDoneForProcess() ignores cleanupCountdown
 * if optional cleanup
 */
void
test__RunawayCleaner_RunawayCleanupDoneForProcess__IgnoresCleanupIfNotRequired(void **state)
{
#define CLEANUP_COUNTDOWN 2
	InitFakeSessionState(2 /* activeProcessCount */,
			CLEANUP_COUNTDOWN /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static EventVersion fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	*latestRunawayVersion = 10;

	/*
	 * Set beginCleanupRunawayVersion to less than *latestRunawayVersino
	 * to trigger a cleanup
	 */
	beginCleanupRunawayVersion = 1;
	endCleanupRunawayVersion = 1;

	/* Make sure the cleanup is not ignored for vmem initialization */
	vmemTrackerInited = true;
	/* Simulate a deactivation before the runaway */
	deactivationVersion = *latestRunawayVersion - 1;
	activationVersion = *latestRunawayVersion - 1;
	isProcessActive = false;

	CritSectionCount = 0;
	InterruptHoldoffCount = 0;

	RunawayCleaner_StartCleanup();

	/* The cleanup shouldn't even start as the QE was deactivated at the time of the runaway*/
	assert_true(beginCleanupRunawayVersion == 1);
	assert_true(endCleanupRunawayVersion == 1);

	/*
	 * cleanupCountdown should not be decremented as this was an optional cleanup
	 */
	assert_true(MySessionState->cleanupCountdown == CLEANUP_COUNTDOWN);
	assert_true(MySessionState->runawayStatus == RunawayStatus_PrimaryRunawaySession);

	/*
	 * Now simulate a scenario where the we activated but the runaway happened
	 * before the activation
	 */
	beginCleanupRunawayVersion = 2;
	endCleanupRunawayVersion = 2;

	/* Another runaway happened after the last cleanup */
	*latestRunawayVersion = beginCleanupRunawayVersion + 2;
	activationVersion = 5;
	deactivationVersion = 3;
	isProcessActive = true;

	RunawayCleaner_StartCleanup();

	/* The cleanup shouldn't even start as the runaway event happened before the QE became active */
	assert_true(beginCleanupRunawayVersion == 2);
	assert_true(endCleanupRunawayVersion == 2);

	/*
	 * cleanupCountdown should not be decremented as this was an optional cleanup
	 */
	assert_true(MySessionState->cleanupCountdown == CLEANUP_COUNTDOWN);
	assert_true(MySessionState->runawayStatus == RunawayStatus_PrimaryRunawaySession);
}
/*
 * Checks if RunawayCleaner_StartCleanup() starts the cleanup process if
 * all conditions are met (i.e., no commit is in progress and vmem tracker
 * is initialized)
 */
void
test__RunawayCleaner_StartCleanup__StartsCleanupIfPossible(void **state)
{
	InitFakeSessionState(2 /* activeProcessCount */,
			2 /* cleanupCountdown */,
			RunawayStatus_PrimaryRunawaySession /* runawayStatus */, 2 /* pinCount */, 12345 /* vmem */);

	static fakeLatestRunawayVersion = 10;
	latestRunawayVersion = &fakeLatestRunawayVersion;
	*latestRunawayVersion = 10;

	/*
	 * Set beginCleanupRunawayVersion to less than *latestRunawayVersion
	 * to trigger a cleanup
	 */
	beginCleanupRunawayVersion = 1;
	endCleanupRunawayVersion = 1;
	isProcessActive = true;

	/* Make sure the cleanup goes through */
	vmemTrackerInited = true;
	CritSectionCount = 0;
	InterruptHoldoffCount = 0;
	/* We need a valid gp_command_count to execute cleanup */
	gp_command_count = 1;
	will_return(superuser, false);

#ifdef FAULT_INJECTOR
	expect_value(FaultInjector_InjectFaultIfSet, identifier, RunawayCleanup);
	expect_value(FaultInjector_InjectFaultIfSet, ddlStatement, DDLNotSpecified);
	expect_value(FaultInjector_InjectFaultIfSet, databaseName, "");
	expect_value(FaultInjector_InjectFaultIfSet, tableName, "");
	will_be_called(FaultInjector_InjectFaultIfSet);
#endif

	EXPECT_EREPORT(ERROR);

	PG_TRY();
	{
		RunawayCleaner_StartCleanup();
		assert_false("Cleanup didn't throw error");
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();

	assert_true(beginCleanupRunawayVersion == *latestRunawayVersion);
	/* We should not finish the cleanup as we errored out */
	assert_true(endCleanupRunawayVersion == 1);

	/* cleanupCountdown shouldn't change as we haven't finished cleanup */
	assert_true(MySessionState->cleanupCountdown == 2);

	/*
	 * If we call RunawayCleaner_StartCleanup again for the same runaway event,
	 * it should be a noop, therefore requiring no "will_be_called" setup
	 */
	RunawayCleaner_StartCleanup();
}
Example #13
0
/*
 * A shared method to test proper deactivation during IdleTracker_Shutdown()
 * or a direct call to IdleTracker_DeactivateProcess() during regular
 * deactivation of an active process when a proper cleanup was not possible
 * (e.g., a transaction is in progress).
 */
static void
CheckForDeactivationWithoutCleanup(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(1 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	EventVersion oldVersion = *CurrentVersion;
	/* Ensure we have a pending runaway event */
	EventVersion fakeLatestRunawayVersion = *CurrentVersion - 1;
	latestRunawayVersion = &fakeLatestRunawayVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to true as we want to verify that it gets set to false
	 * once the testFunc() call returns
	 */
	isProcessActive = true;

	assert_true(MySessionState->activeProcessCount == 1);

	/*
	 * Deactivation must call RunawayCleaner_StartCleanup before finishing deactivation
	 * to check for cleanup requirement for any pending runaway event. The method is
	 * supposed to throw an exception, but for this test we are mocking the function
	 * without side effect. I.e., the function behaves as if a proper cleanup is not
	 * possible
	 */
	will_be_called(RunawayCleaner_StartCleanup);

#ifdef USE_ASSERT_CHECKING
	will_return(RunawayCleaner_IsCleanupInProgress, true);
	/*
	 * Expecting an exception as we are indicating an ongoing cleanup
	 * and yet returning to IdleTracker_DactivateProcess
	 */
	EXPECT_EXCEPTION();
#endif

	PG_TRY();
	{
		testFunc();
#ifdef USE_ASSERT_CHECKING
		assert_false("Expected an assertion failure");
#endif
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();

	assert_true(activationVersion == 0);
	assert_true(deactivationVersion == *CurrentVersion);
	assert_true(isProcessActive == false);
	assert_true(MySessionState->activeProcessCount == 0);
}
Example #14
0
/*
 * A shared method to test that the IdleTracker_DeactivateProcess ignores
 * deactivation for an already idle process when the proc_exit_inprogress
 * is set to true
 */
static void
PreventDuplicateDeactivationDuringProcExit(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(1 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	EventVersion oldVersion = *CurrentVersion;
	/* Ensure we have a pending runaway event */
	EventVersion fakeLatestRunawayVersion = *CurrentVersion - 1;
	latestRunawayVersion = &fakeLatestRunawayVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to false to mark the process as deactivated
	 */
	isProcessActive = false;

	assert_true(MySessionState->activeProcessCount == 1);
	/*
	 * Setting proc_exit_inprogress to true means we won't try to
	 * deactivate an already idle process
	 */
	proc_exit_inprogress = true;
	/* Interrupts should be held off during proc_exit_inprogress*/
	InterruptHoldoffCount = 1;
	/* Now the deactivation should succeed as we have held off interrupts */
	testFunc();
	InterruptHoldoffCount = 0;

	assert_true(activationVersion == deactivationVersion);
	assert_true(deactivationVersion != *CurrentVersion);
	assert_true(isProcessActive == false);
	/* We haven't reduced the activeProcessCount */
	assert_true(MySessionState->activeProcessCount == 1);

#ifdef USE_ASSERT_CHECKING
	/*
	 * Now test that the testFunc fails assert if we try to deactivate an already
	 * idle process during a normal execution (i.e., not proc_exit_inprogress)
	 */
	proc_exit_inprogress = false;
	EXPECT_EXCEPTION();
	PG_TRY();
	{
		testFunc();
		assert_false("Expected assertion failure");
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();
#endif
}