1
/* libinfinity - a GObject-based infinote implementation
2
* Copyright (C) 2007-2011 Armin Burgmeier <armin@arbur.net>
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, write to the Free
16
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
20
/* Cuts away front and back of a replay, so that it still fails. It's very
21
* primitive, and more sophisticated methods can still be implemented. */
23
/* TODO: Break as soon as either (stderr) output or exit status changes */
25
#include "util/inf-test-util.h"
27
#include <libinftext/inf-text-session.h>
28
#include <libinftext/inf-text-default-buffer.h>
29
#include <libinfinity/client/infc-note-plugin.h>
30
#include <libinfinity/adopted/inf-adopted-session-replay.h>
31
#include <libinfinity/common/inf-xml-util.h>
32
#include <libinfinity/common/inf-init.h>
34
#include <glib/gstdio.h>
37
# include <sys/wait.h>
41
static const gchar REPLAY[] = ".libs/inf-test-text-replay";
44
inf_test_reduce_replay_session_new(InfIo* io,
45
InfCommunicationManager* manager,
46
InfSessionStatus status,
47
InfCommunicationJoinedGroup* sync_group,
48
InfXmlConnection* sync_connection,
51
InfTextDefaultBuffer* buffer;
52
InfTextSession* session;
54
buffer = inf_text_default_buffer_new("UTF-8");
55
session = inf_text_session_new(
57
INF_TEXT_BUFFER(buffer),
60
INF_COMMUNICATION_GROUP(sync_group),
63
g_object_unref(buffer);
65
return INF_SESSION(session);
68
/* TODO: This should maybe go to inf-test-util */
69
static const InfcNotePlugin INF_TEST_REDUCE_REPLAY_TEXT_PLUGIN = {
70
NULL, "InfText", inf_test_reduce_replay_session_new
74
inf_test_reduce_replay_find_node(xmlNodePtr xml,
78
for(child = xml->children; child != NULL; child = child->next)
79
if(child->type == XML_ELEMENT_NODE)
80
if(strcmp((const char*)child->name, name) == 0)
86
inf_test_reduce_replay_next_node(xmlNodePtr xml)
91
} while(xml && xml->type != XML_ELEMENT_NODE);
97
inf_test_reduce_replay_first_node(xmlNodePtr xml)
99
while(xml && xml->type != XML_ELEMENT_NODE)
105
inf_test_reduce_replay_validate_test(xmlDocPtr doc)
116
root = xmlDocGetRootElement(doc);
117
cur = inf_test_reduce_replay_find_node(root, "initial");
120
table = g_hash_table_new(NULL, NULL);
122
while( (cur = inf_test_reduce_replay_next_node(cur)) != NULL)
124
if(strcmp((const char*)cur->name, "request") == 0)
126
child = cur->children;
128
if(child->type != XML_ELEMENT_NODE)
129
child = inf_test_reduce_replay_next_node(child);
131
if(!inf_xml_util_get_attribute_uint_required(cur, "user",
134
g_hash_table_unref(table);
138
count = GPOINTER_TO_UINT(
139
g_hash_table_lookup(table, GUINT_TO_POINTER(user_id))
142
count_cur = (count ) & 0xffff;
143
count_max = (count >> 16) & 0xffff;
145
/* move and noop requests don't affect the buffer */
146
if(strcmp((const char*)child->name, "move") != 0 &&
147
strcmp((const char*)child->name, "no-op") != 0)
149
if(strcmp((const char*)child->name, "undo") == 0 ||
150
strcmp((const char*)child->name, "undo-caret") == 0)
154
g_hash_table_unref(table);
160
else if(strcmp((const char*)child->name, "redo") == 0 ||
161
strcmp((const char*)child->name, "redo-caret") == 0)
163
if(count_cur == count_max)
165
g_hash_table_unref(table);
174
if(count_max >= 0xffff)
177
g_hash_table_unref(table);
181
count_max = count_cur + 1;
182
count_cur = count_max;
188
GUINT_TO_POINTER(user_id),
189
GUINT_TO_POINTER((count_max >> 16) | count_cur)
194
g_hash_table_unref(table);
199
inf_test_reduce_replay_run_test(xmlDocPtr doc)
207
xmlSaveFile("test.xml", doc);
208
/*cmd = g_strdup_printf("(%s %s 2>&1) > /dev/null", REPLAY, "test.xml");*/
209
cmd = g_strdup_printf("%s %s", REPLAY, "test.xml");
211
/* make it die on algorithm errors */
212
g_setenv("G_DEBUG", "fatal-warnings", TRUE);
215
if(!g_spawn_command_line_sync(cmd, &stdout_buf, &stderr_buf, &ret, &error))
217
g_unlink("test.xml");
219
fprintf(stderr, "Failed to run test: %s\n", error->message);
224
/* Reset, so that we don't die ourselves */
225
g_setenv("G_DEBUG", "", TRUE);
227
/* These are just dummy variables to suppress the console output */
231
/*g_unlink("test.xml");*/
234
if(WIFSIGNALED(ret) &&
235
(WTERMSIG(ret) == SIGABRT || WTERMSIG(ret) == SIGSEGV))
239
else if(WIFEXITED(ret))
250
g_assert_not_reached();
255
inf_test_reduce_replay_remove_sync_requests(xmlNodePtr initial)
260
xmlNodePtr sync_begin;
264
for(child = inf_test_reduce_replay_first_node(initial->children);
265
child != NULL; child = next)
267
next = inf_test_reduce_replay_next_node(child);
268
if(strcmp((const char*)child->name, "sync-request") == 0)
280
if(strcmp((const char*)child->name, "sync-begin") == 0)
282
else if(strcmp((const char*)child->name, "sync-end") != 0)
287
g_assert(sync_begin != NULL);
288
inf_xml_util_set_attribute_uint(sync_begin, "num-messages", count);
292
inf_test_reduce_replay_reduce(xmlDocPtr doc,
293
const char* filename)
295
InfAdoptedSessionReplay* local_replay;
296
InfAdoptedSession* session;
297
InfSessionClass* session_class;
306
xmlNodePtr sync_begin;
310
root = xmlDocGetRootElement(doc);
311
if(inf_test_reduce_replay_run_test(doc) == TRUE)
313
fprintf(stderr, "Test does not initially fail\n");
317
if(!inf_test_reduce_replay_validate_test(doc))
319
fprintf(stderr, "Test does not initially validate\n");
323
initial = inf_test_reduce_replay_find_node(root, "initial");
326
fprintf(stderr, "Test has no initial\n");
330
/* Remove all sync-requests. We require test to work without for now. */
331
inf_test_reduce_replay_remove_sync_requests(initial);
333
root = xmlDocGetRootElement(doc);
334
if(inf_test_reduce_replay_run_test(doc) == TRUE)
336
fprintf(stderr, "Test does not fail without sync-requests anymore\n");
340
/* Initialize local replay */
342
local_replay = inf_adopted_session_replay_new();
343
inf_adopted_session_replay_set_record(
346
&INF_TEST_REDUCE_REPLAY_TEXT_PLUGIN,
350
session = inf_adopted_session_replay_get_session(local_replay);
351
session_class = INF_SESSION_GET_CLASS(session);
355
fprintf(stderr, "Creating local replay failed: %s\n", error->message);
360
last_fail = xmlCopyDoc(doc, 1);
361
request = inf_test_reduce_replay_next_node(initial);
366
/* Play next request */
367
if(inf_adopted_session_replay_play_next(local_replay, &error))
370
fprintf(stderr, "%.6u... ", i);
373
if(strcmp((const char*)request->name, "request") != 0 &&
374
strcmp((const char*)request->name, "user") != 0)
376
fprintf(stderr, "NOREQ <%s>\n", request->name);
377
request = inf_test_reduce_replay_next_node(request);
381
fprintf(stderr, "REQ %8s ", request->name);
384
/* Get rid of next request, and see if test still fails */
387
next = request->next;
388
xmlUnlinkNode(request);
389
xmlFreeNode(request);
391
} while(next && next->type != XML_ELEMENT_NODE);
393
/* Rewrite initial */
394
xmlFreeNodeList(initial->children);
395
initial->children = NULL;
398
xmlNewChild(initial, NULL, (const xmlChar*)"sync-begin", NULL);
399
session_class->to_xml_sync(INF_SESSION(session), initial);
400
xmlNewChild(initial, NULL, (const xmlChar*)"sync-end", NULL);
401
/* this sets num-messages: */
402
inf_test_reduce_replay_remove_sync_requests(initial);
404
if(inf_test_reduce_replay_validate_test(doc))
406
if(inf_test_reduce_replay_run_test(doc))
408
fprintf(stderr, "OK!\n");
414
fprintf(stderr, "FAIL\n");
415
xmlFreeDoc(last_fail);
416
last_fail = xmlCopyDoc(doc, 1);
421
/* Continue when test is invalid; we probably removed an undo's
422
* associated request, so just wait until we remove the undo request
424
fprintf(stderr, "INVALID\n");
432
fprintf(stderr, "Playing local replay failed: %s\n", error->message);
439
fprintf(stderr, "Played all records and the error still occurs\n");
446
g_object_unref(local_replay);
450
/* Also reduce from back */
453
back_doc = xmlCopyDoc(last_fail, 1);
454
root = xmlDocGetRootElement(back_doc);
455
initial = inf_test_reduce_replay_find_node(root, "initial");
462
next = inf_test_reduce_replay_next_node(request);
464
} while(next != NULL);
471
fprintf(stderr, "%.6u... ", i);
477
request = request->prev;
480
} while(request && request->type != XML_ELEMENT_NODE);
482
if(inf_test_reduce_replay_validate_test(back_doc))
484
if(inf_test_reduce_replay_run_test(back_doc))
486
fprintf(stderr, "OK!\n");
492
fprintf(stderr, "FAIL\n");
493
xmlFreeDoc(last_fail);
494
last_fail = xmlCopyDoc(back_doc, 1);
499
fprintf(stderr, "INVALID\n");
505
xmlFreeDoc(back_doc);
508
/* Save last failing record in each case */
509
xmlSaveFile("last_fail.record.xml", last_fail);
510
printf("Last failing record in last_fail.record.xml\n");
511
xmlFreeDoc(last_fail);
515
int main(int argc, char* argv[])
517
GError* error = NULL;
521
if(!inf_init(&error))
523
fprintf(stderr, "%s\n", error->message);
527
if(!g_file_test(REPLAY, G_FILE_TEST_IS_EXECUTABLE))
529
fprintf(stderr, "Replay tool not available. Run \"make\" first.");
535
fprintf(stderr, "Usage: %s <record-file>\n", argv[0]);
539
doc = xmlReadFile(argv[1], "UTF-8", XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
540
if(!doc || !xmlDocGetRootElement(doc))
542
if(doc) xmlFreeDoc(doc);
543
fprintf(stderr, "%s\n", xmlGetLastError()->message);
547
ret = inf_test_reduce_replay_reduce(doc, argv[1]);
553
/* vim:set et sw=2 ts=2: */