static char *run_failure_tests(catcierge_haar_prey_method_t prey_method)
{
	int i;
	int j;
	catcierge_grb_t grb;
	catcierge_args_t *args = &grb.args;

	catcierge_grabber_init(&grb);

	catcierge_haar_matcher_args_init(&args->haar);
	args->saveimg = 0;
	args->matcher = "haar"; 
	args->matcher_type = MATCHER_HAAR;
	args->ok_matches_needed = 3;

	args->haar.prey_method = prey_method;
	args->haar.prey_steps = 2;
	args->haar.cascade = CATCIERGE_CASCADE;

	#ifdef CATCIERGE_GUI_TESTS
	args->show = 1;
	#endif
	//args->save_steps = 1;
	//args->saveimg = 1;

	if (catcierge_matcher_init(&grb.matcher, (catcierge_matcher_args_t *)&args->haar))
	{
		return "Failed to init catcierge lib!\n";
	}

	catcierge_haar_matcher_print_settings(&args->haar);

	catcierge_set_state(&grb, catcierge_state_waiting);

	for (j = 10; j <= 14; j++)
	{
		catcierge_test_STATUS("Test series %d", j);

		// This is the initial image that obstructs the frame
		// and triggers the matching.
		load_test_image_and_run(&grb, j, 1);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));
	
		// Match against 4 pictures, and decide the lockout status.
		for (i = 1; i <= 4; i++)
		{
			load_test_image_and_run(&grb, j, i);
		}

		mu_assert("Expected LOCKOUT state", (grb.state == catcierge_state_lockout));

		load_test_image_and_run(&grb, 1, 5);
		mu_assert("Expected WAITING state", (grb.state == catcierge_state_waiting));
	}

	catcierge_matcher_destroy(&grb.matcher);
	catcierge_grabber_destroy(&grb);

	return NULL;
}
static char *run_save_steps_test()
{
	catcierge_grb_t grb;
	catcierge_args_t *args = &grb.args;

	catcierge_grabber_init(&grb);

	args->matcher_type = MATCHER_HAAR;
	args->ok_matches_needed = 3;

	catcierge_haar_matcher_args_init(&args->haar);
	args->haar.prey_method = PREY_METHOD_ADAPTIVE;
	args->haar.prey_steps = 2;
	args->haar.cascade = CATCIERGE_CASCADE;

	args->save_steps = 1;
	args->saveimg = 1;
	args->output_path = "./test_save_steps";
	catcierge_make_path(args->output_path);

	if (catcierge_matcher_init(&grb.matcher, (catcierge_matcher_args_t *)&args->haar))
	{
		return "Failed to init catcierge lib!\n";
	}

	catcierge_test_STATUS("Test save steps");

	catcierge_set_state(&grb, catcierge_state_waiting);

	// This is the initial image that obstructs the frame
	// and triggers the matching.
	load_test_image_and_run(&grb, 1, 1);

	// Some normal images.
	load_test_image_and_run(&grb, 10, 1);
	catcierge_test_STATUS("Step image count: %d", grb.match_group.matches[0].result.step_img_count);
	mu_assert("Expected 10 step images", grb.match_group.matches[0].result.step_img_count == 11);
	
	load_test_image_and_run(&grb, 10, 2);
	catcierge_test_STATUS("Step image count: %d", grb.match_group.matches[1].result.step_img_count);
	mu_assert("Expected 10 step images", grb.match_group.matches[1].result.step_img_count == 11);

	load_test_image_and_run(&grb, 6, 2); // Going out.
	catcierge_test_STATUS("Step image count: %d", grb.match_group.matches[2].result.step_img_count);
	mu_assert("Expected 4 step images", grb.match_group.matches[2].result.step_img_count == 4);
	mu_assert("Got NULL image for used step image", grb.match_group.matches[2].result.steps[2].img != NULL);
	mu_assert("Got non-NULL image for unused step image", grb.match_group.matches[2].result.steps[4].img == NULL);

	load_test_image_and_run(&grb, 10, 1);
	catcierge_test_STATUS("Step image count: %d", grb.match_group.matches[3].result.step_img_count);
	mu_assert("Expected 10 step images", grb.match_group.matches[3].result.step_img_count == 11);

	catcierge_matcher_destroy(&grb.matcher);
	catcierge_grabber_destroy(&grb);

	return NULL;
}
static char *run_success_tests()
{
	int i;
	int j;
	catcierge_grb_t grb;
	catcierge_args_t *args = &grb.args;

	catcierge_grabber_init(&grb);
	catcierge_args_init_vars(args);

	catcierge_haar_matcher_args_init(&args->haar);
	args->saveimg = 0;
	args->matcher_type = MATCHER_HAAR;
	args->haar.cascade = strdup(CATCIERGE_CASCADE);

	if (catcierge_matcher_init(&grb.matcher, (catcierge_matcher_args_t *)&args->haar))
	{
		return "Failed to init catcierge lib!\n";
	}

	catcierge_haar_matcher_print_settings(&args->haar);

	grb.running = 1;
	catcierge_set_state(&grb, catcierge_state_waiting);

	for (j = 6; j <= 9; j++)
	{
		catcierge_test_STATUS("Test series %d", j);

		// This is the initial image that obstructs the frame
		// and triggers the matching.
		load_test_image_and_run(&grb, j, 1);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));
	
		// Match against 4 pictures, and decide the lockout status.
		for (i = 1; i <= 4; i++)
		{
			load_test_image_and_run(&grb, j, i);
		}

		mu_assert("Expected KEEP OPEN state", (grb.state == catcierge_state_keepopen));

		load_test_image_and_run(&grb, 1, 5);
		mu_assert("Expected WAITING state", (grb.state == catcierge_state_waiting));
	}

	catcierge_matcher_destroy(&grb.matcher);
	catcierge_args_destroy_vars(args);
	catcierge_grabber_destroy(&grb);

	return NULL;
}
//
// Tests passing 1 initial image that triggers matching.
// Then pass 4 images that should result in a successful match.
// Finally if "obstruct" is set, a single image is passed that obstructs the frame.
// After that we expect to go back to waiting.
//
static char *run_success_tests(int obstruct)
{
	int i;
	int j;
	catcierge_grb_t grb;
	catcierge_args_t *args = &grb.args;

	catcierge_grabber_init(&grb);
	grb.running = 1;

	args->matcher = "template";
	args->matcher_type = MATCHER_TEMPLATE;
	args->saveimg = 0;
	args->templ.match_flipped = 1;
	args->templ.match_threshold = 0.8;
	args->templ.snout_paths[0] = CATCIERGE_SNOUT1_PATH;
	args->templ.snout_count++;
	args->templ.snout_paths[1] = CATCIERGE_SNOUT2_PATH;
	args->templ.snout_count++;

	if (catcierge_matcher_init(&grb.matcher, (catcierge_matcher_args_t *)&args->templ))
	{
		return "Failed to init catcierge lib!\n";
	}

	catcierge_template_matcher_set_debug((catcierge_template_matcher_t *)grb.matcher, 0);

	catcierge_template_matcher_print_settings(&args->templ);

	catcierge_set_state(&grb, catcierge_state_waiting);

	// Give the state machine a series of known images
	// and make sure the states are as expected.
	for (j = 1; j <= 5; j++)
	{
		catcierge_test_STATUS("Test series %d", j);

		// This is the initial image that obstructs the frame
		// and triggers the matching.
		load_test_image_and_run(&grb, j, 1);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));

		// Match against 4 pictures, and decide the lockout status.
		for (i = 1; i <= 4; i++)
		{
			load_test_image_and_run(&grb, j, i);
		}

		mu_assert("Expected KEEP OPEN state", (grb.state == catcierge_state_keepopen));

		if (obstruct)
		{
			// First obstruct the frame.
			load_test_image_and_run(&grb, j, 1);
			mu_assert("Expected KEEP OPEN state", (grb.state == catcierge_state_keepopen));

			// And then clear it.
			load_test_image_and_run(&grb, 1, 5);
			mu_assert("Expected WAITING state", (grb.state == catcierge_state_waiting));
		}
		else
		{
			// Give it a clear frame so that it will stop
			load_test_image_and_run(&grb, 1, 5);
			mu_assert("Expected WAITING state", (grb.state == catcierge_state_waiting));
		}
	}

	catcierge_template_matcher_destroy(&grb.matcher);
	catcierge_grabber_destroy(&grb);

	return NULL;
}
static char *run_failure_tests(int obstruct, catcierge_lockout_method_t lockout_method)
{
	char *e = NULL;
	size_t i;
	catcierge_grb_t grb;
	catcierge_args_t *args = &grb.args;

	catcierge_grabber_init(&grb);
	grb.running = 1;

	args->saveimg = 0;
	args->lockout_method = lockout_method;
	args->lockout_time = 2;
	args->matcher = "template";
	args->matcher_type = MATCHER_TEMPLATE;
	args->templ.match_flipped = 1;
	args->templ.match_threshold = 0.8;
	set_default_test_snouts(args);

	if (catcierge_matcher_init(&grb.matcher, (catcierge_matcher_args_t *)&args->templ))
	{
		return "Failed to init catcierge lib!\n";
	}

	catcierge_template_matcher_print_settings(&args->templ);

	catcierge_set_state(&grb, catcierge_state_waiting);

	//
	// Run the same twice so we're sure the timers work
	// several times properly.
	//
	for (i = 0; i < 2; i++)
	{
		// Obstruct the frame to begin matching.
		load_test_image_and_run(&grb, 1, 2);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));
	
		// Pass 4 frames (first ok, the rest not...)
		load_test_image_and_run(&grb, 1, 2);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));
	
		load_test_image_and_run(&grb, 1, 3);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));
	
		load_test_image_and_run(&grb, 1, 4);
		mu_assert("Expected MATCHING state", (grb.state == catcierge_state_matching));
	
		load_test_image_and_run(&grb, 1, 4);
		mu_assert("Expected LOCKOUT state", (grb.state == catcierge_state_lockout));
		catcierge_test_STATUS("Lockout state as expected");
	
		// Test the lockout logic.
		if ((e = run_lockout_tests(&grb, obstruct, lockout_method)))
		{
			return e;
		}
	}

	catcierge_matcher_destroy(&grb.matcher);
	catcierge_grabber_destroy(&grb);

	return NULL;
}
// This tests the different lockout strategies.
static char *run_lockout_tests(catcierge_grb_t *grb, int obstruct,
	catcierge_lockout_method_t lockout_method)
{
	catcierge_args_t *args = &grb->args;

	switch (lockout_method)
	{
		case OBSTRUCT_OR_TIMER_3:
			if (obstruct == 1)
			{
				// Obstruct and then un-obstruct.
				catcierge_test_STATUS("Lockout method: Obstruct or timer. Obstruct,"
					" then un-obstruct should result in unlock");

				load_test_image_and_run(grb, 1, 2); // Obstruct.
				mu_assert("Expected LOCKOUT state after obstruction",
					(grb->state == catcierge_state_lockout));

				load_test_image_and_run(grb, 1, 5); // Clear frame.
				mu_assert("Expected WAITING state after clear frame",
					(grb->state == catcierge_state_waiting));
			}
			else if (obstruct == 2)
			{
				// Keep on obstructing. Timer should unlock.
				catcierge_test_STATUS("Lockout method: Obstruct or timer. "
					"Continous obstruction, %d second timer should result in unlock",
					args->lockout_time + 1);

				load_test_image_and_run(grb, 1, 2); // Obstruct.
				mu_assert("Expected LOCKOUT state after obstruction",
					(grb->state == catcierge_state_lockout));

				sleep(args->lockout_time + 1);
				
				//catcierge_run_state(grb);
				load_test_image_and_run(grb, 1, 2);
				mu_assert("Expected WAITING state after timeout",
					(grb->state == catcierge_state_waiting));
			}
			else
			{
				catcierge_test_STATUS("Lockout method: Obstruct or timer. "
					"No obstruction. Sleeping %d seconds should result in unlock",
					args->lockout_time + 1);

				sleep(args->lockout_time + 1);

				// Clear frame.
				load_test_image_and_run(grb, 1, 5);
				mu_assert("Expected WAITING stat after timeout",
					(grb->state == catcierge_state_waiting));
			}
			break;
		case OBSTRUCT_THEN_TIMER_2:
			if (obstruct == 1)
			{
				catcierge_test_STATUS("Lockout method: Obstruct then Timer. "
					"%d second timer should start after obstruction has ended",
					args->lockout_time + 1);

				load_test_image_and_run(grb, 1, 2); // Obstruct.
				mu_assert("Expected LOCKOUT state after obstruction",
					(grb->state == catcierge_state_lockout));

				load_test_image_and_run(grb, 1, 5); // Clear frame.
				mu_assert("Expected LOCKOUT state after clear frame",
					(grb->state == catcierge_state_lockout));

				sleep(1);

				mu_assert("Expected LOCKOUT state after 1 second clear frame",
					(grb->state == catcierge_state_lockout));

				sleep(args->lockout_time);

				load_test_image_and_run(grb, 1, 5); // Clear frame.
				mu_assert("Expected WAITING state after clear frame and timeout",
					(grb->state == catcierge_state_waiting));

				catcierge_test_STATUS("Lockout timer value: %f seconds\n",
					catcierge_timer_get(&grb->lockout_timer));
			}
			else if (obstruct == 2)
			{
				catcierge_test_STATUS("Lockout method: Obstruct then Timer. "
					"Continous obstruction. Unlock should never happen");

				load_test_image_and_run(grb, 1, 2); // Obstruct.
				mu_assert("Expected LOCKOUT state after obstruction",
					(grb->state == catcierge_state_lockout));
				
				sleep(args->lockout_time + 1);
				
				load_test_image_and_run(grb, 1, 2); // Obstruct.
				mu_assert("Expected LOCKOUT state after timeout and"
					"continous obstruction",
					(grb->state == catcierge_state_lockout));

				catcierge_set_state(grb, catcierge_state_waiting);
			}
			else
			{
				catcierge_test_STATUS("Lockout method: Obstruct then Timer. "
					"No obstruction. %d second timer should unlock.",
					args->lockout_time + 1);

				load_test_image_and_run(grb, 1, 5); // Clear frame.
				mu_assert("Expected LOCKOUT state after clear frame",
					(grb->state == catcierge_state_lockout));

				sleep(args->lockout_time + 1);

				load_test_image_and_run(grb, 1, 5); // Clear frame.
				mu_assert("Expected WAITING state after clear frame and timeout",
					(grb->state == catcierge_state_waiting));
			}
			break;
		case TIMER_ONLY_1:
			// Sleep for unlock time and make sure we've unlocked.
			catcierge_test_STATUS("Lockout method: Timer only, sleep %d seconds",
				args->lockout_time + 1);

			sleep(args->lockout_time + 1);

			catcierge_run_state(grb);
			mu_assert("Expected WAITING state after timeout",
				(grb->state == catcierge_state_waiting));
			break;
	}

	return NULL;
}