END_TEST START_TEST(Read_valid_string) { static const struct { const char* data; const char* expected; } strings[] = { { "\"\"", "" }, { "\" \"", " " }, { "\" \"", " " }, { "\"\\\"\"", "\"" }, { "\"\\\\\"", "\\" }, { "\"\\/\"", "/" }, { "\"/\"", "/" }, /* { "\"\\b\"", "\b" }, { "\"\\f\"", "\f" }, { "\"\\n\"", "\n" }, { "\"\\r\"", "\r" }, { "\"\\t\"", "\t" }, */ { "\"abc def\"", "abc def" }, }; for (size_t i = 0; i < arr_size(strings); ++i) { char data[128] = ""; sprintf(data, "%s x", strings[i].data); Streader* sr = init_with_cstr(data); char result[128] = "zzz"; fail_if(!Streader_read_string(sr, 128, result), "Could not read string `%s`: %s", data, Streader_get_error_desc(sr)); fail_if(strcmp(result, strings[i].expected) != 0, "Streader stored `%s` instead of `%s`", result, strings[i].expected); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume string `%s` correctly"); } }
bool Streader_read_dict(Streader* sr, Dict_item_reader ir, void* userdata) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; // Check opening brace if (!(Streader_skip_whitespace(sr) && Streader_match_char(sr, '{'))) { Streader_set_error(sr, "Expected a dictionary opening brace"); return false; } // Check empty dictionary if (Streader_try_match_char(sr, '}')) return true; if (ir == NULL) { Streader_set_error(sr, "Expected an empty dictionary"); return false; } // Read key/value pairs do { char key[STREADER_DICT_KEY_LENGTH_MAX + 1] = ""; if (!(Streader_read_string(sr, STREADER_DICT_KEY_LENGTH_MAX + 1, key) && Streader_match_char(sr, ':') && ir(sr, key, userdata))) return false; } while (Streader_try_match_char(sr, ',')); // Check closing brace if (!Streader_match_char(sr, '}')) { Streader_set_error(sr, "Expected a dictionary closing brace"); return false; } return true; }
static bool read_expressions_def(Streader* sr, const char* key, void* userdata) { rassert(sr != NULL); rassert(key != NULL); rassert(userdata != NULL); Au_expressions* ae = userdata; if (string_eq(key, "default_note_expr")) { char expr[KQT_VAR_NAME_MAX + 1] = ""; if (!Streader_read_string(sr, KQT_VAR_NAME_MAX + 1, expr)) return false; if ((expr[0] != '\0') && !is_valid_var_name(expr)) { Streader_set_error(sr, "Invalid default note expression: %s", expr); return false; } strcpy(ae->default_note_expr, expr); } else if (string_eq(key, "expressions")) { if (!Streader_read_dict(sr, read_expressions, ae)) { rassert(Streader_is_error_set(sr)); return false; } } else { Streader_set_error(sr, "Unexpected key in expression specification: %s", key); return false; } return true; }
END_TEST START_TEST(Reading_invalid_string_fails) { const char* data[] = { "abc\"", "\"abc", "abc", "\"\\z\"", "\"\n\"", }; for (size_t i = 0; i < arr_size(data); ++i) { Streader* sr = init_with_cstr(data[i]); char str[128] = ""; fail_if(Streader_read_string(sr, 128, str), "Streader accepted `%s` as a valid string", data[i]); } }
bool read_received_events_bind(Streader* sr, int32_t index, void* userdata) { assert(sr != NULL); (void)index; assert(userdata != NULL); int32_t* expected = userdata; double actual = NAN; if (index == 0 && *expected == 0) { return Streader_readf(sr, "[0, [") && Streader_match_string(sr, "#") && Streader_match_char(sr, ',') && Streader_read_string(sr, 0, NULL) && Streader_readf(sr, "]]"); } if (!(Streader_readf(sr, "[0, [") && Streader_match_string(sr, "n+") && Streader_readf(sr, ", %f]]", &actual)) ) return false; if ((int)round(actual) != *expected) { Streader_set_error( sr, "Received argument %.0f instead of %" PRId32, actual, *expected); return false; } *expected = (int)round(actual) + 1; return true; }
bool Streader_readf(Streader* sr, const char* format, ...) { rassert(sr != NULL); rassert(format != NULL); va_list args; va_start(args, format); while (!Streader_is_error_set(sr) && *format != '\0') { if (*format == '%') { // Conversion characters ++format; switch (*format) { case 'n': { Streader_read_null(sr); } break; case 'b': { bool* dest = va_arg(args, bool*); Streader_read_bool(sr, dest); } break; case 'i': { int64_t* dest = va_arg(args, int64_t*); Streader_read_int(sr, dest); } break; case 'f': { double* dest = va_arg(args, double*); Streader_read_float(sr, dest); } break; case 's': { Streader_readf_str_info info = va_arg(args, Streader_readf_str_info); rassert(info.guard == Streader_readf_str_guard); const int64_t max_bytes = info.max_bytes; char* dest = info.dest; Streader_read_string(sr, max_bytes, dest); } break; case 't': { Tstamp* dest = va_arg(args, Tstamp*); Streader_read_tstamp(sr, dest); } break; case 'p': { Pat_inst_ref* dest = va_arg(args, Pat_inst_ref*); Streader_read_piref(sr, dest); } break; case 'l': { List_item_reader* ir = va_arg(args, List_item_reader*); void* userdata = va_arg(args, void*); Streader_read_list(sr, ir, userdata); } break; case 'd': { Dict_item_reader* ir = va_arg(args, Dict_item_reader*); void* userdata = va_arg(args, void*); Streader_read_dict(sr, ir, userdata); } break; case '%': { Streader_match_char(sr, '%'); } break; default: rassert(false); } } else { // Characters to be matched if (!isspace(*format))