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