| 1 | #include "sch/tests/test_internal.h" |
| 2 | |
| 3 | TEST_GROUP_DEFINE(climb, .intensity_desc = { |
| 4 | .curve = SCALE_PIECEWISE_LOG, |
| 5 | .unit = "steps" , |
| 6 | }); |
| 7 | |
| 8 | TEST_DEFINE_UNIT(climb, pressure_cubic_curve, TEST_INTENSITY(20, 100, 1000)) { |
| 9 | /* Pressure p = 0 -> boost target = 0 */ |
| 10 | TEST_ASSERT_EQ_S(TEST_CALL(climb_pressure_to_boost_target)(0), 0); |
| 11 | |
| 12 | size_t steps = ctx->intensity_val ? ctx->intensity_val : 100; |
| 13 | |
| 14 | /* monotonicity: target(p_{i+1}) >= target(p_i) */ |
| 15 | int32_t prev_target = 0; |
| 16 | for (size_t i = 0; i <= steps; i++) { |
| 17 | climb_pressure_t p = fx_div(a: fx_from_int(x: i), b: fx_from_int(x: steps)); |
| 18 | int32_t target = TEST_CALL(climb_pressure_to_boost_target)(p); |
| 19 | TEST_ASSERT_GE_S(target, prev_target); |
| 20 | TEST_ASSERT_LE_S(target, CLIMB_BOOST_LEVEL_MAX); |
| 21 | prev_target = target; |
| 22 | } |
| 23 | |
| 24 | /* Max pressure saturates at CLIMB_BOOST_LEVEL_MAX (20) */ |
| 25 | TEST_ASSERT_EQ_S( |
| 26 | TEST_CALL(climb_pressure_to_boost_target)(CLIMB_PRESSURE_MAX), |
| 27 | CLIMB_BOOST_LEVEL_MAX); |
| 28 | |
| 29 | return TEST_SUCCESS; |
| 30 | } |
| 31 | |
| 32 | static struct climb_thread_data climb_keyed(int32_t periods, |
| 33 | climb_pressure_t direct) { |
| 34 | struct climb_thread_data cts = {0}; |
| 35 | cts.pressure_periods = periods; |
| 36 | cts.direct_pressure = direct; |
| 37 | rbt_init_node(n: &cts.climb_node); |
| 38 | return cts; |
| 39 | } |
| 40 | |
| 41 | static int32_t climb_cmp(struct climb_thread_data *a, |
| 42 | struct climb_thread_data *b) { |
| 43 | return climb_cmp_threads(a: &a->climb_node, b: &b->climb_node); |
| 44 | } |
| 45 | |
| 46 | /* climb_pressure_t is 32.32 */ |
| 47 | TEST_DEFINE_UNIT(climb, tree_key_orders_by_pressure, |
| 48 | TEST_INTENSITY(8, 64, 4096)) { |
| 49 | size_t steps = ctx->intensity_val ? ctx->intensity_val : 64; |
| 50 | |
| 51 | struct climb_thread_data prev = climb_keyed(periods: 1, direct: 0); |
| 52 | for (size_t i = 1; i <= steps; i++) { |
| 53 | climb_pressure_t p = fx_div(a: fx_from_int(x: i), b: fx_from_int(x: steps)); |
| 54 | struct climb_thread_data curr = climb_keyed(periods: 1, direct: p); |
| 55 | |
| 56 | TEST_ASSERT_LT_S(climb_cmp(&prev, &curr), 0); |
| 57 | TEST_ASSERT_GT_S(climb_cmp(&curr, &prev), 0); |
| 58 | prev = curr; |
| 59 | } |
| 60 | |
| 61 | /* the two values that used to collide */ |
| 62 | struct climb_thread_data zero = climb_keyed(periods: 1, direct: 0); |
| 63 | struct climb_thread_data full = climb_keyed(periods: 1, CLIMB_PRESSURE_MAX); |
| 64 | TEST_ASSERT_LT_S(climb_cmp(&zero, &full), 0); |
| 65 | |
| 66 | return TEST_SUCCESS; |
| 67 | } |
| 68 | |
| 69 | TEST_DEFINE_UNIT(climb, tree_key_comparator_is_consistent, |
| 70 | TEST_INTENSITY(4, 32, 512)) { |
| 71 | size_t steps = ctx->intensity_val ? ctx->intensity_val : 32; |
| 72 | |
| 73 | for (size_t i = 0; i <= steps; i++) { |
| 74 | for (size_t j = 0; j <= steps; j++) { |
| 75 | struct climb_thread_data a = climb_keyed( |
| 76 | periods: (int32_t) i - 2, direct: fx_div(a: fx_from_int(x: i), b: fx_from_int(x: steps))); |
| 77 | struct climb_thread_data b = climb_keyed( |
| 78 | periods: (int32_t) j - 2, direct: fx_div(a: fx_from_int(x: j), b: fx_from_int(x: steps))); |
| 79 | |
| 80 | int32_t ab = climb_cmp(a: &a, b: &b); |
| 81 | int32_t ba = climb_cmp(a: &b, b: &a); |
| 82 | |
| 83 | if (ab == 0) |
| 84 | TEST_ASSERT_EQ_S(ba, 0); |
| 85 | else |
| 86 | TEST_ASSERT_LT_S((int64_t) ab * (int64_t) ba, 0); |
| 87 | |
| 88 | size_t da = climb_get_thread_data(n: &a.climb_node); |
| 89 | size_t db = climb_get_thread_data(n: &b.climb_node); |
| 90 | TEST_ASSERT_EQ_S(ab < 0, da < db); |
| 91 | TEST_ASSERT_EQ_S(ab > 0, da > db); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | return TEST_SUCCESS; |
| 96 | } |
| 97 | |
| 98 | TEST_DEFINE_UNIT(climb, tree_key_trades_periods_against_pressure) { |
| 99 | struct climb_thread_data waited = climb_keyed(periods: 3, direct: 0); |
| 100 | struct climb_thread_data pressured = climb_keyed(periods: 1, CLIMB_PRESSURE_MAX); |
| 101 | |
| 102 | /* two extra periods == one full pressure point */ |
| 103 | TEST_ASSERT_EQ_S(climb_cmp(&waited, &pressured), 0); |
| 104 | |
| 105 | /* one more period breaks the tie towards the thread that waited */ |
| 106 | struct climb_thread_data waited_longer = climb_keyed(periods: 4, direct: 0); |
| 107 | TEST_ASSERT_GT_S(climb_cmp(&waited_longer, &pressured), 0); |
| 108 | |
| 109 | struct climb_thread_data decaying = climb_keyed(periods: -1, CLIMB_PRESSURE_MAX); |
| 110 | struct climb_thread_data active = climb_keyed(periods: 1, CLIMB_PRESSURE_MAX); |
| 111 | TEST_ASSERT_LT_S(climb_cmp(&decaying, &active), 0); |
| 112 | |
| 113 | struct climb_thread_data fresh = climb_keyed(periods: 1, direct: 0); |
| 114 | TEST_ASSERT_EQ_S(climb_cmp(&decaying, &fresh), 0); |
| 115 | |
| 116 | return TEST_SUCCESS; |
| 117 | } |
| 118 | |