1 #include <stic.h>
2 
3 #include <locale.h> /* setlocale() */
4 #include <stddef.h> /* NULL size_t */
5 #include <string.h>
6 
7 #include "../../src/utils/utf8.h"
8 #include "../../src/utils/utils.h"
9 #include "../../src/ui/column_view.h"
10 
11 #include "test.h"
12 
13 static void column_line_print(const void *data, int column_id, const char buf[],
14 		size_t offset, AlignType align);
15 static void column1_func(int id, const void *data, size_t buf_len, char buf[]);
16 static void column2_func(int id, const void *data, size_t buf_len, char buf[]);
17 static int locale_works(void);
18 
19 static const size_t MAX_WIDTH = 20;
20 static char print_buffer[80 + 1];
21 
22 static const char *col1_str;
23 
SETUP_ONCE()24 SETUP_ONCE()
25 {
26 	(void)setlocale(LC_ALL, "");
27 	if(!locale_works())
28 	{
29 		(void)setlocale(LC_ALL, "en_US.utf8");
30 	}
31 }
32 
SETUP()33 SETUP()
34 {
35 	print_next = &column_line_print;
36 	col1_next = &column1_func;
37 	col2_next = &column2_func;
38 
39 	col1_str = "师从螺丝刀йклмнопрстуфхцчшщьыъэюя";
40 }
41 
TEARDOWN()42 TEARDOWN()
43 {
44 	print_next = NULL;
45 	col1_next = NULL;
46 	col2_next = NULL;
47 }
48 
49 static void
column_line_print(const void * data,int column_id,const char buf[],size_t offset,AlignType align)50 column_line_print(const void *data, int column_id, const char buf[],
51 		size_t offset, AlignType align)
52 {
53 	strncpy(print_buffer +
54 			utf8_nstrsnlen(print_buffer, offset), buf, strlen(buf));
55 }
56 
57 static void
column1_func(int id,const void * data,size_t buf_len,char buf[])58 column1_func(int id, const void *data, size_t buf_len, char buf[])
59 {
60 	snprintf(buf, buf_len + 1, "%s", col1_str);
61 }
62 
63 static void
column2_func(int id,const void * data,size_t buf_len,char buf[])64 column2_func(int id, const void *data, size_t buf_len, char buf[])
65 {
66 	snprintf(buf, buf_len + 1, "%s", "яюэъыьщшчцхфутсрпонмлкйизжёедгв推");
67 }
68 
69 static void
perform_test(column_info_t column_infos[],size_t count,size_t max_width)70 perform_test(column_info_t column_infos[], size_t count, size_t max_width)
71 {
72 	size_t i;
73 
74 	columns_t *const cols = columns_create();
75 	for(i = 0U; i < count; ++i)
76 	{
77 		columns_add_column(cols, column_infos[i]);
78 	}
79 
80 	memset(print_buffer, '\0', sizeof(print_buffer));
81 	columns_format_line(cols, NULL, max_width);
82 
83 	columns_free(cols);
84 }
85 
TEST(not_truncating_short_utf8_ok,IF (locale_works))86 TEST(not_truncating_short_utf8_ok, IF(locale_works))
87 {
88 	static column_info_t column_infos[1] = {
89 		{ .column_id = COL1_ID, .full_width = 33UL,    .text_width = 33UL,
90 		  .align = AT_LEFT,     .sizing = ST_ABSOLUTE, .cropping = CT_TRUNCATE, },
91 	};
92 	static const char expected[] = "师从螺丝刀йклмнопрстуфхцчшщьыъэюя       ";
93 
94 	perform_test(column_infos, 1, 40);
95 
96 	assert_string_equal(expected, print_buffer);
97 }
98 
TEST(donot_add_ellipsis_short_utf8_ok,IF (locale_works))99 TEST(donot_add_ellipsis_short_utf8_ok, IF(locale_works))
100 {
101 	static column_info_t column_infos[1] = {
102 		{ .column_id = COL1_ID, .full_width = 33UL,    .text_width = 33UL,
103 		  .align = AT_LEFT,     .sizing = ST_ABSOLUTE, .cropping = CT_ELLIPSIS, },
104 	};
105 	static const char expected[] = "师从螺丝刀йклмнопрстуфхцчшщьыъэюя       ";
106 
107 	perform_test(column_infos, 1, 40);
108 
109 	assert_string_equal(expected, print_buffer);
110 }
111 
TEST(truncating_ok,IF (locale_works))112 TEST(truncating_ok, IF(locale_works))
113 {
114 	static column_info_t column_infos[2] = {
115 		{ .column_id = COL1_ID, .full_width = 10UL,    .text_width = 10UL,
116 		  .align = AT_LEFT,     .sizing = ST_ABSOLUTE, .cropping = CT_TRUNCATE, },
117 		{ .column_id = COL2_ID, .full_width = 10UL,    .text_width = 10UL,
118 		  .align = AT_RIGHT,    .sizing = ST_ABSOLUTE, .cropping = CT_TRUNCATE, },
119 	};
120 	static const char expected[] = "师从螺丝刀изжёедгв推";
121 
122 	perform_test(column_infos, 2, MAX_WIDTH);
123 
124 	assert_string_equal(expected, print_buffer);
125 }
126 
TEST(none_cropping_allows_for_correct_gaps,IF (locale_works))127 TEST(none_cropping_allows_for_correct_gaps, IF(locale_works))
128 {
129 	static column_info_t column_infos[2] = {
130 		{ .column_id = COL1_ID, .full_width = 10UL,    .text_width = 10UL,
131 		  .align = AT_LEFT,     .sizing = ST_AUTO,     .cropping = CT_NONE, },
132 		{ .column_id = COL2_ID, .full_width = 4UL,     .text_width = 4UL,
133 		  .align = AT_RIGHT,    .sizing = ST_AUTO,     .cropping = CT_NONE, },
134 	};
135 	static const char expected[] = "师从 яюэъыьщшчцхфутсрпонмлкйизжёедгв推";
136 
137 	perform_test(column_infos, 2, 38);
138 
139 	assert_string_equal(expected, print_buffer);
140 }
141 
TEST(add_ellipsis_ok,IF (locale_works))142 TEST(add_ellipsis_ok, IF(locale_works))
143 {
144 	static column_info_t column_infos[2] = {
145 		{ .column_id = COL1_ID, .full_width = 10UL,    .text_width = 10UL,
146 		  .align = AT_LEFT,     .sizing = ST_ABSOLUTE, .cropping = CT_ELLIPSIS, },
147 		{ .column_id = COL2_ID, .full_width = 10UL,    .text_width = 10UL,
148 		  .align = AT_RIGHT,    .sizing = ST_ABSOLUTE, .cropping = CT_ELLIPSIS, },
149 	};
150 	static const char expected[] = "师从螺... ...ёедгв推";
151 
152 	perform_test(column_infos, 2, MAX_WIDTH);
153 
154 	assert_string_equal(expected, print_buffer);
155 }
156 
TEST(wide_ellipsis_work,IF (locale_works))157 TEST(wide_ellipsis_work, IF(locale_works))
158 {
159 	static column_info_t column_infos[2] = {
160 		{ .column_id = COL1_ID, .full_width = 10UL,    .text_width = 10UL,
161 		  .align = AT_LEFT,     .sizing = ST_ABSOLUTE, .cropping = CT_ELLIPSIS, },
162 		{ .column_id = COL2_ID, .full_width = 10UL,    .text_width = 10UL,
163 		  .align = AT_RIGHT,    .sizing = ST_ABSOLUTE, .cropping = CT_ELLIPSIS, },
164 	};
165 	static const char expected[] = "师从螺丝… …зжёедгв推";
166 
167 	columns_set_ellipsis("…");
168 	perform_test(column_infos, 2, MAX_WIDTH);
169 
170 	assert_string_equal(expected, print_buffer);
171 }
172 
TEST(filling,IF (locale_works))173 TEST(filling, IF(locale_works))
174 {
175 	static column_info_t column_infos[1] = {
176 		{ .column_id = COL1_ID, .full_width = 0UL, .text_width = 0UL,
177 		  .align = AT_LEFT,     .sizing = ST_AUTO, .cropping = CT_NONE, },
178 	};
179 	static const char expected[] = "师从螺丝刀йклмнопрстуфхцчшщьыъэюя       ";
180 
181 	columns_t *const cols = columns_create();
182 	columns_add_column(cols, column_infos[0]);
183 
184 	memset(print_buffer, '\0', 80);
185 	columns_format_line(cols, NULL, 40);
186 
187 	columns_free(cols);
188 
189 	assert_string_equal(expected, print_buffer);
190 }
191 
TEST(right_filling,IF (locale_works))192 TEST(right_filling, IF(locale_works))
193 {
194 	static column_info_t column_infos[1] = {
195 		{ .column_id = COL2_ID, .full_width = 0UL, .text_width = 0UL,
196 		  .align = AT_RIGHT,    .sizing = ST_AUTO, .cropping = CT_ELLIPSIS, },
197 	};
198 	static const char expected[] = ".";
199 
200 	columns_t *const cols = columns_create();
201 	columns_add_column(cols, column_infos[0]);
202 
203 	memset(print_buffer, '\0', 80);
204 	columns_format_line(cols, NULL, 1);
205 
206 	columns_free(cols);
207 
208 	assert_string_equal(expected, print_buffer);
209 }
210 
TEST(wide_right_ellipsis_ok,IF (locale_works))211 TEST(wide_right_ellipsis_ok, IF(locale_works))
212 {
213 	/* A gap might appear after removing several characters to insert ellipsis in
214 	 * their place, make sure that it's filled with spaces to obtain requested
215 	 * width. */
216 
217 	static column_info_t column_infos[1] = {
218 		{ .column_id = COL1_ID, .full_width = 0UL, .text_width = 0UL,
219 		  .align = AT_RIGHT,    .sizing = ST_AUTO, .cropping = CT_ELLIPSIS, },
220 	};
221 	static const char expected[] = " ...螺丝刀师从螺丝刀";
222 
223 	col1_str = ",师从螺丝刀师从螺丝刀";
224 	perform_test(column_infos, ARRAY_LEN(column_infos), MAX_WIDTH);
225 
226 	assert_string_equal(expected, print_buffer);
227 }
228 
229 static int
locale_works(void)230 locale_works(void)
231 {
232 	return (vifm_wcwidth(L'丝') == 2);
233 }
234 
235 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
236 /* vim: set cinoptions+=t0 filetype=c : */
237