diff --git a/test/ic3/ratio.in b/test/ic3/ratio.in
index ed2b89e..472e03d 100644
--- a/test/ic3/ratio.in
+++ b/test/ic3/ratio.in
@@ -36,3 +36,15 @@ quote 2/3 * 5/6
2/3 * 5/6
quote 2/3 / 5/6
2/3 / 5/6
+quote 1/2 > 1/3
+1/2 > 1/3
+quote 1/2 < 1/3
+1/2 < 1/3
+quote 1/2 >= 1/2
+1/2 >= 1/2
+quote 1/2 <= 1/2
+1/2 <= 1/2
+quote 1/2 == 1/2
+1/2 == 1/2
+quote 1/2 != 1/3
+1/2 != 1/3
diff --git a/test/ic3/ratio.out.expected b/test/ic3/ratio.out.expected
index 56b493e..274d93f 100644
--- a/test/ic3/ratio.out.expected
+++ b/test/ic3/ratio.out.expected
@@ -36,3 +36,15 @@
5/9
2/3 / 5/6
4/5
+1/2 > 1/3
+true
+1/2 < 1/3
+false
+1/2 >= 1/2
+true
+1/2 <= 1/2
+true
+1/2 == 1/2
+true
+1/2 != 1/3
+true
diff --git a/test/ratio_test.c b/test/ratio_test.c
new file mode 100644
index 0000000..11018cf
--- /dev/null
+++ b/test/ratio_test.c
@@ -0,0 +1,96 @@
+/* c3
+ * Copyright 2022,2023 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software granted the above
+ * copyright notice and this permission paragraph are included in all
+ * copies and substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "compare_test.h"
+#include "../libc3/str.h"
+#include "../libc3/buf.h"
+#include "../libc3/buf_parse.h"
+#include "../libc3/integer.h"
+#include "../libc3/ratio.h"
+#include "test.h"
+
+#define RATIO_TEST_INSPECT(test_numerator, test_denominator, expected_string) \
+do { \
+ s_ratio ratio; \
+ s_str result; \
+ integer_init_u64(&ratio.numerator, test_numerator); \
+ integer_init_u64(&ratio.denominator, test_denominator); \
+ char context[100]; \
+ sprintf(context, "ratio_inspect(%d/%d) -> expected: %s", test_numerator, test_denominator, expected_string); \
+ test_context(context); \
+ TEST_EQ(ratio_inspect(&ratio, &result), &result); \
+ TEST_STRNCMP(result.ptr.p, expected_string, result.size); \
+ ratio_clean(&ratio); \
+ str_clean(&result); \
+ test_context(NULL); \
+} while (0)
+
+#define RATIO_TEST_PARSE(test_string, expected_numerator, expected_denominator) \
+do { \
+ sw result_num; \
+ sw result_den; \
+ s_buf buf; \
+ s_ratio ratio; \
+ s_str str; \
+ str_init_1(&str, NULL, test_string); \
+ char context[100]; \
+ sprintf(context, "buf_parse_ratio(%s) -> expected: %d/%d", test_string, expected_numerator, expected_denominator); \
+ test_context(context); \
+ buf_init_str(&buf, false, &str); \
+ TEST_ASSERT(buf_parse_ratio(&buf, &ratio) > 0); \
+ result_num = integer_to_sw(&ratio.numerator); \
+ result_den = integer_to_sw(&ratio.denominator); \
+ TEST_EQ(result_num, expected_numerator); \
+ TEST_EQ(result_den, expected_denominator); \
+ str_clean(&str); \
+ ratio_clean(&ratio); \
+ test_context(NULL); \
+} while (0)
+
+TEST_CASE_PROTOTYPE(ratio_inspect);
+TEST_CASE_PROTOTYPE(ratio_parse);
+
+void ratio_test (void)
+{
+ TEST_CASE_RUN(ratio_inspect);
+ TEST_CASE_RUN(ratio_parse);
+}
+
+TEST_CASE(ratio_inspect)
+{
+ RATIO_TEST_INSPECT(0, 1, "0/1");
+ RATIO_TEST_INSPECT(1, 1, "1/1");
+ RATIO_TEST_INSPECT(1, 10, "1/10");
+ RATIO_TEST_INSPECT(1, 100, "1/100");
+ RATIO_TEST_INSPECT(1, 1000, "1/1000");
+ RATIO_TEST_INSPECT(10, 1, "10/1");
+ RATIO_TEST_INSPECT(100, 1, "100/1");
+ RATIO_TEST_INSPECT(1000, 1, "1000/1");
+}
+TEST_CASE_END(ratio_inspect)
+
+TEST_CASE(ratio_parse)
+{
+ RATIO_TEST_PARSE("0/1", 0, 1);
+ RATIO_TEST_PARSE("1/1", 1, 1);
+ RATIO_TEST_PARSE("1/10", 1, 10);
+ RATIO_TEST_PARSE("1/100", 1, 100);
+ RATIO_TEST_PARSE("1/1000", 1, 1000);
+ RATIO_TEST_PARSE("10/1", 10, 1);
+ RATIO_TEST_PARSE("100/1", 100, 1);
+ RATIO_TEST_PARSE("1000/1", 1000, 1);
+}
+TEST_CASE_END(ratio_parse)
\ No newline at end of file