~ubuntu-branches/ubuntu/oneiric/libinfinity/oneiric

« back to all changes in this revision

Viewing changes to test/inf-test-reduce-replay.c

  • Committer: Bazaar Package Importer
  • Author(s): Philipp Kern
  • Date: 2011-04-03 15:50:33 UTC
  • mfrom: (1.3.4 upstream)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: james.westby@ubuntu.com-20110403155033-7f5ud651l9ctzpys
Tags: upstream-0.5.0
ImportĀ upstreamĀ versionĀ 0.5.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* libinfinity - a GObject-based infinote implementation
 
2
 * Copyright (C) 2007-2011 Armin Burgmeier <armin@arbur.net>
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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,
 
17
 * MA 02110-1301, USA.
 
18
 */
 
19
 
 
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. */
 
22
 
 
23
/* TODO: Break as soon as either (stderr) output or exit status changes */
 
24
 
 
25
#include "util/inf-test-util.h"
 
26
 
 
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>
 
33
 
 
34
#include <glib/gstdio.h>
 
35
 
 
36
#ifndef G_OS_WIN32
 
37
# include <sys/wait.h>
 
38
#endif
 
39
#include <string.h>
 
40
 
 
41
static const gchar REPLAY[] = ".libs/inf-test-text-replay";
 
42
 
 
43
static InfSession*
 
44
inf_test_reduce_replay_session_new(InfIo* io,
 
45
                                   InfCommunicationManager* manager,
 
46
                                   InfSessionStatus status,
 
47
                                   InfCommunicationJoinedGroup* sync_group,
 
48
                                   InfXmlConnection* sync_connection,
 
49
                                   gpointer user_data)
 
50
{
 
51
  InfTextDefaultBuffer* buffer;
 
52
  InfTextSession* session;
 
53
 
 
54
  buffer = inf_text_default_buffer_new("UTF-8");
 
55
  session = inf_text_session_new(
 
56
    manager,
 
57
    INF_TEXT_BUFFER(buffer),
 
58
    io,
 
59
    status,
 
60
    INF_COMMUNICATION_GROUP(sync_group),
 
61
    sync_connection
 
62
  );
 
63
  g_object_unref(buffer);
 
64
 
 
65
  return INF_SESSION(session);
 
66
}
 
67
 
 
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
 
71
};
 
72
 
 
73
static xmlNodePtr
 
74
inf_test_reduce_replay_find_node(xmlNodePtr xml,
 
75
                                 const gchar* name)
 
76
{
 
77
  xmlNodePtr child;
 
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)
 
81
        return child;
 
82
  return NULL;
 
83
}
 
84
 
 
85
static xmlNodePtr
 
86
inf_test_reduce_replay_next_node(xmlNodePtr xml)
 
87
{
 
88
  do
 
89
  {
 
90
    xml = xml->next;
 
91
  } while(xml && xml->type != XML_ELEMENT_NODE);
 
92
 
 
93
  return xml;
 
94
}
 
95
 
 
96
static xmlNodePtr
 
97
inf_test_reduce_replay_first_node(xmlNodePtr xml)
 
98
{
 
99
  while(xml && xml->type != XML_ELEMENT_NODE)
 
100
    xml = xml->next;
 
101
  return xml;
 
102
}
 
103
 
 
104
static gboolean
 
105
inf_test_reduce_replay_validate_test(xmlDocPtr doc)
 
106
{
 
107
  GHashTable* table;
 
108
  xmlNodePtr root;
 
109
  xmlNodePtr cur;
 
110
  xmlNodePtr child;
 
111
  guint user_id;
 
112
  guint count;
 
113
  guint count_cur;
 
114
  guint count_max;
 
115
 
 
116
  root = xmlDocGetRootElement(doc);
 
117
  cur = inf_test_reduce_replay_find_node(root, "initial");
 
118
  g_assert(cur);
 
119
 
 
120
  table = g_hash_table_new(NULL, NULL);
 
121
 
 
122
  while( (cur = inf_test_reduce_replay_next_node(cur)) != NULL)
 
123
  {
 
124
    if(strcmp((const char*)cur->name, "request") == 0)
 
125
    {
 
126
      child = cur->children;
 
127
      g_assert(child);
 
128
      if(child->type != XML_ELEMENT_NODE)
 
129
        child = inf_test_reduce_replay_next_node(child);
 
130
 
 
131
      if(!inf_xml_util_get_attribute_uint_required(cur, "user",
 
132
                                                   &user_id, NULL))
 
133
      {
 
134
        g_hash_table_unref(table);
 
135
        return FALSE;
 
136
      }
 
137
 
 
138
      count = GPOINTER_TO_UINT(
 
139
        g_hash_table_lookup(table, GUINT_TO_POINTER(user_id))
 
140
      );
 
141
 
 
142
      count_cur = (count      ) & 0xffff;
 
143
      count_max = (count >> 16) & 0xffff;
 
144
 
 
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)
 
148
      {
 
149
        if(strcmp((const char*)child->name, "undo") == 0 ||
 
150
           strcmp((const char*)child->name, "undo-caret") == 0)
 
151
        {
 
152
          if(count_cur == 0)
 
153
          {
 
154
            g_hash_table_unref(table);
 
155
            return FALSE;
 
156
          }
 
157
 
 
158
          --count_cur;
 
159
        }
 
160
        else if(strcmp((const char*)child->name, "redo") == 0 ||
 
161
                strcmp((const char*)child->name, "redo-caret") == 0)
 
162
        {
 
163
          if(count_cur == count_max)
 
164
          {
 
165
            g_hash_table_unref(table);
 
166
            return FALSE;
 
167
          }
 
168
 
 
169
          ++count_cur;
 
170
        }
 
171
        else
 
172
        {
 
173
          /* do */
 
174
          if(count_max >= 0xffff)
 
175
          {
 
176
            /* overflow */
 
177
            g_hash_table_unref(table);
 
178
            return FALSE;
 
179
          }
 
180
 
 
181
          count_max = count_cur + 1;
 
182
          count_cur = count_max;
 
183
        }
 
184
      }
 
185
 
 
186
      g_hash_table_insert(
 
187
        table,
 
188
        GUINT_TO_POINTER(user_id),
 
189
        GUINT_TO_POINTER((count_max >> 16) | count_cur)
 
190
      );
 
191
    }
 
192
  }
 
193
 
 
194
  g_hash_table_unref(table);
 
195
  return TRUE;
 
196
}
 
197
 
 
198
static gboolean
 
199
inf_test_reduce_replay_run_test(xmlDocPtr doc)
 
200
{
 
201
  GError* error;
 
202
  gchar* cmd;
 
203
  gchar* stdout_buf;
 
204
  gchar* stderr_buf;
 
205
  int ret;
 
206
 
 
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");
 
210
 
 
211
  /* make it die on algorithm errors */
 
212
  g_setenv("G_DEBUG", "fatal-warnings", TRUE);
 
213
 
 
214
  error = NULL;
 
215
  if(!g_spawn_command_line_sync(cmd, &stdout_buf, &stderr_buf, &ret, &error))
 
216
  {
 
217
    g_unlink("test.xml");
 
218
    g_free(cmd);
 
219
    fprintf(stderr, "Failed to run test: %s\n", error->message);
 
220
    g_error_free(error);
 
221
    return FALSE;
 
222
  }
 
223
 
 
224
  /* Reset, so that we don't die ourselves */
 
225
  g_setenv("G_DEBUG", "", TRUE);
 
226
 
 
227
  /* These are just dummy variables to suppress the console output */
 
228
  g_free(stdout_buf);
 
229
  g_free(stderr_buf);
 
230
 
 
231
  /*g_unlink("test.xml");*/
 
232
 
 
233
#ifndef G_OS_WIN32
 
234
  if(WIFSIGNALED(ret) &&
 
235
     (WTERMSIG(ret) == SIGABRT || WTERMSIG(ret) == SIGSEGV))
 
236
  {
 
237
    return FALSE;
 
238
  }
 
239
  else if(WIFEXITED(ret))
 
240
  {
 
241
    if(WEXITSTATUS(ret))
 
242
      return FALSE;
 
243
    else
 
244
      return TRUE;
 
245
  }
 
246
  else
 
247
#endif
 
248
  {
 
249
    /* what happen? */
 
250
    g_assert_not_reached();
 
251
  }
 
252
}
 
253
 
 
254
static void
 
255
inf_test_reduce_replay_remove_sync_requests(xmlNodePtr initial)
 
256
{
 
257
  xmlNodePtr child;
 
258
  xmlNodePtr next;
 
259
  xmlNodePtr prev;
 
260
  xmlNodePtr sync_begin;
 
261
  guint count;
 
262
 
 
263
  count = 0;
 
264
  for(child = inf_test_reduce_replay_first_node(initial->children);
 
265
      child != NULL; child = next)
 
266
  {
 
267
    next = inf_test_reduce_replay_next_node(child);
 
268
    if(strcmp((const char*)child->name, "sync-request") == 0)
 
269
    {
 
270
      while(child != next)
 
271
      {
 
272
        prev = child;
 
273
        child = child->next;
 
274
        xmlUnlinkNode(prev);
 
275
        xmlFreeNode(prev);
 
276
      }
 
277
    }
 
278
    else
 
279
    {
 
280
      if(strcmp((const char*)child->name, "sync-begin") == 0)
 
281
        sync_begin = child;
 
282
      else if(strcmp((const char*)child->name, "sync-end") != 0)
 
283
        ++count;
 
284
    }
 
285
  }
 
286
 
 
287
  g_assert(sync_begin != NULL);
 
288
  inf_xml_util_set_attribute_uint(sync_begin, "num-messages", count);
 
289
}
 
290
 
 
291
static gboolean
 
292
inf_test_reduce_replay_reduce(xmlDocPtr doc,
 
293
                              const char* filename)
 
294
{
 
295
  InfAdoptedSessionReplay* local_replay;
 
296
  InfAdoptedSession* session;
 
297
  InfSessionClass* session_class;
 
298
  xmlDocPtr last_fail;
 
299
  xmlDocPtr back_doc;
 
300
  gboolean result;
 
301
 
 
302
  xmlNodePtr root;
 
303
  xmlNodePtr initial;
 
304
  xmlNodePtr next;
 
305
  xmlNodePtr request;
 
306
  xmlNodePtr sync_begin;
 
307
  GError* error;
 
308
  guint i;
 
309
 
 
310
  root = xmlDocGetRootElement(doc);
 
311
  if(inf_test_reduce_replay_run_test(doc) == TRUE)
 
312
  {
 
313
    fprintf(stderr, "Test does not initially fail\n");
 
314
    return FALSE;
 
315
  }
 
316
 
 
317
  if(!inf_test_reduce_replay_validate_test(doc))
 
318
  {
 
319
    fprintf(stderr, "Test does not initially validate\n");
 
320
    return FALSE;
 
321
  }
 
322
 
 
323
  initial = inf_test_reduce_replay_find_node(root, "initial");
 
324
  if(!initial)
 
325
  {
 
326
    fprintf(stderr, "Test has no initial\n");
 
327
    return FALSE;
 
328
  }
 
329
 
 
330
  /* Remove all sync-requests. We require test to work without for now. */
 
331
  inf_test_reduce_replay_remove_sync_requests(initial);
 
332
 
 
333
  root = xmlDocGetRootElement(doc);
 
334
  if(inf_test_reduce_replay_run_test(doc) == TRUE)
 
335
  {
 
336
    fprintf(stderr, "Test does not fail without sync-requests anymore\n");
 
337
    return FALSE;
 
338
  }
 
339
 
 
340
  /* Initialize local replay */
 
341
  error = NULL;
 
342
  local_replay = inf_adopted_session_replay_new();
 
343
  inf_adopted_session_replay_set_record(
 
344
    local_replay,
 
345
    filename,
 
346
    &INF_TEST_REDUCE_REPLAY_TEXT_PLUGIN,
 
347
    &error
 
348
  );
 
349
 
 
350
  session = inf_adopted_session_replay_get_session(local_replay);
 
351
  session_class = INF_SESSION_GET_CLASS(session);
 
352
 
 
353
  if(error)
 
354
  {
 
355
    fprintf(stderr, "Creating local replay failed: %s\n", error->message);
 
356
    g_error_free(error);
 
357
    return FALSE;
 
358
  }
 
359
 
 
360
  last_fail = xmlCopyDoc(doc, 1);
 
361
  request = inf_test_reduce_replay_next_node(initial);
 
362
 
 
363
  i = 0;
 
364
  for(;;)
 
365
  {
 
366
    /* Play next request */
 
367
    if(inf_adopted_session_replay_play_next(local_replay, &error))
 
368
    {
 
369
      ++i;
 
370
      fprintf(stderr, "%.6u... ", i);
 
371
      fflush(stderr);
 
372
 
 
373
      if(strcmp((const char*)request->name, "request") != 0 &&
 
374
         strcmp((const char*)request->name, "user") != 0)
 
375
      {
 
376
        fprintf(stderr, "NOREQ <%s>\n", request->name);
 
377
        request = inf_test_reduce_replay_next_node(request);
 
378
      }
 
379
      else
 
380
      {
 
381
        fprintf(stderr, "REQ %8s  ", request->name);
 
382
        fflush(stderr);
 
383
 
 
384
        /* Get rid of next request, and see if test still fails */
 
385
        do
 
386
        {
 
387
          next = request->next;
 
388
          xmlUnlinkNode(request);
 
389
          xmlFreeNode(request);
 
390
          request = next;
 
391
        } while(next && next->type != XML_ELEMENT_NODE);
 
392
 
 
393
        /* Rewrite initial */
 
394
        xmlFreeNodeList(initial->children);
 
395
        initial->children = NULL;
 
396
 
 
397
        sync_begin =
 
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);
 
403
 
 
404
        if(inf_test_reduce_replay_validate_test(doc))
 
405
        {
 
406
          if(inf_test_reduce_replay_run_test(doc))
 
407
          {
 
408
            fprintf(stderr, "OK!\n");
 
409
            result = TRUE;
 
410
            break;
 
411
          }
 
412
          else
 
413
          {
 
414
            fprintf(stderr, "FAIL\n");
 
415
            xmlFreeDoc(last_fail);
 
416
            last_fail = xmlCopyDoc(doc, 1);
 
417
          }
 
418
        }
 
419
        else
 
420
        {
 
421
          /* Continue when test is invalid; we probably removed an undo's
 
422
           * associated request, so just wait until we remove the undo request
 
423
           * itself. */
 
424
          fprintf(stderr, "INVALID\n");
 
425
        }
 
426
      }
 
427
    }
 
428
    else
 
429
    {
 
430
      if(error)
 
431
      {
 
432
        fprintf(stderr, "Playing local replay failed: %s\n", error->message);
 
433
        g_error_free(error);
 
434
        result = FALSE;
 
435
        break;
 
436
      }
 
437
      else
 
438
      {
 
439
        fprintf(stderr, "Played all records and the error still occurs\n");
 
440
        result = FALSE;
 
441
        break;
 
442
      }
 
443
    }
 
444
  }
 
445
 
 
446
  g_object_unref(local_replay);
 
447
 
 
448
  if(result)
 
449
  {
 
450
    /* Also reduce from back */
 
451
    i = 0;
 
452
 
 
453
    back_doc = xmlCopyDoc(last_fail, 1);
 
454
    root = xmlDocGetRootElement(back_doc);
 
455
    initial = inf_test_reduce_replay_find_node(root, "initial");
 
456
    g_assert(initial);
 
457
 
 
458
    next = initial;
 
459
    do
 
460
    {
 
461
      request = next;
 
462
      next = inf_test_reduce_replay_next_node(request);
 
463
      ++i;
 
464
    } while(next != NULL);
 
465
 
 
466
    for(;;)
 
467
    {
 
468
      g_assert(i > 1);
 
469
 
 
470
      --i;
 
471
      fprintf(stderr, "%.6u... ", i);
 
472
      fflush(stderr);
 
473
 
 
474
      do
 
475
      {
 
476
        next = request;
 
477
        request = request->prev;
 
478
        xmlUnlinkNode(next);
 
479
        xmlFreeNode(next);
 
480
      } while(request && request->type != XML_ELEMENT_NODE);
 
481
 
 
482
      if(inf_test_reduce_replay_validate_test(back_doc))
 
483
      {
 
484
        if(inf_test_reduce_replay_run_test(back_doc))
 
485
        {
 
486
          fprintf(stderr, "OK!\n");
 
487
          result = TRUE;
 
488
          break;
 
489
        }
 
490
        else
 
491
        {
 
492
          fprintf(stderr, "FAIL\n");
 
493
          xmlFreeDoc(last_fail);
 
494
          last_fail = xmlCopyDoc(back_doc, 1);
 
495
        }
 
496
      }
 
497
      else
 
498
      {
 
499
        fprintf(stderr, "INVALID\n");
 
500
        result = FALSE;
 
501
        break;
 
502
      }
 
503
    }
 
504
 
 
505
    xmlFreeDoc(back_doc);
 
506
  }
 
507
 
 
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);
 
512
  return result;
 
513
}
 
514
 
 
515
int main(int argc, char* argv[])
 
516
{
 
517
  GError* error = NULL;
 
518
  xmlDocPtr doc;
 
519
  gboolean ret;
 
520
 
 
521
  if(!inf_init(&error))
 
522
  {
 
523
    fprintf(stderr, "%s\n", error->message);
 
524
    return -1;
 
525
  }
 
526
 
 
527
  if(!g_file_test(REPLAY, G_FILE_TEST_IS_EXECUTABLE))
 
528
  {
 
529
    fprintf(stderr, "Replay tool not available. Run \"make\" first.");
 
530
    return -1;
 
531
  }
 
532
 
 
533
  if(argc < 2)
 
534
  {
 
535
    fprintf(stderr, "Usage: %s <record-file>\n", argv[0]);
 
536
    return -1;
 
537
  }
 
538
 
 
539
  doc = xmlReadFile(argv[1], "UTF-8", XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
 
540
  if(!doc || !xmlDocGetRootElement(doc))
 
541
  {
 
542
    if(doc) xmlFreeDoc(doc);
 
543
    fprintf(stderr, "%s\n", xmlGetLastError()->message);
 
544
    return -1;
 
545
  }
 
546
 
 
547
  ret = inf_test_reduce_replay_reduce(doc, argv[1]);
 
548
 
 
549
  xmlFreeDoc(doc);
 
550
  return ret ? 0 : -1;
 
551
}
 
552
 
 
553
/* vim:set et sw=2 ts=2: */