summaryrefslogtreecommitdiff
path: root/test/test_paragraph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_paragraph.cpp')
-rw-r--r--test/test_paragraph.cpp971
1 files changed, 971 insertions, 0 deletions
diff --git a/test/test_paragraph.cpp b/test/test_paragraph.cpp
new file mode 100644
index 000000000..2736c44fb
--- /dev/null
+++ b/test/test_paragraph.cpp
@@ -0,0 +1,971 @@
+/* Copyright (C) 2016- Free Software Foundation, Inc.
+ Written by Bertrand Garrigues (bertrand.garrigues@laposte.net)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "paragraph_l.h"
+#include "paragraph.h"
+
+#define TRACE_ALIGN_LENGTH 40
+#include "trace.h"
+trace_define_category(item, LEVEL_ERROR);
+trace_define_category(breakpoint, LEVEL_ERROR);
+trace_define_category(paragraph, LEVEL_ERROR);
+
+#define TEST_FAIL 1
+#define TEST_SUCCESS 0
+#define test_assert(expression, fmt, args...) \
+ (((expression) == 0) ? \
+ printf(" * FAIL [%s:%d]: " fmt "\n", \
+ __FUNCTION__, __LINE__, ##args ), TEST_FAIL : TEST_SUCCESS)
+
+/* A simple implementation of paragraph_word, for ascii only. */
+class ascii_paragraph_word : public paragraph_word {
+private:
+ /* Length of each letter as set in Knuth's original example in his
+ * article "Breaking Paragraph Into Lines" */
+ static unsigned int char_len_[256];
+ static void init_char_len();
+ static bool is_class_initialized_;
+ char *sz_word_;
+ unsigned int width_;
+public:
+ // width_ is set by the strlen of sz_word
+ ascii_paragraph_word(const char *sz_word);
+ ~ascii_paragraph_word();
+ // overwrite the width calculated by the constructor, this is useful
+ // to create a box for indentation (which are empty but with a width
+ // > 0
+ void set_width(unsigned int width);
+ unsigned int get_width();
+ int get_next_glue_values(unsigned int *width,
+ unsigned int *stretchability,
+ unsigned int *shrinkability);
+ int snprint(char *str, size_t size);
+ void write();
+};
+
+unsigned int ascii_paragraph_word::char_len_[256];
+bool ascii_paragraph_word::is_class_initialized_;
+
+void
+ascii_paragraph_word::init_char_len()
+{
+ int k;
+ // Unknown characters width is set to 10
+ for (k = 0; k < 256; k++)
+ char_len_[k] = 10;
+ char_len_[' '] = 0; // we set the space width to 0 here to be able to quickly
+ // compute the length of a line without having to deal with
+ // the last space
+ char_len_['a'] = 9;
+ char_len_['b'] = 10;
+ char_len_['c'] = 8;
+ char_len_['d'] = 10;
+ char_len_['e'] = 8;
+ char_len_['f'] = 6;
+ char_len_['g'] = 9;
+ char_len_['h'] = 10;
+ char_len_['i'] = 5;
+ char_len_['j'] = 6;
+ char_len_['k'] = 10;
+ char_len_['l'] = 5;
+ char_len_['m'] = 15;
+ char_len_['n'] = 10;
+ char_len_['o'] = 9;
+ char_len_['p'] = 10;
+ char_len_['q'] = 10;
+ char_len_['r'] = 7;
+ char_len_['s'] = 7;
+ char_len_['t'] = 7;
+ char_len_['u'] = 10;
+ char_len_['v'] = 9;
+ char_len_['w'] = 13;
+ char_len_['x'] = 10;
+ char_len_['y'] = 10;
+ char_len_['z'] = 8;
+ char_len_['C'] = 13;
+ char_len_['I'] = 6;
+
+ char_len_['-'] = 6;
+ char_len_[','] = 5;
+ char_len_[';'] = 5;
+ char_len_['.'] = 5;
+ char_len_['\''] = 5;
+
+ is_class_initialized_ = true ;
+}
+
+ascii_paragraph_word::ascii_paragraph_word(const char *sz_word)
+{
+ if (is_class_initialized_ == false)
+ init_char_len();
+
+ width_ = 0;
+ if (sz_word) {
+ size_t len;
+ int k = 0;
+ len = strlen(sz_word);
+ sz_word_ = (char *)malloc(len + 1);
+ strcpy(sz_word_, sz_word);
+ while (sz_word_[k] != '\0') {
+ if (sz_word_[k] >= 0)
+ width_ += char_len_[(unsigned char) sz_word_[k]];
+ k++;
+ }
+ }
+}
+
+ascii_paragraph_word::~ascii_paragraph_word()
+{
+ if (sz_word_)
+ free(sz_word_);
+}
+
+void
+ascii_paragraph_word::set_width(unsigned int width)
+{
+ width_ = width;
+}
+
+unsigned int
+ascii_paragraph_word::get_width()
+{
+ return width_;
+}
+
+int
+ascii_paragraph_word::get_next_glue_values(unsigned int *width,
+ unsigned int *stretchability,
+ unsigned int *shrinkability)
+{
+ size_t len;
+ char last_character;
+
+ if (sz_word_) {
+ len = strlen(sz_word_);
+ last_character = sz_word_[len - 1];
+ } else {
+ last_character = 0;
+ }
+ switch (last_character) {
+ case ',':
+ *width = 6;
+ *stretchability = 4;
+ *shrinkability = 2;
+ break;
+ case ';':
+ *width = 6;
+ *stretchability = 4;
+ *shrinkability = 1;
+ break;
+ case '.':
+ *width = 8;
+ *stretchability = 6;
+ *shrinkability = 1;
+ break;
+ default:
+ *width = 6;
+ *stretchability = 3;
+ *shrinkability = 2;
+ break;
+ }
+}
+
+int
+ascii_paragraph_word::snprint(char *str, size_t size)
+{
+ return snprintf(str, size, "%s", sz_word_);
+}
+
+void
+ascii_paragraph_word::write()
+{
+ printf("%s", sz_word_);
+}
+
+/* Return -1 if the word cannot be hyphenate. If it can, return the position in
+ the word were the hyphenation can be done. We just take all the words in the
+ example paragraph that can be hyphenated. */
+typedef enum {
+ NO_HYPHEN,
+ EXPLICIT_HYPHEN,
+ OPTIONAL_HYPHEN
+} hyphen_type_t;
+
+class text_loader {
+ char *text_; // Complete paragraph text in one string
+ hyphen_type_t simulate_hyphenate (const char *word,
+ unsigned int *first_part_len);
+public:
+ text_loader(char *text, const char *path = NULL);
+ ~text_loader();
+ void process_text(paragraph *par, bool with_indentation);
+};
+
+text_loader::text_loader(char *text, const char *path)
+{
+ if (text) {
+ text_ = (char *)calloc(strlen(text) +1, sizeof (char));
+ strcpy(text_, text);
+ } else if (path) {
+ FILE *fp;
+ char *c;
+ size_t text_size;
+ struct stat buf;
+
+ fp = fopen (path, "r");
+ if (fp == NULL) {
+ printf("Error:%s\n", strerror(errno));
+ text_ = NULL;
+ } else {
+ stat(path, &buf);
+ text_ = (char *) calloc((buf.st_size) + 1, sizeof(char));
+ text_size = fread(text_, 1, buf.st_size, fp);
+ for (c = text_; c < text_ + text_size; c++) {
+ if (*c == '\n')
+ *c = ' ';
+ }
+ fclose(fp);
+ }
+ }
+}
+
+text_loader::~text_loader()
+{
+ if (text_)
+ free(text_);
+}
+
+hyphen_type_t
+text_loader::simulate_hyphenate (const char *word, unsigned int *first_part_len)
+{
+ hyphen_type_t ret = OPTIONAL_HYPHEN;
+
+ if (word == NULL || first_part_len == NULL)
+ goto end;
+
+ if (strncmp(word, "lime-tree", 9) == 0) {
+ *first_part_len = 5;
+ ret = EXPLICIT_HYPHEN;
+ goto end;
+ }
+
+ if (strncmp(word, "wishing", 7) == 0)
+ *first_part_len = 4;
+ else if (strncmp(word, "daughters", 9) == 0)
+ *first_part_len = 5;
+ /* for the sake of simplicity we don't consider the break after beauti */
+ else if (strncmp(word, "beautiful", 9) == 0)
+ *first_part_len = 4;
+ else if (strncmp(word, "youngest", 8) == 0)
+ *first_part_len = 5;
+ else if (strncmp(word, "itself", 6) == 0)
+ *first_part_len = 2;
+ else if (strncmp(word, "astonished", 10) == 0)
+ *first_part_len = 5;
+ else if (strncmp(word, "whenever", 8) == 0)
+ *first_part_len = 4;
+ else if (strncmp(word, "forest", 6) == 0)
+ *first_part_len = 3;
+ else if (strncmp(word, "under", 5) == 0)
+ *first_part_len = 2;
+ else if (strncmp(word, "lime-tree", 9) == 0)
+ *first_part_len = 5;
+ else if (strncmp(word, "fountain", 8) == 0)
+ *first_part_len = 4;
+ else if (strncmp(word, "favorite", 8) == 0)
+ *first_part_len = 5;
+ else if (strncmp(word, "plaything", 9) == 0)
+ *first_part_len = 4;
+ else if (strncmp(word, "hyphenationtest", 15) == 0)
+ *first_part_len = 11;
+ else
+ ret = NO_HYPHEN;
+
+end:
+ return ret;
+}
+
+void
+text_loader::process_text(paragraph *par, bool with_indentation)
+{
+ char *cur, *tmp;
+ ascii_paragraph_word *cur_word, *indentation;
+ unsigned int width;
+ hyphen_type_t type;
+ unsigned int hyphenate = 0;
+ char previous_char;
+ char *tmp_text;
+
+ /* Add indentation width 18 */
+ if (with_indentation) {
+ indentation = new ascii_paragraph_word(" ");
+ indentation->set_width(18);
+ par->add_box(indentation);
+ }
+
+ /* Build the paragraph: for each word of 'text' we check if there is an
+ * explicit hyphen (here only the word "lime-tree"), otherwise we add an
+ * optional hyphen, and add the corresponding items. For example 'whenever'
+ * has an optional hyphen (when-ever), so we:
+ * - add a box of width 4 ('when').
+ * - add an optional hyphen, which is a penalty item (penalty added only if
+ the hyphen is chosen).
+ * - add a box of width 4 ('ever').
+ */
+ tmp_text = (char *) calloc(strlen(text_) + 1, sizeof(char));
+ strcpy(tmp_text, text_);
+ cur = strtok(tmp_text, " ");
+ while (cur != NULL) {
+ type = simulate_hyphenate(cur, &hyphenate);
+ if (type != NO_HYPHEN) {
+ tmp = (char *) malloc(hyphenate + 1);
+ strncpy(tmp, cur, hyphenate);
+ tmp[hyphenate] = '\0';
+ cur_word = new ascii_paragraph_word(tmp);
+ par->add_box(cur_word);
+ free(tmp);
+ if (type == EXPLICIT_HYPHEN) {
+ par->add_explicit_hyphen();
+ } else {
+ ascii_paragraph_word *hyphen_sign = new ascii_paragraph_word("-");
+ par->add_optional_hyphen(hyphen_sign);
+ }
+ cur_word = new ascii_paragraph_word(cur + hyphenate);
+ par->add_box(cur_word);
+ par->add_glue();
+ } else {
+ cur_word = new ascii_paragraph_word(cur);
+ par->add_box(cur_word);
+ par->add_glue();
+ }
+ cur = strtok(NULL, " ");
+ }
+ /* Add final glue */
+ par->finish();
+ free(tmp_text);
+}
+
+struct expected_break_info {
+ const char word[64];
+ unsigned int demerit;
+ unsigned int total_demerits;
+};
+
+
+#define PRINT_RESULT(n_failures) \
+ do { \
+ if (n_failures == 0) \
+ printf("-- Test %s PASSED\n\n", __FUNCTION__); \
+ else \
+ fprintf(stderr, "** Test %s FAILED, %d failures\n", __FUNCTION__, n_failures); \
+ } while(0)
+
+class test_paragraph {
+
+private:
+ text_loader *text_loader_;
+ int check_all_breakpoint(struct expected_break_info tab_expected[],
+ paragraph *par);
+ int check_best_breakpoint(struct expected_break_info tab_expected[],
+ paragraph *par);
+ int check_best_breakpoint(const char*tab_expected[], paragraph *par);
+public:
+ test_paragraph();
+ ~test_paragraph();
+ void suite1_init();
+ int test11_original_example();
+ int test12_example_with_default_demerits_formula();
+ int test13_example_with_larger_tolerance();
+
+ void suite2_init();
+ int test21_hyphen_flagged_penalty();
+
+ void suite3_init();
+ int test31_fitness_class();
+};
+
+test_paragraph::test_paragraph()
+{
+ text_loader_ = NULL;
+}
+
+/* Check all breakpoints list, we start at passive_breaks_list_head_.next
+ * because the first breakpoint is the initial breakpoint */
+int
+test_paragraph::check_all_breakpoint(struct expected_break_info tab_expected[],
+ paragraph *par)
+{
+ int res = 0;
+ breakpoint *pos;
+ unsigned int total_demerits;
+ char word[256];
+ int k = 0;
+ item *i;
+
+ list_for_each_entry(pos,
+ par->passive_breaks_list_head_.next,
+ list_,
+ breakpoint) {
+ total_demerits = pos->get_total_demerits();
+ res += test_assert(tab_expected[k].total_demerits == total_demerits,
+ "total demerits: expected: %u, actual: %u",
+ tab_expected[k].total_demerits,
+ total_demerits);
+ i = pos->get_previous_box();
+ if (i != NULL) {
+ paragraph_word *pw = i->get_word();
+ pw->snprint(word, 256);
+ i = pos->get_item();
+ res += test_assert(strcmp(tab_expected[k].word, word) == 0,
+ "expected: '%s' actual: '%s'",
+ tab_expected[k].word,
+ word);
+ }
+ k++;
+ }
+
+ return res;
+}
+
+/* Check best breakpoints list, the first breakpoint of best_breaks_list_head_
+ is the breakpoint at the end of the 1st line, not the initial breakpoint
+ Return the number of assert that failed or 0 if OK */
+int
+test_paragraph::check_best_breakpoint(struct expected_break_info tab_expected[],
+ paragraph *par)
+{
+ int res = 0;
+ unsigned int total_demerits;
+ char word[256];
+ int k = 0;
+ item *i;
+
+ for (k = 0; k < par->get_number_of_lines(); k++) {
+ total_demerits = par->array_best_breaks_[k]->get_total_demerits();
+ res += test_assert(tab_expected[k].total_demerits == total_demerits,
+ "total demerits: expected: %u, actual: %u",
+ tab_expected[k].total_demerits,
+ total_demerits);
+ i = par->array_best_breaks_[k]->get_previous_box();
+ if (i == NULL) {
+ res += test_assert(false, "cannot find breakpoint's previous box");
+ } else {
+ paragraph_word *pw = i->get_word();
+ pw->snprint(word, 256);
+ i = par->array_best_breaks_[k]->get_item();
+ res += test_assert(strcmp(tab_expected[k].word, word) == 0,
+ "expected: '%s' actual: '%s'",
+ tab_expected[k].word,
+ word);
+ }
+ }
+
+ return res;
+}
+
+int
+test_paragraph::check_best_breakpoint(const char*tab_expected[], paragraph *par)
+{
+ int res = 0;
+ char word[256];
+ int k = 0;
+ item *i;
+
+ for (k = 0; k < par->get_number_of_lines(); k++) {
+ i = par->array_best_breaks_[k]->get_previous_box();
+ if (i == NULL) {
+ res += test_assert(false, "cannot find breakpoint's previous box");
+ } else {
+ paragraph_word *pw;
+ pw = i->get_word();
+ pw->snprint(word, 256);
+ i = par->array_best_breaks_[k]->get_item();
+ res += test_assert(strcmp(tab_expected[k], word) == 0,
+ "expected: '%s' actual: '%s'",
+ tab_expected[k],
+ word);
+ }
+ }
+
+ return res;
+}
+
+test_paragraph::~test_paragraph()
+{
+ if (text_loader_)
+ delete text_loader_;
+}
+
+/* Load the text and add to par the corresponding box, glue, penalty */
+
+/**
+ * Test 1
+ *
+ * Check each line ratio. FIXME understand why the last one should be 0.004 and
+ * not 0.000.
+ */
+void
+test_paragraph::suite1_init()
+{
+ text_loader *tl;
+ char text[] =
+ "In olden times when wishing still helped one, there lived a "
+ "king whose daughters were all beautiful; and the youngest was "
+ "so beautiful that the sun itself, which has seen so much, was "
+ "astonished whenever it shone in her face. Close by the king's "
+ "castle lay a great dark forest, and under an old lime-tree in the "
+ "forest was a well, and when the day was very warm, the king's "
+ "child went out into the forest and sat down by the side of the "
+ "cool fountain; and when she was bored she took a golden ball, "
+ "and threw it up on high and caught it; and this ball was her "
+ "favorite plaything.";
+
+ printf("-- Suite 1 Initialisation\n");
+ tl = new text_loader(text);
+ text_loader_ = tl;
+}
+
+int
+test_paragraph::test11_original_example()
+{
+ paragraph *par = new paragraph;
+ paragraph_printer *printer;
+ int res = 0;
+ float expected_line_ratio[10] = {0.774, 0.179, 0.629, 0.545, 0.000,
+ 0.079, 0.282, 0.294, 0.575, 0.000};
+
+ int n_lines;
+ int k;
+ float ratio;
+ struct expected_break_info all_expected[] = {
+ { "a", 2209, 2209 },
+ { "king", 1521, 1521 },
+ { "was", 4, 2213 },
+ { "so", 3136, 4657 },
+ { "was", 676, 2889 },
+ { "aston", 3600, 8257 },
+ { "king's", 289, 3178 },
+ { "castle", 9, 8266 },
+ { "lay", 4489, 12746 },
+ { "in", 5929, 9107 },
+ { "the", 1, 3179 },
+ { "for", 3481, 11747 },
+ { "est", 1, 8267 },
+ { "was", 4, 12750 },
+ { "a", 2209, 14955 },
+ { "the", 676, 9783 },
+ { "king's", 1, 3180 },
+ { "child", 4, 8271 },
+ { "went", 1, 12751 },
+ { "out", 1369, 16324 },
+ { "side", 16, 9799 },
+ { "of", 49, 9832 },
+ { "the", 9, 3189 },
+ { "cool", 121, 8392 },
+ { "foun", 3249, 16000 },
+ { "tain;", 400, 13151 },
+ { "and", 1444, 17768 },
+ { "golden", 1, 9800 },
+ { "ball,", 16, 3205 },
+ { "and", 25, 8417 },
+ { "threw", 4, 16004 },
+ { "it", 289, 13440 },
+ { "up", 4, 13155 },
+ { "on", 1, 17769 },
+ { "was", 25, 9825 },
+ { "her", 400, 3605 },
+ { "favor", 2601, 11018 },
+ { "ite", 16, 8433 },
+ { "play", 3364, 16804 },
+ { "thing.", 1, 3606 }
+ };
+
+ struct expected_break_info best_expected[] = {
+ { "a", 2209, 2209 },
+ { "was", 4, 2213 },
+ { "was", 676, 2889 },
+ { "king's", 289, 3178 },
+ { "the", 1, 3179 },
+ { "king's", 1, 3180 },
+ { "the", 9, 3189 },
+ { "ball,", 16, 3205 },
+ { "her", 400, 3605 },
+ { "thing.", 1, 3606 }
+ };
+
+ printf("-- Test11...\n");
+
+ par->config_use_old_demerits_formula();
+ par->config_no_fitness_class();
+ text_loader_->process_text(par, true);
+ par->format_knuth_plass();
+
+ /* There should be 10 lines */
+ printf(" Checking the number of lines\n");
+ n_lines = par->get_number_of_lines();
+ if (test_assert(n_lines == 10,
+ "actual number of lines:%d expected: 10",
+ n_lines) == TEST_FAIL) {
+ res++;
+ }
+
+ /* test ratio */
+ printf(" Checking the lines adjustement ratio\n");
+ for (k = 0; k < 10; k++) {
+ ratio = par->get_adjust_ratio(k + 1);
+ if (test_assert(fabs(ratio - expected_line_ratio[k]) < 0.001,
+ "line number %d expected: %.3f actual ratio: %.3f",
+ k + 1, expected_line_ratio[k], ratio) == TEST_FAIL) {
+ res++;
+ }
+ }
+
+ printf(" Checking all breakpoints demerits\n");
+ res += check_all_breakpoint(all_expected, par);
+
+ printf(" Checking the best breakpoints array\n");
+ res += check_best_breakpoint(best_expected, par);
+ printer = new paragraph_printer(par);
+ printer->print();
+ delete printer;
+ PRINT_RESULT(res);
+ delete par;
+
+ return res;
+}
+
+int
+test_paragraph::test12_example_with_default_demerits_formula()
+{
+ int res = 0;
+ paragraph *par = new paragraph;
+ fitness_class_t fitness_class;
+ struct expected_break_info best_expected[] = {
+ { "a", 2209, 2209 },
+ { "was", 4, 2213 },
+ { "was", 676, 2889 },
+ { "king's", 289, 3178 },
+ { "the", 1, 3179 },
+ { "king's", 1, 3180 },
+ { "the", 9, 3189 },
+ { "ball,", 16, 3205 },
+ { "her", 400, 3605 },
+ { "thing.", 1, 3606 }
+ };
+ fitness_class_t expected_fitness_class[10] = {
+ FITNESS_CLASS_LOOSE,
+ FITNESS_CLASS_NORMAL,
+ FITNESS_CLASS_LOOSE,
+ FITNESS_CLASS_LOOSE,
+ FITNESS_CLASS_NORMAL,
+ FITNESS_CLASS_NORMAL,
+ FITNESS_CLASS_NORMAL,
+ FITNESS_CLASS_NORMAL,
+ FITNESS_CLASS_LOOSE,
+ FITNESS_CLASS_NORMAL
+ };
+
+ int k = 0;
+
+ printf("-- Test12...\n");
+ text_loader_->process_text(par, true);
+ par->format_knuth_plass();
+
+ printf(" Checking the best breakpoints array\n");
+ res += check_best_breakpoint(best_expected, par);
+
+ printf(" Checking the lines fitness class\n");
+ for (k = 0; k < 10; k++) {
+ fitness_class = par->get_fitness_class(k + 1);
+ if (test_assert(
+ (fitness_class == expected_fitness_class[k]),
+ "line number: %d expected: %d actual fitness class: %d",
+ k + 1,
+ expected_fitness_class[k],
+ fitness_class) == TEST_FAIL)
+ res++;
+ }
+
+ PRINT_RESULT(res);
+ delete par;
+
+ return res;
+}
+
+int
+test_paragraph::test13_example_with_larger_tolerance()
+{
+ int res = 0;
+ paragraph *par = new paragraph;
+ struct expected_break_info best_expected[] = {
+ { "a", 2209, 2209 },
+ { "was", 4, 2213 },
+ { "was", 676, 2889 },
+ { "king's", 289, 3178 },
+ { "the", 1, 3179 },
+ { "king's", 1, 3180 },
+ { "the", 9, 3189 },
+ { "ball,", 16, 3205 },
+ { "her", 400, 3605 },
+ { "thing.", 1, 3606 }
+ };
+
+ printf("-- Test13...\n");
+ text_loader_->process_text(par, true);
+ par->format_knuth_plass(10);
+
+ printf(" Checking the best breakpoints array\n");
+ res += check_best_breakpoint(best_expected, par);
+
+ PRINT_RESULT(res);
+ delete par;
+
+ return res;
+}
+
+/* In this test the word 'hyphenationtest' can be hyphenated after
+ * 'hyphenation'. Letters 'A', 'B' and 'D' have a width of 10; 'C' has a width
+ * of 13, which means that the algorithm would naturally try to cut like this:
+
+ AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAA hyphenation-
+ test BBBBBBBBBB BBBBBBBBBB BBBBBBBBBB jlC hyphenation-
+ test DDDDDDDDDD DDDDDDDDDD";
+
+ However the additional penalty for two hyphenation in a row should make the
+ algorithm chose the break before the first DDDDDDDDDD.
+ */
+void
+test_paragraph::suite2_init()
+{
+ text_loader *tl;
+ char text[] =
+ "AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAA hyphenationtest "
+ "BBBBBBBBBB BBBBBBBBBB BBBBBBBBBB jlC hyphenationtest "
+ "DDDDDDDDDD DDDDDDDDDD";
+
+ printf("-- Suite 2 Initialisation\n");
+ tl = new text_loader(text);
+ text_loader_ = tl;
+}
+
+int
+test_paragraph::test21_hyphen_flagged_penalty()
+{
+ int res = 0;
+ paragraph *par = new paragraph;
+ paragraph_printer *printer;
+ const char *best_expected[] = {
+ "hyphenation",
+ "test", //FIXME actually is should be hyphenationtest
+ "DDDDDDDDDD"
+ };
+
+ printf("-- Test21...\n");
+ text_loader_->process_text(par, false);
+ par->format_knuth_plass(2);
+
+ printf(" Checking the best breakpoints array\n");
+ res += check_best_breakpoint(best_expected, par);
+
+ printer = new paragraph_printer(par);
+ printer->print();
+ delete printer;
+ PRINT_RESULT(res);
+ delete par;
+
+ return res;
+}
+
+/* There is only 1 feasible breakpoint at the first line, and the line is of
+class 0. At the second line, there are two feasible breaks, a class and a
+class 2; the class 2 is better but the class 1 should be chosen because of the
+first line. */
+void
+test_paragraph::suite3_init()
+{
+ text_loader *tl;
+ char text[] =
+ "The first line's best break makes it very veryyyyyy tiiiiiiiiiiiiiight, "
+ "the second line's best break is of class two but another break will "
+ "have to be preferred; it will give another line of class 0.";
+
+ printf("-- Suite 3 Initialisation\n");
+ tl = new text_loader(text);
+ text_loader_ = tl;
+}
+
+int
+test_paragraph::test31_fitness_class()
+{
+ int res = 0;
+ paragraph *par = new paragraph;
+ paragraph_printer *printer;
+ int k = 0;
+ item *i;
+
+ const char *best_expected[] = {
+ "tiiiiiiiiiiiiiight,",
+ "will",
+ "0."
+ };
+ char word[256];
+
+ printf("-- Test31...\n");
+ text_loader_->process_text(par, false);
+ par->format_knuth_plass(2);
+
+ printf(" Checking the best breakpoints array\n");
+ for (k = 0; k < par->get_number_of_lines(); k++) {
+ i = par->array_best_breaks_[k]->get_previous_box();
+ if (i == NULL) {
+ res += test_assert(false, "cannot find breakpoint's previous box");
+ } else {
+ i->sprint_word(word);
+ i = par->array_best_breaks_[k]->get_item();
+ res += test_assert(strcmp(best_expected[k], word) == 0,
+ "expected: '%s' actual: '%s'",
+ best_expected[k],
+ word);
+ }
+ }
+
+ printer = new paragraph_printer(par);
+ printer->print();
+ delete printer;
+ PRINT_RESULT(res);
+ delete par;
+
+ return res;
+}
+
+
+int main (int argc, char **argv)
+{
+ int res = 0;
+ int c;
+ test_paragraph *tp;
+ char *file_path = NULL;
+ unsigned int line_length = 500;
+ float tolerance = 1;
+ long int suite_to_launch = -1;
+ long int test_to_launch = -1;
+
+ while ((c = getopt (argc, argv, "f:l:s:t:T:")) != -1) {
+ switch (c)
+ {
+ case 'f':
+ file_path = optarg;
+ break;
+ case 'l':
+ line_length = strtol(optarg, NULL, 10);
+ break;
+ case 's':
+ suite_to_launch = strtol(optarg, NULL, 10);
+ break;
+ case 't':
+ test_to_launch = strtol(optarg, NULL, 10);
+ break;
+ case 'T':
+ tolerance = strtof(optarg, NULL);
+ break;
+ case '?':
+ if (optopt == 'f' || optopt == 's' || optopt == 't')
+ fprintf (stderr, "Option -%c requires an argument.\n", optopt);
+ else if (isprint(optopt))
+ fprintf (stderr, "Unknown option `-%c'.\n", optopt);
+ else
+ fprintf (stderr,
+ "Unknown option character `\\x%x'.\n",
+ optopt);
+ return 1;
+ default:
+ abort();
+ }
+ }
+
+ if (suite_to_launch == 0 && test_to_launch > 0) {
+ fprintf (stderr,
+ "Passing test number without test suite, please use option -s");
+ res = -1;
+ goto end;
+ }
+
+ if (file_path != NULL) {
+ text_loader *tl = new text_loader(NULL, file_path);
+ paragraph *par = new paragraph();
+ paragraph_printer *printer;
+ printf("Processing content of %s with tolerance:%.3f line length:%u\n\n",
+ file_path, tolerance, line_length);
+ tl->process_text(par, true);
+ par->format_knuth_plass(tolerance, line_length);
+ printer = new paragraph_printer(par);
+ printer->print();
+ delete printer;
+ delete par;
+ delete tl;
+ } else {
+ if (suite_to_launch == -1 || suite_to_launch == 1) {
+ tp = new test_paragraph();
+ tp->suite1_init();
+ if (test_to_launch == -1 || test_to_launch == 1)
+ res += tp->test11_original_example();
+ if (test_to_launch == -1 || test_to_launch == 2)
+ res += tp->test12_example_with_default_demerits_formula();
+ if (test_to_launch == -1 || test_to_launch == 3)
+ res += tp->test13_example_with_larger_tolerance();
+ delete tp;
+ }
+
+ if (suite_to_launch == -1 || suite_to_launch == 2) {
+ tp = new test_paragraph();
+ tp->suite2_init();
+ if (test_to_launch == -1 || test_to_launch == 1)
+ res += tp->test21_hyphen_flagged_penalty();
+ delete tp;
+ }
+
+ if (suite_to_launch == -1 || suite_to_launch == 3) {
+ tp = new test_paragraph();
+ tp->suite3_init();
+ if (test_to_launch == -1 || test_to_launch == 1)
+ res += tp->test31_fitness_class();
+ delete tp;
+ }
+
+ if (res == 0)
+ printf("All tests passed\n");
+ else
+ fprintf(stderr, "%d tests failed\n", res);
+ }
+
+end:
+ return res;
+}