1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 3 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see <https://www.gnu.org/licenses/>.
14 *
15 * Copyright (C) 2017 Jon Nordby <jononor@gmail.com>
16 */
17
18 #include <gegl.h>
19 #include <glib/gstdio.h>
20
21 // in order of progression
22 typedef enum _TestState {
23 TestInvalid = 0,
24 TestInitialized,
25 TestSetBlue,
26 TestWaitingForBlue,
27 TestAssertBlue,
28 TestSetYellow,
29 TestWaitingForYellow,
30 TestAssertYellow,
31 TestFailed,
32 TestSucceed,
33 } TestState;
34
35 typedef struct _TestData {
36 GMainLoop *loop;
37 GeglBuffer *a;
38 GeglBuffer *b;
39 TestState state;
40 gchar *temp_dir;
41 gchar *file_path;
42 } TestData;
43
44 static void
print_color(GeglColor * color)45 print_color(GeglColor *color) {
46 unsigned char rgba[4];
47 const Babl *format = babl_format("R'G'B'A u8");
48 gegl_color_get_pixel(color, format, (gpointer)rgba);
49 g_print("[%d, %d, %d, %d]",
50 rgba[0], rgba[1], rgba[2], rgba[3]);
51 }
52
53 static gboolean
assert_color_equal(GeglColor * expect,GeglColor * actual)54 assert_color_equal(GeglColor *expect, GeglColor *actual) {
55 const Babl *format = babl_format("R'G'B'A u8");
56 unsigned char e[4];
57 unsigned char a[4];
58 gegl_color_get_pixel(expect, format, (gpointer)e);
59 gegl_color_get_pixel(actual, format, (gpointer)a);
60 {
61 const gboolean equal =
62 a[0] == e[0] &&
63 a[1] == e[1] &&
64 a[2] == e[2] &&
65 a[3] == e[3];
66
67 if (!equal) {
68 print_color(expect);
69 g_print(" != ");
70 print_color(actual);
71 g_print("\n");
72 return FALSE;
73 }
74 }
75 return TRUE;
76 }
77
78 static GeglColor *
buffer_get_color(GeglBuffer * buffer)79 buffer_get_color(GeglBuffer *buffer) {
80 const int pixels = 1;
81 guint8 pixel[4];
82 GeglRectangle r = { 0, 0, 1, pixels };
83 const Babl *format = babl_format("R'G'B'A u8");
84 GeglColor *color = gegl_color_new(NULL);
85 gegl_buffer_get(buffer, &r, 1.0, format, (gpointer)(pixel), GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
86 gegl_color_set_pixel(color, format, (gpointer)pixel);
87 return color;
88 }
89
90 // Core state-machine
91 static void
test_change_state(TestData * data,TestState new)92 test_change_state(TestData *data, TestState new) {
93 GeglColor *blue = gegl_color_new("blue");
94 GeglColor *yellow = gegl_color_new("yellow");
95 GeglRectangle rect = { 0, 0, 100, 100 };
96
97 data->state = new;
98
99 switch (data->state) {
100 // test actions and checking
101 case TestSetBlue:
102 // Write blue to A, should be reflected in B
103 gegl_buffer_set_extent(data->a, &rect);
104 gegl_buffer_set_color(data->a, &rect, blue);
105 gegl_buffer_flush(data->a);
106 test_change_state(data, TestWaitingForBlue);
107 break;
108 case TestAssertBlue:
109 {
110 GeglColor *actual = buffer_get_color(data->a);
111 const gboolean pass = assert_color_equal(blue, actual);
112 test_change_state(data, (pass) ? TestSetYellow : TestFailed);
113 }
114 break;
115 case TestSetYellow:
116 // Write blue to A, should be reflected in B
117 gegl_buffer_set_extent(data->a, &rect);
118 gegl_buffer_set_color(data->a, &rect, yellow);
119 gegl_buffer_flush(data->a);
120 test_change_state(data, TestWaitingForYellow);
121 break;
122 case TestAssertYellow:
123 {
124 GeglColor *actual = buffer_get_color(data->a);
125 const gboolean pass = assert_color_equal(yellow, actual);
126 test_change_state(data, (pass) ? TestSucceed : TestFailed);
127 }
128 break;
129 // handled elsewhere
130 case TestWaitingForYellow:
131 case TestInitialized:
132 case TestWaitingForBlue:
133 break;
134 // exit
135 case TestInvalid:
136 case TestFailed:
137 case TestSucceed:
138 g_main_loop_quit(data->loop);
139 break;
140 }
141 }
142
143 static void
on_buffer_changed(GeglBuffer * buffer,const GeglRectangle * rect,gpointer user_data)144 on_buffer_changed(GeglBuffer *buffer,
145 const GeglRectangle *rect,
146 gpointer user_data)
147 {
148 TestData *test = (TestData *)user_data;
149 //g_print("changed! %d %d %d %d \n", rect->x, rect->y, rect->width, rect->height);
150
151 if (test->state == TestWaitingForBlue) {
152 test_change_state(test, TestAssertBlue);
153 } else if (test->state == TestWaitingForYellow) {
154 test_change_state(test, TestAssertYellow);
155 } else {
156 test_change_state(test, TestFailed);
157 }
158 }
159
160 static gboolean
on_timeout(gpointer user_data)161 on_timeout(gpointer user_data) {
162 TestData *test = (TestData *)user_data;
163 g_print("timeout!\n");
164 test_change_state(test, TestFailed);
165 return FALSE;
166 }
167
168 #include <unistd.h>
169
170 static void
test_init(TestData * data)171 test_init(TestData *data) {
172 GeglRectangle rect = { 0, 0, 100, 100 };
173 GeglColor *blank = gegl_color_new("transparent");
174
175 data->loop = g_main_loop_new(NULL, TRUE);
176 data->temp_dir = g_strdup("test-buffer-sharing-XXXXXX");
177 data->temp_dir = g_mkdtemp(data->temp_dir);
178 data->file_path = g_strjoin(G_DIR_SEPARATOR_S, data->temp_dir, "buffer.gegl", NULL);
179
180 data->a = gegl_buffer_open(data->file_path);
181 // FIXME: if not setting an extent and adding some data, the written on-disk file seems to be corrupt
182 gegl_buffer_set_extent(data->a, &rect);
183 gegl_buffer_set_color(data->a, &rect, blank);
184 gegl_buffer_flush(data->a); // ensure file exists on disk
185
186 sleep(1);
187
188 // B observes the same on-disk buffer
189 data->b = gegl_buffer_open(data->file_path);
190 data->state = TestInitialized;
191
192 gegl_buffer_signal_connect(data->b, "changed", (void*)on_buffer_changed, data);
193 g_timeout_add_seconds(10, on_timeout, data);
194 }
195
196 static void
test_destroy(TestData * data)197 test_destroy(TestData *data) {
198
199 g_remove(data->file_path);
200 g_free(data->file_path);
201
202 g_rmdir(data->temp_dir);
203 g_free(data->temp_dir);
204
205 g_object_unref(data->a);
206 g_object_unref(data->b);
207 }
208
209 int
main(int argc,char * argv[])210 main (int argc,
211 char *argv[])
212 {
213 int exitcode;
214 TestData test;
215 TestData *data = &test;
216 gegl_init (&argc, &argv);
217
218 test_init(data);
219
220 test_change_state(data, TestSetBlue);
221
222 g_main_loop_run(data->loop);
223 exitcode = (data->state == TestSucceed) ? 0 : 1;
224 g_print("%s\n", (data->state == TestSucceed) ? "PASS" : "FAIL");
225 test_destroy(data);
226 return exitcode;
227 }
228