/* Copyright 2019 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Test USB Type-C VPD and CTVPD module.
 */
#include "common.h"
#include "task.h"
#include "test_util.h"
#include "timer.h"
#include "usb_pd.h"
#include "usb_sm.h"
#include "usb_tc_sm.h"
#include "util.h"
#include "usb_pd_test_util.h"
#include "vpd_api.h"

/*
 * Test State Hierarchy
 *   SM_TEST_A4 transitions to SM_TEST_B4
 *   SM_TEST_B4 transitions to SM_TEST_B5
 *   SM_TEST_B5 transitions to SM_TEST_B6
 *   SM_TEST_B6 transitions to SM_TEST_C
 *   SM_TEST_C  transitions to SM_TEST_A7
 *   SM_TEST_A7 transitions to SM_TEST_A6
 *   SM_TEST_A6 transitions to SM_TEST_A5
 *   SM_TEST_A5 transitions to SM_TEST_A4
 *
 * ---------------------------     ---------------------------
 * | SM_TEST_SUPER_A1        |     | SM_TEST_SUPER_B1        |
 * | ----------------------- |     | ----------------------- |
 * | | SM_TEST_SUPER_A2    | |     | | SM_TEST_SUPER_B2    | |
 * | | ------------------- | |     | | ------------------- | |
 * | | |SM_TEST_SUPER_A3 | | |     | | |SM_TEST_SUPER_B3 | | |
 * | | |                 | | |     | | |                 | | |
 * | | |  -------------  | | |     | | |  -------------  | | |
 * | | |  | SM_TEST_A4|------------------>| SM_TEST_B4|  | | |
 * | | |  -------------  | | |     | | |  -------------  | | |
 * | | |        ^        | | |     | | |--------|--------| | |
 * | | |        |        | | |     | |          |          | |
 * | | |  -------------- | | |     | |          \/         | |
 * | | |  | SM_TEST_A5 | | | |     | |    --------------   | |
 * | | |  -------------- | | |     | |    | SM_TEST_B5 |   | |
 * | | |--------^--------| | |     | |    --------------   | |
 * | |          |          | |     | |          |          | |
 * | |    --------------   | |     | -----------|----------- |
 * | |    | SM_TEST_A6 |   | |     |            \/           |
 * | |    --------------   | |     |      --------------     |
 * | |----------^----------| |     |      | SM_TEST_B6 |     |
 * |            |            |     |      --------------     |
 * |      --------------     |     |--------/----------------|
 * |      | SM_TEST_A7 |     |             /
 * |      --------------     |            /
 * |------------------^------|           /
 *                     \                /
 *                      \              \/
 *                        -------------
 *                        | SM_TEST_C |
 *                        -------------
 *
 * test_hierarchy_0: Tests a flat state machine without super states
 * test_hierarchy_1: Tests a hierarchical state machine with 1 super state
 * test_hierarchy_2: Tests a hierarchical state machine with 2 super states
 * test_hierarchy_3: Tests a hierarchical state machine with 3 super states
 *
 */

#define SEQUENCE_SIZE 55

enum state_id {
	ENTER_A1 = 1,
	RUN_A1,
	EXIT_A1,
	ENTER_A2,
	RUN_A2,
	EXIT_A2,
	ENTER_A3,
	RUN_A3,
	EXIT_A3,
	ENTER_A4,
	RUN_A4,
	EXIT_A4,
	ENTER_A5,
	RUN_A5,
	EXIT_A5,
	ENTER_A6,
	RUN_A6,
	EXIT_A6,
	ENTER_A7,
	RUN_A7,
	EXIT_A7,
	ENTER_B1,
	RUN_B1,
	EXIT_B1,
	ENTER_B2,
	RUN_B2,
	EXIT_B2,
	ENTER_B3,
	RUN_B3,
	EXIT_B3,
	ENTER_B4,
	RUN_B4,
	EXIT_B4,
	ENTER_B5,
	RUN_B5,
	EXIT_B5,
	ENTER_B6,
	RUN_B6,
	EXIT_B6,
	ENTER_C,
	RUN_C,
	EXIT_C,
};

#define PORT0   0

struct sm_ {
	/* struct sm_obj must be first */
	struct sm_ctx ctx;
	int sv_tmp;
	int idx;
	int seq[SEQUENCE_SIZE];
} sm[1];

enum state {
	SM_TEST_SUPER_A1,
	SM_TEST_SUPER_A2,
	SM_TEST_SUPER_A3,
	SM_TEST_SUPER_B1,
	SM_TEST_SUPER_B2,
	SM_TEST_SUPER_B3,
	SM_TEST_A4,
	SM_TEST_A5,
	SM_TEST_A6,
	SM_TEST_A7,
	SM_TEST_B4,
	SM_TEST_B5,
	SM_TEST_B6,
	SM_TEST_C,
};
static const struct usb_state states[];

static struct control {
	usb_state_ptr a3_entry_to;
	usb_state_ptr b3_run_to;
	usb_state_ptr b6_entry_to;
	usb_state_ptr c_entry_to;
	usb_state_ptr c_exit_to;
} test_control;

static void set_state_sm(const int port, const enum state new_state)
{
	set_state(port, &sm[port].ctx, &states[new_state]);
}

static void sm_test_super_A1_entry(const int port)
{
	sm[port].seq[sm[port].idx++] = ENTER_A1;
}

static void sm_test_super_A1_run(const int port)
{
	sm[port].seq[sm[port].idx++] = RUN_A1;
}

static void sm_test_super_A1_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A1;
}

static void sm_test_super_B1_entry(const int port)
{
	sm[port].seq[sm[port].idx++] = ENTER_B1;
}

static void sm_test_super_B1_run(const int port)
{
	sm[port].seq[sm[port].idx++] = RUN_B1;
}

static void sm_test_super_B1_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_B1;
}

static void sm_test_super_A2_entry(const int port)
{
	sm[port].seq[sm[port].idx++] = ENTER_A2;
}

static void sm_test_super_A2_run(const int port)
{
	sm[port].seq[sm[port].idx++] = RUN_A2;
}

static void sm_test_super_A2_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A2;
}


static void sm_test_super_B2_entry(const int port)
{
	sm[port].seq[sm[port].idx++] = ENTER_B2;
}

static void sm_test_super_B2_run(const int port)
{
	sm[port].seq[sm[port].idx++] = RUN_B2;
}

static void sm_test_super_B2_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_B2;
}

static void sm_test_super_A3_entry(const int port)
{
	sm[port].seq[sm[port].idx++] = ENTER_A3;
	if (test_control.a3_entry_to)
		set_state(port, &sm[port].ctx, test_control.a3_entry_to);
}

static void sm_test_super_A3_run(const int port)
{
	sm[port].seq[sm[port].idx++] = RUN_A3;
}

static void sm_test_super_A3_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A3;
}

static void sm_test_super_B3_entry(const int port)
{
	sm[port].seq[sm[port].idx++] = ENTER_B3;
}

static void sm_test_super_B3_run(const int port)
{
	sm[port].seq[sm[port].idx++] = RUN_B3;
	if (test_control.b3_run_to)
		set_state(port, &sm[port].ctx, test_control.b3_run_to);
}

static void sm_test_super_B3_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_B3;
}

static void sm_test_A4_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_A4;
}

static void sm_test_A4_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].sv_tmp = 1;
		sm[port].seq[sm[port].idx++] = RUN_A4;
	} else {
		set_state_sm(port, SM_TEST_B4);
	}
}

static void sm_test_A4_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A4;
}


static void sm_test_A5_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_A5;
}

static void sm_test_A5_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].sv_tmp = 1;
		sm[port].seq[sm[port].idx++] = RUN_A5;
	} else {
		set_state_sm(port, SM_TEST_A4);
	}
}

static void sm_test_A5_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A5;
}


static void sm_test_A6_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_A6;
}

static void sm_test_A6_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].sv_tmp = 1;
		sm[port].seq[sm[port].idx++] = RUN_A6;
	} else {
		set_state_sm(port, SM_TEST_A5);
	}
}

static void sm_test_A6_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A6;
}

static void sm_test_A7_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_A7;
}

static void sm_test_A7_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].sv_tmp = 1;
		sm[port].seq[sm[port].idx++] = RUN_A7;
	} else {
		set_state_sm(port, SM_TEST_A6);
	}
}

static void sm_test_A7_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_A7;
}

static void sm_test_B4_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_B4;
}

static void sm_test_B4_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].seq[sm[port].idx++] = RUN_B4;
		sm[port].sv_tmp = 1;
	} else {
		set_state_sm(port, SM_TEST_B5);
	}
}

static void sm_test_B4_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_B4;
}


static void sm_test_B5_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_B5;
}

static void sm_test_B5_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].sv_tmp = 1;
		sm[port].seq[sm[port].idx++] = RUN_B5;
	} else {
		set_state_sm(port, SM_TEST_B6);
	}
}

static void sm_test_B5_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_B5;
}


static void sm_test_B6_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_B6;
	if (test_control.b6_entry_to)
		set_state(port, &sm[port].ctx, test_control.b6_entry_to);
}

static void sm_test_B6_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].sv_tmp = 1;
		sm[port].seq[sm[port].idx++] = RUN_B6;
	} else {
		set_state_sm(port, SM_TEST_C);
	}
}

static void sm_test_B6_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_B6;
}

static void sm_test_C_entry(const int port)
{
	sm[port].sv_tmp = 0;
	sm[port].seq[sm[port].idx++] = ENTER_C;
	if (test_control.c_entry_to)
		set_state(port, &sm[port].ctx, test_control.c_entry_to);
}

static void sm_test_C_run(const int port)
{
	if (sm[port].sv_tmp == 0) {
		sm[port].seq[sm[port].idx++] = RUN_C;
		sm[port].sv_tmp = 1;
	} else {
		set_state_sm(port, SM_TEST_A7);
	}
}

static void sm_test_C_exit(const int port)
{
	sm[port].seq[sm[port].idx++] = EXIT_C;
	if (test_control.c_exit_to)
		set_state(port, &sm[port].ctx, test_control.c_exit_to);
}

static void run_sm(void)
{
	task_wake(TASK_ID_TEST);
	task_wait_event(5 * MSEC);
}

test_static int test_hierarchy_0(void)
{
	int port = PORT0;
	int i = 0;

	set_state_sm(port, SM_TEST_A4);

	run_sm();
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_C, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A7, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	for (; i < SEQUENCE_SIZE; i++)
		TEST_EQ(sm[port].seq[i], 0, "%d");

	return EC_SUCCESS;
}

test_static int test_hierarchy_1(void)
{
	int port = PORT0;
	int i = 0;

	set_state_sm(port, SM_TEST_A4);

	run_sm();
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A3, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B3, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_C, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A7, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A3, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	for (i = 33; i < SEQUENCE_SIZE; i++)
		TEST_EQ(sm[port].seq[i], 0, "%d");

	return EC_SUCCESS;
}

test_static int test_hierarchy_2(void)
{

	int port = PORT0;
	int i = 0;

	set_state_sm(port, SM_TEST_A4);

	run_sm();
	TEST_EQ(sm[port].seq[i], ENTER_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A2, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B2, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B2, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_C, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A7, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A2, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A2, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	for (; i < SEQUENCE_SIZE; i++)
		TEST_EQ(sm[port].seq[i], 0, "%d");

	return EC_SUCCESS;
}

test_static int test_hierarchy_3(void)
{

	int port = PORT0;
	int i = 0;

	set_state_sm(port, SM_TEST_A4);

	run_sm();
	TEST_EQ(sm[port].seq[i], ENTER_A1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B4, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_C, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_C, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A7, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A7, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A7, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A6, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A5, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_A1, "%d"); ++i;

	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_A5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A4, "%d"); ++i;

	for (; i < SEQUENCE_SIZE; i++)
		TEST_EQ(sm[port].seq[i], 0, "%d");

	return EC_SUCCESS;
}

test_static int test_set_state_from_parents(void)
{
	int port = PORT0;
	int i = 0;

	/* Start state machine */
	test_control.a3_entry_to = &states[SM_TEST_B4];
	run_sm();
	set_state_sm(port, SM_TEST_A4);
	TEST_EQ(sm[port].seq[i], ENTER_A1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A3, "%d"); ++i;
	/* Does not enter or exit A4 */
	TEST_EQ(sm[port].seq[i], EXIT_A3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_A1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B4, "%d"); ++i;
	/* Ensure we didn't go further than above statements */
	TEST_EQ(sm[port].seq[i], 0, "%d");

	test_control.b3_run_to = &states[SM_TEST_B5];
	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B3, "%d"); ++i;
	/* Does not run b2 or b1 */
	TEST_EQ(sm[port].seq[i], EXIT_B4, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B3, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B5, "%d"); ++i;
	/* Ensure we didn't go further than above statements */
	TEST_EQ(sm[port].seq[i], 0, "%d");

	run_sm();
	TEST_EQ(sm[port].seq[i], RUN_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], RUN_B1, "%d"); ++i;
	/* Ensure we didn't go further than above statements */
	TEST_EQ(sm[port].seq[i], 0, "%d");

	/*
	 * Ensure that multiple chains of parent entry works. Also ensure
	 * that set states in exit are ignored.
	 */
	test_control.b6_entry_to = &states[SM_TEST_C];
	test_control.c_entry_to = &states[SM_TEST_A7];
	test_control.c_exit_to = &states[SM_TEST_A4];
	run_sm();
	TEST_EQ(sm[port].seq[i], EXIT_B5, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B2, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B6, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_B1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_C, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], EXIT_C, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A1, "%d"); ++i;
	TEST_EQ(sm[port].seq[i], ENTER_A7, "%d"); ++i;
	/* Ensure we didn't go further than above statements */
	TEST_EQ(sm[port].seq[i], 0, "%d");

	for (; i < SEQUENCE_SIZE; i++)
		TEST_EQ(sm[port].seq[i], 0, "%d");

	return EC_SUCCESS;
}

#ifdef TEST_USB_SM_FRAMEWORK_H3
#define TEST_AT_LEAST_3
#endif

#if defined(TEST_AT_LEAST_3) || defined(TEST_USB_SM_FRAMEWORK_H2)
#define TEST_AT_LEAST_2
#endif

#if defined(TEST_AT_LEAST_2) || defined(TEST_USB_SM_FRAMEWORK_H1)
#define TEST_AT_LEAST_1
#endif

static const struct usb_state states[] = {
	[SM_TEST_SUPER_A1] = {
		.entry  = sm_test_super_A1_entry,
		.run    = sm_test_super_A1_run,
		.exit   = sm_test_super_A1_exit,
	},
	[SM_TEST_SUPER_A2] = {
		.entry  = sm_test_super_A2_entry,
		.run    = sm_test_super_A2_run,
		.exit   = sm_test_super_A2_exit,
#ifdef TEST_AT_LEAST_3
		.parent = &states[SM_TEST_SUPER_A1],
#endif
	},
	[SM_TEST_SUPER_A3] = {
		.entry  = sm_test_super_A3_entry,
		.run    = sm_test_super_A3_run,
		.exit   = sm_test_super_A3_exit,
#ifdef TEST_AT_LEAST_2
		.parent = &states[SM_TEST_SUPER_A2],
#endif
	},
	[SM_TEST_SUPER_B1] = {
		.entry  = sm_test_super_B1_entry,
		.run    = sm_test_super_B1_run,
		.exit   = sm_test_super_B1_exit,
	},
	[SM_TEST_SUPER_B2] = {
		.entry  = sm_test_super_B2_entry,
		.run    = sm_test_super_B2_run,
		.exit   = sm_test_super_B2_exit,
#ifdef TEST_AT_LEAST_3
		.parent = &states[SM_TEST_SUPER_B1],
#endif
	},
	[SM_TEST_SUPER_B3] = {
		.entry  = sm_test_super_B3_entry,
		.run    = sm_test_super_B3_run,
		.exit   = sm_test_super_B3_exit,
#ifdef TEST_AT_LEAST_2
		.parent = &states[SM_TEST_SUPER_B2],
#endif
	},
	[SM_TEST_A4] = {
		.entry  = sm_test_A4_entry,
		.run    = sm_test_A4_run,
		.exit   = sm_test_A4_exit,
#ifdef TEST_AT_LEAST_1
		.parent = &states[SM_TEST_SUPER_A3],
#endif
	},
	[SM_TEST_A5] = {
		.entry  = sm_test_A5_entry,
		.run    = sm_test_A5_run,
		.exit   = sm_test_A5_exit,
#ifdef TEST_AT_LEAST_1
		.parent = &states[SM_TEST_SUPER_A3],
#endif
	},
	[SM_TEST_A6] = {
		.entry  = sm_test_A6_entry,
		.run    = sm_test_A6_run,
		.exit   = sm_test_A6_exit,
#ifdef TEST_AT_LEAST_2
		.parent = &states[SM_TEST_SUPER_A2],
#endif
	},
	[SM_TEST_A7] = {
		.entry  = sm_test_A7_entry,
		.run    = sm_test_A7_run,
		.exit   = sm_test_A7_exit,
#ifdef TEST_AT_LEAST_3
		.parent = &states[SM_TEST_SUPER_A1],
#endif
	},
	[SM_TEST_B4] = {
		.entry  = sm_test_B4_entry,
		.run    = sm_test_B4_run,
		.exit   = sm_test_B4_exit,
#ifdef TEST_AT_LEAST_1
		.parent = &states[SM_TEST_SUPER_B3],
#endif
	},
	[SM_TEST_B5] = {
		.entry  = sm_test_B5_entry,
		.run    = sm_test_B5_run,
		.exit   = sm_test_B5_exit,
#ifdef TEST_AT_LEAST_2
		.parent = &states[SM_TEST_SUPER_B2],
#endif
	},
	[SM_TEST_B6] = {
		.entry  = sm_test_B6_entry,
		.run    = sm_test_B6_run,
		.exit   = sm_test_B6_exit,
#ifdef TEST_AT_LEAST_3
		.parent = &states[SM_TEST_SUPER_B1],
#endif
	},
	[SM_TEST_C] = {
		.entry  = sm_test_C_entry,
		.run    = sm_test_C_run,
		.exit   = sm_test_C_exit,
	},
};

/* Run before each RUN_TEST line */
void before_test(void)
{
	/* Rest test variables */
	memset(&sm[PORT0], 0, sizeof(struct sm_));
	memset(&test_control, 0, sizeof(struct control));
}

int test_task(void *u)
{
	int port = PORT0;

	while (1) {
		/* wait for next event/packet or timeout expiration */
		task_wait_event(-1);
		/* run state machine */
		run_state(port, &sm[port].ctx);
	}

	return EC_SUCCESS;
}

void run_test(void)
{
	test_reset();
#if defined(TEST_USB_SM_FRAMEWORK_H3)
	RUN_TEST(test_hierarchy_3);
	RUN_TEST(test_set_state_from_parents);
#elif defined(TEST_USB_SM_FRAMEWORK_H2)
	RUN_TEST(test_hierarchy_2);
#elif defined(TEST_USB_SM_FRAMEWORK_H1)
	RUN_TEST(test_hierarchy_1);
#else
	RUN_TEST(test_hierarchy_0);
#endif
	test_print_result();
}
