/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC) * SPDX-License-Identifier: GPL-3.0-or-later */ #include "emu/mux.h" #include "common.h" #include "unittest.h" #define N 10 static void check_output(struct mux *mux, struct value expected) { struct value out_value = value_null(); OK(chan_read(mux->output, &out_value)); if (!value_is_equal(&out_value, &expected)) { die("unexpected value found %s in output (expected %s)", value_str(out_value), value_str(expected)); } } static void test_select(struct mux *mux, int key) { OK(chan_set(mux->select, value_int64(key))); OK(bay_propagate(mux->bay)); check_output(mux, value_int64(1000 + key)); err("OK"); } static void test_input(struct mux *mux, int key) { /* Set the select channel to the selected key */ test_select(mux, key); int new_value = 2000 + key; /* Then change that channel */ struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d", key); OK(chan_set(mi->chan, value_int64(new_value))); OK(bay_propagate(mux->bay)); check_output(mux, value_int64(new_value)); err("OK"); } static void test_select_and_input(struct mux *mux, int key) { /* Set the select channel to the selected key, but don't * propagate the changes yet */ OK(chan_set(mux->select, value_int64(key))); int new_value = 2000 + key; /* Also change that channel */ struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d", key); OK(chan_set(mi->chan, value_int64(new_value))); /* Write twice to the output */ OK(bay_propagate(mux->bay)); check_output(mux, value_int64(new_value)); err("OK"); } static void test_input_and_select(struct mux *mux, int key) { int new_value = 2000 + key; /* First change the input */ struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d", key); OK(chan_set(mi->chan, value_int64(new_value))); /* Then change select */ OK(chan_set(mux->select, value_int64(key))); /* Write twice to the output */ OK(bay_propagate(mux->bay)); check_output(mux, value_int64(new_value)); err("OK"); } static void test_mid_propagate(struct mux *mux, int key) { int new_value = 2000 + key; struct mux_input *mi = mux_get_input(mux, key); if (mi == NULL) die("mux_get_input failed to locate input %d", key); OK(chan_set(mi->chan, value_int64(new_value))); OK(bay_propagate(mux->bay)); OK(chan_set(mux->select, value_int64(key))); OK(bay_propagate(mux->bay)); check_output(mux, value_int64(new_value)); err("OK"); } static void test_duplicate_output(struct mux *mux, int key1, int key2) { int new_value = 2000 + key1; struct mux_input *in1 = mux_get_input(mux, key1); if (in1 == NULL) die("mux_get_input failed to locate input1 %d", key1); struct mux_input *in2 = mux_get_input(mux, key2); if (in2 == NULL) die("mux_get_input failed to locate input2 %d", key2); OK(chan_set(in1->chan, value_int64(new_value))); OK(chan_set(in2->chan, value_int64(new_value))); /* Select input 1 */ OK(chan_set(mux->select, value_int64(key1))); OK(bay_propagate(mux->bay)); check_output(mux, value_int64(new_value)); /* Now switch to input 2, which has the same value */ OK(chan_set(mux->select, value_int64(key2))); OK(bay_propagate(mux->bay)); check_output(mux, value_int64(new_value)); err("OK"); } /* Ensure that the output of a mux is correct while the mux is connected * to the bay with a clean select channel but that already contains a * valid value of a input of the mux. The select must be marked as dirty * */ static void test_delayed_connect(void) { struct bay bay; bay_init(&bay); struct chan input, output, select; chan_init(&output, CHAN_SINGLE, "output"); chan_init(&select, CHAN_SINGLE, "select"); chan_init(&input, CHAN_SINGLE, "input.0"); /* Register all channels in the bay */ OK(bay_register(&bay, &select)); OK(bay_register(&bay, &output)); OK(bay_register(&bay, &input)); /* Setup channel values */ OK(chan_set(&select, value_int64(0))); OK(chan_set(&input, value_int64(1000))); /* Propagate now so they are clean */ OK(bay_propagate(&bay)); /* ----- delayed connect ----- */ struct mux mux; OK(mux_init(&mux, &bay, &select, &output, NULL, 1)); OK(mux_set_input(&mux, 0, &input)); /* Don't modify the input of the select until propagation, the * mux_init must have marked the select as dirty. */ OK(bay_propagate(&bay)); /* The mux must have selected the first input */ check_output(&mux, value_int64(1000)); err("OK"); } int main(void) { struct bay bay; bay_init(&bay); struct chan inputs[N]; struct chan output; struct chan select; chan_init(&output, CHAN_SINGLE, "output"); chan_init(&select, CHAN_SINGLE, "select"); for (int i = 0; i < N; i++) { char buf[MAX_CHAN_NAME]; sprintf(buf, "input.%d", i); chan_init(&inputs[i], CHAN_SINGLE, buf); } /* Register all channels in the bay */ OK(bay_register(&bay, &select)); for (int i = 0; i < N; i++) OK(bay_register(&bay, &inputs[i])); struct mux mux; /* Attempt to init the mux without registering the output */ ERR(mux_init(&mux, &bay, &select, &output, NULL, N)); OK(bay_register(&bay, &output)); OK(mux_init(&mux, &bay, &select, &output, NULL, N)); for (int i = 0; i < N; i++) OK(mux_set_input(&mux, i, &inputs[i])); /* Write something to the input channels */ for (int i = 0; i < N; i++) OK(chan_set(&inputs[i], value_int64(1000 + i))); /* Propagate values and call the callbacks */ OK(bay_propagate(&bay)); test_select(&mux, 1); test_input(&mux, 2); test_select_and_input(&mux, 3); test_input_and_select(&mux, 4); test_mid_propagate(&mux, 5); test_duplicate_output(&mux, 6, 7); test_delayed_connect(); err("OK"); return 0; }