1#include "sch/tests/test_internal.h"
2
3TEST_GROUP_DEFINE(climb, .intensity_desc = {
4 .curve = SCALE_PIECEWISE_LOG,
5 .unit = "steps",
6 });
7
8TEST_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
32static 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
41static 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 */
47TEST_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
69TEST_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
98TEST_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