| /* liblouis Braille Translation and Back-Translation Library |
| |
| Copyright (C) 2008 Eitan Isaacson <eitan@ascender.com> |
| Copyright (C) 2012 James Teh <jamie@nvaccess.org> |
| Copyright (C) 2012 Bert Frees <bertfrees@gmail.com> |
| Copyright (C) 2014 Mesar Hameed <mesar.hameed@gmail.com> |
| Copyright (C) 2015 Mike Gray <mgray@aph.org> |
| Copyright (C) 2010-2017 Swiss Library for the Blind, Visually Impaired and Print |
| Disabled |
| Copyright (C) 2016-2017 Davy Kager <mail@davykager.nl> |
| |
| Copying and distribution of this file, with or without modification, |
| are permitted in any medium without royalty provided the copyright |
| notice and this notice are preserved. This file is offered as-is, |
| without any warranty. |
| */ |
| |
| /** |
| * @file |
| * @brief Test helper functions |
| */ |
| |
| #include <config.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "liblouis.h" |
| #include "internal.h" |
| #include "brl_checks.h" |
| #include "unistr.h" |
| |
| static void |
| print_int_array(const char *prefix, int *pos_list, int len) { |
| int i; |
| fprintf(stderr, "%s ", prefix); |
| for (i = 0; i < len; i++) fprintf(stderr, "%d ", pos_list[i]); |
| fprintf(stderr, "\n"); |
| } |
| |
| static void |
| print_typeform(const formtype *typeform, int len) { |
| int i; |
| fprintf(stderr, "Typeform: "); |
| for (i = 0; i < len; i++) fprintf(stderr, "%hi", typeform[i]); |
| fprintf(stderr, "\n"); |
| } |
| |
| static void |
| print_widechars(widechar *buffer, int length) { |
| uint8_t *result_buf; |
| size_t result_len; |
| |
| #ifdef WIDECHARS_ARE_UCS4 |
| result_buf = u32_to_u8(buffer, length, NULL, &result_len); |
| #else |
| result_buf = u16_to_u8(buffer, length, NULL, &result_len); |
| #endif |
| fprintf(stderr, "%.*s", (int)result_len, result_buf); |
| free(result_buf); |
| } |
| |
| /* direction, 0=forward, 1=backwards, 2=both directions. If diagnostics is 1 then |
| * print diagnostics in case where the translation is not as |
| * expected */ |
| |
| int |
| check_base(const char *tableList, const char *input, const char *expected, |
| optional_test_params in) { |
| |
| int i, retval = 0; |
| int direction = in.direction; |
| const int *expected_inputPos = in.expected_inputPos; |
| const int *expected_outputPos = in.expected_outputPos; |
| if (in.direction < 0 || in.direction > 2) { |
| fprintf(stderr, "Invalid direction.\n"); |
| return 1; |
| } |
| if (in.direction != 0 && in.typeform != NULL) { |
| // Currently, in backward translation, nothing is done with the initial value of |
| // the typeform argument, and on return it always contains all zeros, so it |
| // doesn't make any sense to use typeforms in backward translation tests. |
| fprintf(stderr, "typeforms only supported with testmode 'forward'\n"); |
| return 1; |
| } |
| if (in.direction == 2 && in.cursorPos >= 0) { |
| fprintf(stderr, "cursorPos not supported with testmode 'bothDirections'\n"); |
| return 1; |
| } |
| if (in.direction == 2 && in.max_outlen >= 0) { |
| fprintf(stderr, "maxOutputLength not supported with testmode 'bothDirections'\n"); |
| return 1; |
| } |
| if (in.real_inlen >= 0 && in.max_outlen < 0) { |
| fprintf(stderr, |
| "realInputLength not supported when maxOutputLength is not specified\n"); |
| return 1; |
| } |
| while (1) { |
| widechar *inbuf, *outbuf, *expectedbuf; |
| int inlen = strlen(input); |
| int actualInlen; |
| const int outlen_multiplier = 4 + sizeof(widechar) * 2; |
| int outlen = inlen * outlen_multiplier; |
| int expectedlen = strlen(expected); |
| int funcStatus = 0; |
| formtype *typeformbuf = NULL; |
| int *inputPos = NULL; |
| int *outputPos = NULL; |
| int cursorPos = 0; |
| inbuf = malloc(sizeof(widechar) * inlen); |
| outbuf = malloc(sizeof(widechar) * outlen); |
| expectedbuf = malloc(sizeof(widechar) * expectedlen); |
| if (in.typeform != NULL) { |
| typeformbuf = malloc(outlen * sizeof(formtype)); |
| memcpy(typeformbuf, in.typeform, inlen * sizeof(formtype)); |
| } |
| if (in.cursorPos >= 0) { |
| cursorPos = in.cursorPos; |
| } |
| if (in.max_outlen >= 0) { |
| outlen = in.max_outlen; |
| } |
| inlen = _lou_extParseChars(input, inbuf); |
| if (!inlen) { |
| fprintf(stderr, "Cannot parse input string.\n"); |
| retval = 1; |
| goto fail; |
| } |
| if (in.real_inlen > inlen) { |
| fprintf(stderr, |
| "expected realInputLength (%d) may not exceed total input length " |
| "(%d)\n", |
| in.real_inlen, inlen); |
| return 1; |
| } |
| if (expected_inputPos) { |
| inputPos = malloc(sizeof(int) * outlen); |
| } |
| if (expected_outputPos) { |
| outputPos = malloc(sizeof(int) * inlen); |
| } |
| actualInlen = inlen; |
| // Note that this loop is not strictly needed to make the current tests pass, but |
| // in the general case it is needed because it is theoretically possible that we |
| // provided a too short output buffer. |
| for (int k = 1; k <= 3; k++) { |
| if (direction == 1) { |
| funcStatus = _lou_backTranslate(tableList, in.display_table, inbuf, |
| &actualInlen, outbuf, &outlen, typeformbuf, NULL, outputPos, |
| inputPos, &cursorPos, in.mode, NULL, NULL); |
| } else { |
| funcStatus = _lou_translate(tableList, in.display_table, inbuf, |
| &actualInlen, outbuf, &outlen, typeformbuf, NULL, outputPos, |
| inputPos, &cursorPos, in.mode, NULL, NULL); |
| } |
| if (!funcStatus) { |
| fprintf(stderr, "Translation failed.\n"); |
| retval = 1; |
| goto fail; |
| } |
| if (in.max_outlen >= 0 || inlen == actualInlen) { |
| break; |
| } else if (k < 3) { |
| // Hm, something is not quite right. Try again with a larger outbuf |
| free(outbuf); |
| outlen = inlen * outlen_multiplier * (k + 1); |
| outbuf = malloc(sizeof(widechar) * outlen); |
| if (expected_inputPos) { |
| free(inputPos); |
| inputPos = malloc(sizeof(int) * outlen); |
| } |
| fprintf(stderr, |
| "Warning: For %s: returned inlen (%d) differs from passed inlen " |
| "(%d) " |
| "using outbuf of size %d. Trying again with bigger outbuf " |
| "(%d).\n", |
| input, actualInlen, inlen, inlen * outlen_multiplier * k, outlen); |
| actualInlen = inlen; |
| } |
| } |
| expectedlen = _lou_extParseChars(expected, expectedbuf); |
| for (i = 0; i < outlen && i < expectedlen && expectedbuf[i] == outbuf[i]; i++) |
| ; |
| if (i < outlen || i < expectedlen) { |
| retval = 1; |
| if (in.diagnostics) { |
| outbuf[outlen] = 0; |
| fprintf(stderr, "Input: '%s'\n", input); |
| /* Print the original typeform not the typeformbuf, as the |
| * latter has been modified by the translation and contains some |
| * information about outbuf */ |
| if (in.typeform != NULL) print_typeform(in.typeform, inlen); |
| if (in.cursorPos >= 0) fprintf(stderr, "Cursor: %d\n", in.cursorPos); |
| fprintf(stderr, "Expected: '%s' (length %d)\n", expected, expectedlen); |
| fprintf(stderr, "Received: '"); |
| print_widechars(outbuf, outlen); |
| fprintf(stderr, "' (length %d)\n", outlen); |
| |
| uint8_t *expected_utf8; |
| uint8_t *out_utf8; |
| size_t expected_utf8_len; |
| size_t out_utf8_len; |
| #ifdef WIDECHARS_ARE_UCS4 |
| expected_utf8 = u32_to_u8(&expectedbuf[i], 1, NULL, &expected_utf8_len); |
| out_utf8 = u32_to_u8(&outbuf[i], 1, NULL, &out_utf8_len); |
| #else |
| expected_utf8 = u16_to_u8(&expectedbuf[i], 1, NULL, &expected_utf8_len); |
| out_utf8 = u16_to_u8(&outbuf[i], 1, NULL, &out_utf8_len); |
| #endif |
| |
| if (i < outlen && i < expectedlen) { |
| fprintf(stderr, |
| "Diff: Expected '%.*s' but received '%.*s' in index %d\n", |
| (int)expected_utf8_len, expected_utf8, (int)out_utf8_len, |
| out_utf8, i); |
| } else if (i < expectedlen) { |
| fprintf(stderr, |
| "Diff: Expected '%.*s' but received nothing in index " |
| "%d\n", |
| (int)expected_utf8_len, expected_utf8, i); |
| } else { |
| fprintf(stderr, |
| "Diff: Expected nothing but received '%.*s' in index " |
| "%d\n", |
| (int)out_utf8_len, out_utf8, i); |
| } |
| free(expected_utf8); |
| free(out_utf8); |
| } |
| } |
| if (expected_inputPos) { |
| int error_printed = 0; |
| for (i = 0; i < outlen; i++) { |
| if (expected_inputPos[i] != inputPos[i]) { |
| retval = 1; |
| if (in.diagnostics) { |
| if (!error_printed) { // Print only once |
| fprintf(stderr, "Input position failure:\n"); |
| error_printed = 1; |
| } |
| fprintf(stderr, "Expected %d, received %d in index %d\n", |
| expected_inputPos[i], inputPos[i], i); |
| } |
| } |
| } |
| } |
| if (expected_outputPos) { |
| int error_printed = 0; |
| for (i = 0; i < inlen; i++) { |
| if (expected_outputPos[i] != outputPos[i]) { |
| retval = 1; |
| if (in.diagnostics) { |
| if (!error_printed) { // Print only once |
| fprintf(stderr, "Output position failure:\n"); |
| error_printed = 1; |
| } |
| fprintf(stderr, "Expected %d, received %d in index %d\n", |
| expected_outputPos[i], outputPos[i], i); |
| } |
| } |
| } |
| } |
| if ((in.expected_cursorPos >= 0) && (cursorPos != in.expected_cursorPos)) { |
| retval = 1; |
| if (in.diagnostics) { |
| fprintf(stderr, "Cursor position failure:\n"); |
| fprintf(stderr, "Initial:%d Expected:%d Actual:%d \n", in.cursorPos, |
| in.expected_cursorPos, cursorPos); |
| } |
| } |
| if (in.max_outlen < 0 && inlen != actualInlen) { |
| retval = 1; |
| if (in.diagnostics) { |
| fprintf(stderr, |
| "Unexpected error happened: input length is not the same before " |
| "as " |
| "after the translation:\n"); |
| fprintf(stderr, "Before: %d After: %d \n", inlen, actualInlen); |
| } |
| } else if (actualInlen > inlen) { |
| retval = 1; |
| if (in.diagnostics) { |
| fprintf(stderr, |
| "Unexpected error happened: returned input length (%d) exceeds " |
| "total input length (%d)\n", |
| actualInlen, inlen); |
| } |
| } else if (in.real_inlen >= 0 && in.real_inlen != actualInlen) { |
| retval = 1; |
| if (in.diagnostics) { |
| fprintf(stderr, "Real input length failure:\n"); |
| fprintf(stderr, "Expected: %d, received: %d\n", in.real_inlen, |
| actualInlen); |
| } |
| } |
| |
| fail: |
| free(inbuf); |
| free(outbuf); |
| free(expectedbuf); |
| free(typeformbuf); |
| free(inputPos); |
| free(outputPos); |
| |
| if (direction == 2) { |
| const char *tmp = input; |
| input = expected; |
| expected = tmp; |
| expected_inputPos = in.expected_outputPos; |
| expected_outputPos = in.expected_inputPos; |
| direction = 1; |
| continue; |
| } else { |
| break; |
| } |
| } |
| |
| return retval; |
| } |
| |
| /* Helper function to convert a typeform string of '0's, '1's, '2's etc. |
| * to the required format, which is an array of 0s, 1s, 2s, etc. |
| * For example, "0000011111000" is converted to {0,0,0,0,0,1,1,1,1,1,0,0,0} |
| * The caller is responsible for freeing the returned array. */ |
| formtype * |
| convert_typeform(const char *typeform_string) { |
| int len = strlen(typeform_string); |
| formtype *typeform = malloc(len * sizeof(formtype)); |
| int i; |
| for (i = 0; i < len; i++) typeform[i] = typeform_string[i] - '0'; |
| return typeform; |
| } |
| |
| void |
| update_typeform(const char *typeform_string, formtype *typeform, const typeforms kind) { |
| int len = strlen(typeform_string); |
| int i; |
| for (i = 0; i < len; i++) |
| if (typeform_string[i] != ' ') typeform[i] |= kind; |
| } |
| |
| int |
| check_cursor_pos(const char *tableList, const char *str, const int *expected_pos) { |
| widechar *inbuf; |
| widechar *outbuf; |
| int *inpos, *outpos; |
| int inlen = strlen(str); |
| int outlen = inlen; |
| int cursor_pos; |
| int i, retval = 0; |
| |
| inbuf = malloc(sizeof(widechar) * inlen); |
| outbuf = malloc(sizeof(widechar) * inlen); |
| inpos = malloc(sizeof(int) * inlen); |
| outpos = malloc(sizeof(int) * inlen); |
| inlen = _lou_extParseChars(str, inbuf); |
| |
| for (i = 0; i < inlen; i++) { |
| cursor_pos = i; |
| if (!lou_translate(tableList, inbuf, &inlen, outbuf, &outlen, NULL, NULL, NULL, |
| NULL, &cursor_pos, compbrlAtCursor)) { |
| fprintf(stderr, "Translation failed.\n"); |
| retval = 1; |
| goto fail; |
| } |
| if (expected_pos[i] != cursor_pos) { |
| if (!retval) // Print only once |
| fprintf(stderr, "Cursorpos failure:\n"); |
| fprintf(stderr, |
| "string='%s' cursor=%d ('%c') expected=%d received=%d ('%c')\n", str, |
| i, str[i], expected_pos[i], cursor_pos, (char)outbuf[cursor_pos]); |
| retval = 1; |
| } |
| } |
| |
| fail: |
| free(inbuf); |
| free(outbuf); |
| free(inpos); |
| free(outpos); |
| return retval; |
| } |
| |
| /* Check if a string is hyphenated as expected, by passing the |
| * expected hyphenation position array. |
| * |
| * @return 0 if the hyphenation is as expected and 1 otherwise. |
| */ |
| int |
| check_hyphenation_pos(const char *tableList, const char *str, const char *expected) { |
| widechar *inbuf; |
| char *hyphens = NULL; |
| int inlen = strlen(str); |
| int retval = 0; |
| |
| inbuf = malloc(sizeof(widechar) * inlen); |
| inlen = _lou_extParseChars(str, inbuf); |
| if (!inlen) { |
| fprintf(stderr, "Cannot parse input string.\n"); |
| retval = 1; |
| goto fail; |
| } |
| hyphens = calloc(inlen + 1, sizeof(char)); |
| |
| if (!lou_hyphenate(tableList, inbuf, inlen, hyphens, 0)) { |
| fprintf(stderr, "Hyphenation failed.\n"); |
| retval = 1; |
| goto fail; |
| } |
| |
| if (strcmp(expected, hyphens)) { |
| fprintf(stderr, "Input: '%s'\n", str); |
| fprintf(stderr, "Expected: '%s'\n", expected); |
| fprintf(stderr, "Received: '%s'\n", hyphens); |
| retval = 1; |
| } |
| |
| fail: |
| free(inbuf); |
| free(hyphens); |
| return retval; |
| } |
| |
| /** Check if a string is hyphenated as expected. |
| * |
| * mode is '0' when input is text and '1' when input is braille |
| * |
| * @return 0 if the hyphenation is as expected and 1 otherwise. |
| */ |
| int |
| check_hyphenation( |
| const char *tableList, const char *str, const char *expected, int mode) { |
| widechar *inbuf; |
| widechar *hyphenatedbuf = NULL; |
| uint8_t *hyphenated = NULL; |
| char *hyphens = NULL; |
| int inlen = strlen(str); |
| size_t hyphenatedlen = inlen * 2; |
| int retval = 0; |
| |
| inbuf = malloc(sizeof(widechar) * inlen); |
| inlen = _lou_extParseChars(str, inbuf); |
| if (!inlen) { |
| fprintf(stderr, "Cannot parse input string.\n"); |
| retval = 1; |
| goto fail; |
| } |
| hyphens = calloc(inlen + 1, sizeof(char)); |
| |
| if (!lou_hyphenate(tableList, inbuf, inlen, hyphens, mode)) { |
| fprintf(stderr, "Hyphenation failed.\n"); |
| retval = 1; |
| goto fail; |
| } |
| if (hyphens[0] != '0') { |
| fprintf(stderr, "Unexpected output from lou_hyphenate.\n"); |
| retval = 1; |
| goto fail; |
| } |
| |
| hyphenatedbuf = malloc(sizeof(widechar) * hyphenatedlen); |
| int i = 0; |
| int j = 0; |
| hyphenatedbuf[i++] = inbuf[j++]; |
| for (; j < inlen; j++) { |
| if (hyphens[j] == '2') |
| hyphenatedbuf[i++] = (widechar)'|'; |
| else if (hyphens[j] != '0') |
| hyphenatedbuf[i++] = (widechar)'-'; |
| hyphenatedbuf[i++] = inbuf[j]; |
| } |
| |
| #ifdef WIDECHARS_ARE_UCS4 |
| hyphenated = u32_to_u8(hyphenatedbuf, i, NULL, &hyphenatedlen); |
| #else |
| hyphenated = u16_to_u8(hyphenatedbuf, i, NULL, &hyphenatedlen); |
| #endif |
| |
| if (!hyphenated) { |
| fprintf(stderr, "Unexpected error during UTF-8 encoding\n"); |
| free(hyphenatedbuf); |
| retval = 2; |
| goto fail; |
| } |
| |
| if (strlen(expected) != hyphenatedlen || |
| strncmp(expected, (const char *)hyphenated, hyphenatedlen)) { |
| fprintf(stderr, "Input: '%s'\n", str); |
| fprintf(stderr, "Expected: '%s'\n", expected); |
| fprintf(stderr, "Received: '%.*s'\n", (int)hyphenatedlen, hyphenated); |
| retval = 1; |
| } |
| |
| free(hyphenatedbuf); |
| free(hyphenated); |
| |
| fail: |
| free(inbuf); |
| free(hyphens); |
| return retval; |
| } |