fzy

terminal fuzzy finder picker

git clone https://9o.is/git/fzy.git

test_properties.c

(3914B)


      1 #define _DEFAULT_SOURCE
      2 #include <string.h>
      3 
      4 #include "greatest/greatest.h"
      5 #include "theft/theft.h"
      6 
      7 #include "match.h"
      8 
      9 static void *string_alloc_cb(struct theft *t, theft_hash seed, void *env) {
     10 	(void)env;
     11 	int limit = 128;
     12 
     13 	size_t sz = (size_t)(seed % limit) + 1;
     14 	char *str = malloc(sz + 1);
     15 	if (str == NULL) {
     16 		return THEFT_ERROR;
     17 	}
     18 
     19 	for (size_t i = 0; i < sz; i += sizeof(theft_hash)) {
     20 		theft_hash s = theft_random(t);
     21 		for (uint8_t b = 0; b < sizeof(theft_hash); b++) {
     22 			if (i + b >= sz) {
     23 				break;
     24 			}
     25 			str[i + b] = (uint8_t)(s >> (8 * b)) & 0xff;
     26 		}
     27 	}
     28 	str[sz] = 0;
     29 
     30 	return str;
     31 }
     32 
     33 static void string_free_cb(void *instance, void *env) {
     34 	free(instance);
     35 	(void)env;
     36 }
     37 
     38 static void string_print_cb(FILE *f, void *instance, void *env) {
     39 	char *str = (char *)instance;
     40 	(void)env;
     41 	size_t size = strlen(str);
     42 	fprintf(f, "str[%zd]:\n    ", size);
     43 	uint8_t bytes = 0;
     44 	for (size_t i = 0; i < size; i++) {
     45 		fprintf(f, "%02x", str[i]);
     46 		bytes++;
     47 		if (bytes == 16) {
     48 			fprintf(f, "\n    ");
     49 			bytes = 0;
     50 		}
     51 	}
     52 	fprintf(f, "\n");
     53 }
     54 
     55 static uint64_t string_hash_cb(void *instance, void *env) {
     56 	(void)env;
     57 	char *str = (char *)instance;
     58 	int size = strlen(str);
     59 	return theft_hash_onepass((uint8_t *)str, size);
     60 }
     61 
     62 static void *string_shrink_cb(void *instance, uint32_t tactic, void *env) {
     63 	(void)env;
     64 	char *str = (char *)instance;
     65 	int n = strlen(str);
     66 
     67 	if (tactic == 0) { /* first half */
     68 		return strndup(str, n / 2);
     69 	} else if (tactic == 1) { /* second half */
     70 		return strndup(str + (n / 2), n / 2);
     71 	} else {
     72 		return THEFT_NO_MORE_TACTICS;
     73 	}
     74 }
     75 
     76 static struct theft_type_info string_info = {
     77     .alloc = string_alloc_cb,
     78     .free = string_free_cb,
     79     .print = string_print_cb,
     80     .hash = string_hash_cb,
     81     .shrink = string_shrink_cb,
     82 };
     83 
     84 static theft_trial_res prop_should_return_results_if_there_is_a_match(char *needle,
     85 								      char *haystack) {
     86 	int match_exists = has_match(needle, haystack);
     87 	if (!match_exists)
     88 		return THEFT_TRIAL_SKIP;
     89 
     90 	score_t score = match(needle, haystack);
     91 
     92 	if (needle[0] == '\0')
     93 		return THEFT_TRIAL_SKIP;
     94 
     95 	if (score == SCORE_MIN)
     96 		return THEFT_TRIAL_FAIL;
     97 
     98 	return THEFT_TRIAL_PASS;
     99 }
    100 
    101 TEST should_return_results_if_there_is_a_match() {
    102 	struct theft *t = theft_init(0);
    103 	struct theft_cfg cfg = {
    104 	    .name = __func__,
    105 	    .fun = prop_should_return_results_if_there_is_a_match,
    106 	    .type_info = {&string_info, &string_info},
    107 	    .trials = 100000,
    108 	};
    109 
    110 	theft_run_res res = theft_run(t, &cfg);
    111 	theft_free(t);
    112 	GREATEST_ASSERT_EQm("should_return_results_if_there_is_a_match", THEFT_RUN_PASS, res);
    113 	PASS();
    114 }
    115 
    116 static theft_trial_res prop_positions_should_match_characters_in_string(char *needle,
    117 									char *haystack) {
    118 	int match_exists = has_match(needle, haystack);
    119 	if (!match_exists)
    120 		return THEFT_TRIAL_SKIP;
    121 
    122 	int n = strlen(needle);
    123 	size_t *positions = calloc(n, sizeof(size_t));
    124 	if (!positions)
    125 		return THEFT_TRIAL_ERROR;
    126 
    127 	match_positions(needle, haystack, positions);
    128 
    129 	/* Must be increasing */
    130 	for (int i = 1; i < n; i++) {
    131 		if (positions[i] <= positions[i - 1]) {
    132 			return THEFT_TRIAL_FAIL;
    133 		}
    134 	}
    135 
    136 	/* Matching characters must be in returned positions */
    137 	for (int i = 0; i < n; i++) {
    138 		if (toupper(needle[i]) != toupper(haystack[positions[i]])) {
    139 			return THEFT_TRIAL_FAIL;
    140 		}
    141 	}
    142 
    143 	free(positions);
    144 	return THEFT_TRIAL_PASS;
    145 }
    146 
    147 TEST positions_should_match_characters_in_string() {
    148 	struct theft *t = theft_init(0);
    149 	struct theft_cfg cfg = {
    150 	    .name = __func__,
    151 	    .fun = prop_positions_should_match_characters_in_string,
    152 	    .type_info = {&string_info, &string_info},
    153 	    .trials = 100000,
    154 	};
    155 
    156 	theft_run_res res = theft_run(t, &cfg);
    157 	theft_free(t);
    158 	GREATEST_ASSERT_EQm("should_return_results_if_there_is_a_match", THEFT_RUN_PASS, res);
    159 	PASS();
    160 }
    161 
    162 SUITE(properties_suite) {
    163 	RUN_TEST(should_return_results_if_there_is_a_match);
    164 	RUN_TEST(positions_should_match_characters_in_string);
    165 }