/*

	9unit
	Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this program.  If not, see
	<http://www.gnu.org/licenses/>.

*/

#include <u.h>
#include <libc.h>
#include <stdio.h>

#include "../9unit.h"
#include "util.h"
#include "append-test-log.h"

// Local Types

typedef struct LogData LogData;

struct LogData
{
	TestState state;
	TestLogEntry first, second;
};

// Local Prototypes

static void append_to_empty(TestState *);
static void append_to_existing(TestState *);
static void missing_last(TestState *);
static void missing_first(TestState *);
static void chk_last(TestState *, LogData *, const char *);
static void mk_log_data(LogData *);

static void chk_ptr_chg(
	TestState *,
	const char *,
	const char *,
	const void *,
	const void *
);

// Public Functions

void
test_append_test_log(TestState *s)
{
	print("append_test_log()\n");
	append_to_empty(s);
	append_to_existing(s);
	missing_last(s);
	missing_first(s);
}

// Local Functions

static void
append_to_empty(TestState *s)
{
	TestState test;
	print("\tappend to empty\n");
	mk_sample_state(&test);
	append_test_log(&test, "foo");

	// first_log shouldn't be null
	print("\t\tfirst_log\n");
	chk_ptr_ne(
		s,
		"ERROR: append_test_log(): append to empty: first_log:",
		test.first_log,
		0
	);

	// log should say "foo"
	print("\t\t\ttext\n");
	chk_str_eq(
		s,
		"ERROR: append_test_log(): append to empty: first_log: text:",
		test.first_log->text,
		"foo"
	);

	// last_log should match first_log
	print("\t\tlast_log\n");
	chk_ptr_eq(
		s,
		"ERROR: append_test_log(): append to empty: last_log:",
		test.last_log,
		test.first_log
	);
}

static void
append_to_existing(TestState *s)
{
	LogData ld;
	print("\tappend to existing\n");
	mk_log_data(&ld);
	append_test_log(&ld.state, "baz");

	// first shouldn't change
	print("\t\tfirst_log\n");
	chk_ptr_eq(
		s,
		"ERROR: append_test_log(): append to existing: first_log:",
		ld.state.first_log,
		&ld.first
	);

	chk_last(
		s,
		&ld,
		"ERROR: append_test_log(): append to existing: last_log:"
	);
	free(ld.state.last_log);
}

static void
missing_last(TestState *s)
{
	LogData ld;
	print("\tlast_log missing\n");
	mk_log_data(&ld);
	ld.state.last_log = 0;
	append_test_log(&ld.state, "baz");

	// first shouldn't change
	print("\t\tfirst_log\n");
	chk_ptr_eq(
		s,
		"ERROR: append_test_log(): last_log missing: first_log:",
		ld.state.first_log,
		&ld.first
	);

	chk_last(
		s,
		&ld,
		"ERROR: append_test_log(): last_log missing: last_log:"
	);
	free(ld.state.last_log);
}

static void
missing_first(TestState *s)
{
	LogData ld;
	print("\tfirst_log missing\n");
	mk_log_data(&ld);
	ld.state.first_log = 0;
	append_test_log(&ld.state, "baz");

	// first_log should point to second
	print("\t\tfirst_log\n");
	chk_ptr_eq(
		s,
		"ERROR: append_test_log(): first_log missing: first_log:",
		ld.state.first_log,
		&ld.second
	);

	chk_last(
		s,
		&ld,
		"ERROR: append_test_log(): first_log missing: last_log:"
	);
	free(ld.state.last_log);
}

static void
chk_last(TestState *s, LogData *ld, const char *context)
{
	print("\t\tlast_log\n");
	chk_ptr_chg(
		s,
		"\t\t\t",
		context,
		ld->state.last_log,
		&ld->second
	);
}

static void
mk_log_data(LogData *ld)
{
	mk_sample_state(&ld->state);
	ld->state.first_log = &ld->first;
	ld->state.last_log = &ld->second;
	ld->first.text = "foo";
	ld->first.next = &ld->second;
	ld->second.text = "bar";
	ld->second.next = 0;
}

static void
chk_ptr_chg(
	TestState *s,
	const char *prefix,
	const char *context,
	const void *actual,
	const void *prohibited
)
{
	char str[STR_BUF_SIZE];

	// make sure it's changed
	print(prefix);
	print("changed\n");
	snprintf(str, STR_BUF_SIZE, "%s changed:", context);
	chk_ptr_ne(
		s,
		str,
		actual,
		prohibited
	);

	// make sure it's not null
	print(prefix);
	print("not null\n");
	snprintf(str, STR_BUF_SIZE, "%s not null:", context);
	chk_ptr_ne(
		s,
		str,
		actual,
		0
	);
}

//jl