summaryrefslogtreecommitdiffstats
path: root/ldap/servers/plugins/replication/tests
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/plugins/replication/tests
downloadds-ldapserver7x.tar.gz
ds-ldapserver7x.tar.xz
ds-ldapserver7x.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/plugins/replication/tests')
-rw-r--r--ldap/servers/plugins/replication/tests/dnp_sim.c1033
-rw-r--r--ldap/servers/plugins/replication/tests/dnp_sim2.c972
-rw-r--r--ldap/servers/plugins/replication/tests/dnp_sim3.c1489
-rwxr-xr-xldap/servers/plugins/replication/tests/makesim58
4 files changed, 3552 insertions, 0 deletions
diff --git a/ldap/servers/plugins/replication/tests/dnp_sim.c b/ldap/servers/plugins/replication/tests/dnp_sim.c
new file mode 100644
index 00000000..ff524988
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/dnp_sim.c
@@ -0,0 +1,1033 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dnp_simulation.c - this file varifies the correctness of dnp algorithm
+ by generating random sequences of operations, applying
+ the algorithm and outputing the result
+
+ usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]
+ -h - print usage information.
+ -n <number of simulations> - how many simulations to perform; default - 1.
+ -v - verbose mode (prints full entry state after each operation execution)
+ -f <output file> - file where results are stored; by default results are
+ printed to the screen.
+ -o <op file> - file that contains operation sequence to execute; by default,
+ random sequence is generated.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+
+#define MAX_OPS 12 /* maximum number of operations in a simulation */
+#define MAX_VALS 10 /* maximum number of values is entry or dn */
+#define NOT_PRESENT -1
+
+/* data types */
+typedef struct value_state
+{
+ int value_index; /* value */
+ int presense_csn; /* last time at which we know the value was present */
+ int distinguished_csn; /* last time at which we know the value was distinguished */
+ int delete_csn; /* last attempt to delete this value */
+ int non_distinguished_csns [MAX_OPS];/* list of times at which value became non-distinguished */
+ int present; /* flag that tells whether the value iscurrently present */
+} Value_State;
+
+typedef struct entry_state
+{
+ int dn_index;
+ int dn_csn;
+ Value_State values[MAX_VALS]; /* values of the attribute */
+ int attr_delete_csn; /* last attempt to delete the entire attribute */
+} Entry_State;
+
+typedef enum
+{
+ OP_ADD_VALUE,
+ OP_RENAME_ENTRY,
+ OP_DELETE_VALUE,
+ OP_DELETE_ATTR,
+ OP_END
+} Operation_Type;
+
+typedef struct operation
+{
+ Operation_Type type; /* operation type */
+ int csn; /* operation type */
+ int value_index; /* value to add, remove or rename from */
+ int old_dn_index; /* new dn - rename only */
+}Operation;
+
+typedef struct simulator_params
+{
+ int runs; /* number of runs */
+ FILE *fout; /* output file */
+ int value_count; /* number of values */
+ int verbose; /* verbose mode */
+ Operation *ops; /* operation sequence to apply */
+ int op_count;
+}Simulator_Params;
+
+
+/* gloabl data */
+Simulator_Params sim;
+char *g_values[] =
+{
+ "v",
+ "u",
+ "w",
+ NULL
+};
+
+/* forward declarations */
+
+/* initialization */
+void process_cmd (int argc, char **argv);
+void set_default_sim_params ();
+int count_values ();
+void parse_operations_file (char *name);
+void parse_operation (char *line, int pos);
+int value2index (char *value);
+void print_usage ();
+
+/* simulation run */
+void run_simulation ();
+void generate_operations (Operation **ops, int *op_count);
+void generate_operation (Operation *op, int csn, int *last_dn_index);
+int* generate_operation_order (int op_count, int seq_num);
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry);
+void init_entry_state (Entry_State *entry);
+void init_value_state (Value_State *val, int seq_num);
+void apply_operation (Entry_State *entry, Operation *op);
+void free_operations (Operation **ops);
+int ** new_perm_table (int op_count);
+void free_perm_table (int ***perm_table, int op_count);
+int get_perm_count (int op_count);
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table);
+void apply_add_operation (Entry_State *entry, Operation *op);
+void apply_value_delete_operation (Entry_State *entry, Operation *op);
+void apply_attr_delete_operation (Entry_State *entry, Operation *op);
+void apply_rename_operation (Entry_State *entry, Operation *op);
+void make_value_distinguished (int op_csn, Entry_State *entry, int value_index);
+void make_value_non_distinguished (int op_csn, Entry_State *entry, int value_index);
+void purge_value_state (Value_State *value);
+void purge_non_distinguished_csns (Value_State *value);
+void resolve_value_state (Entry_State *entry, int value_index);
+int value_distinguished_at_delete (Value_State *value, int attr_delete_csn);
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run);
+
+/* data tracing */
+void dump_operations (Operation *ops, int op_count, int *order);
+void dump_operation (Operation *op);
+void dump_perm_table (int **perm_table, int op_count);
+void dump_entry_state (Entry_State *entry);
+void dump_value_state (Value_State *value);
+void dump_list (int *list);
+
+/* misc functions */
+int max_val (int a, int b);
+int is_list_empty (int *list);
+int min_list_val (int *list);
+int list_equal (int *list1, int *list2);
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ process_cmd (argc, argv);
+
+ for (i = 0; i < sim.runs; i++)
+ {
+ fprintf (sim.fout, "*******running simulation #%d ...\n\n", i+1);
+ run_simulation ();
+ fprintf (sim.fout, "\n*******done with simulation #%d ...\n\n", i+1);
+ }
+
+ if (sim.fout != stdout)
+ fclose (sim.fout);
+
+ return 0;
+}
+
+void process_cmd (int argc, char **argv)
+{
+ int i;
+
+ set_default_sim_params ();
+
+ if (argc == 1)
+ {
+ return;
+ }
+
+ if (strcmp (argv[1], "-h") == 0) /* print help */
+ {
+ print_usage ();
+ exit (0);
+ }
+
+ i = 1;
+ while (i < argc)
+ {
+ if (strcmp (argv[i], "-v") == 0) /* verbose mode */
+ {
+ sim.verbose = 1;
+ i ++;
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (i < argc - 1)
+ {
+ int runs = atoi (argv[i + 1]);
+ if (runs > 0)
+ sim.runs = runs;
+ i+=2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i++;
+ }
+ }
+ else if (strcmp (argv[i], "-f") == 0) /* output file */
+ {
+ if (i < argc - 1)
+ {
+ FILE *f = fopen (argv[i + 1], "w");
+ if (f == 0)
+ {
+ printf ("failed to open output file; error - %s, using stdout\n",
+ strerror(errno));
+ }
+ else
+ {
+ /* ONREPL print warning */
+ sim.fout = f;
+ }
+
+ i += 2;
+ }
+ else
+ i++;
+ }
+ else if (strcmp (argv[i], "-o") == 0) /* file with operation sequence */
+ {
+ if (i < argc - 1)
+ {
+ parse_operations_file (argv[i+1]);
+ i += 2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i ++;
+ }
+ }
+ else /* unknown option */
+ {
+ printf ("unknown option - %s; ignored\n", argv[i]);
+ i ++;
+ }
+
+ }
+}
+
+void set_default_sim_params ()
+{
+ memset (&sim, 0, sizeof (sim));
+ sim.runs = 1;
+ sim.fout = stdout;
+ sim.value_count = count_values ();
+}
+
+/* file format: <op count>
+ add <value>
+ delete <value>
+ delete attribute
+ rename <value> to <value>
+ */
+void parse_operations_file (char *name)
+{
+ FILE *file = fopen (name, "r");
+ char line [256];
+ int i;
+
+ if (file == NULL)
+ {
+ printf ("failed to open operations file %s: error = %d\n", name, errno);
+ print_usage ();
+ exit (1);
+ }
+
+ i = 0;
+ while (fgets (line, sizeof (line), file))
+ {
+ if (i == 0)
+ {
+ /* read operation count */
+ sim.op_count = atoi (line);
+ if (sim.op_count < 1 || sim.op_count > MAX_OPS/2)
+ {
+ printf ("invalid operation count - %d; value must be between 1 and %d\n",
+ sim.op_count, MAX_OPS/2);
+ print_usage ();
+ exit (1);
+ }
+ else
+ {
+ sim.ops = (Operation*)malloc (sim.op_count * sizeof (Operation));
+ }
+ }
+ else
+ {
+ if (strlen (line) == 0) /* skip empty lines */
+ continue;
+ parse_operation (line, i);
+ }
+
+ i ++;
+ }
+
+}
+
+void parse_operation (char *line, int i)
+{
+ sim.ops [i - 1].csn = i;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ if (strncmp (line, "add", 3) == 0)
+ {
+ sim.ops [i - 1].type = OP_ADD_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[4]);
+ }
+ else if (strncmp (line, "delete attribute", 16) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_ATTR;
+ }
+ else if (strncmp (line, "delete", 6) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[7]);
+ }
+ else if (strncmp (line, "rename", 6) == 0)
+ {
+ char *tok;
+ sim.ops [i - 1].type = OP_RENAME_ENTRY;
+ /* strtok() is not MT safe, but it is okay to call here because this is a program test */
+ tok = strtok (&line[7], " ");
+ sim.ops [i - 1].old_dn_index = value2index (tok);
+ /* skip to */
+ tok = strtok (NULL, " ");
+ tok = strtok (NULL, " ");
+ sim.ops [i - 1].value_index = value2index (tok);
+ }
+ else
+ {
+ /* invalid line */
+ printf ("invalid operation: %s\n", line);
+ exit (1);
+ }
+}
+
+int value2index (char *value)
+{
+ int i;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (strcmp (g_values[i], value) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void print_usage ()
+{
+ printf ("usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]\n"
+ "\t-h - print usage information\n"
+ "\t-n <number of simulations>; default - 1\n"
+ "\t-v - verbose mode\n"
+ "\t-f <output file> - by default, results are printed to the screen\n"
+ "\t-o <op file> - file that contains operation sequence to execute;\n"
+ "\tby default, random sequence is generated.\n");
+}
+
+int count_values ()
+{
+ int i;
+
+ for (i = 0; g_values[i]; i++);
+
+ return i;
+}
+
+void run_simulation ()
+{
+ int *order;
+ int i;
+ int perm_count;
+ Entry_State entry_first, entry_current;
+ int error = 0;
+
+ init_entry_state (&entry_first);
+ fprintf (sim.fout, "initial entry state :\n");
+ dump_entry_state (&entry_first);
+
+ if (sim.ops == NULL)
+ {
+ generate_operations (&sim.ops, &sim.op_count);
+ }
+ fprintf (sim.fout, "initial operation set:\n");
+ dump_operations (sim.ops, sim.op_count, NULL/* order */);
+
+ perm_count = get_perm_count (sim.op_count);
+ for (i = 0; i < perm_count; i++)
+ {
+ fprintf (sim.fout, "--------------------------------\n");
+ fprintf (sim.fout, "simulation run %d\n", i + 1);
+ fprintf (sim.fout, "--------------------------------\n");
+ order = generate_operation_order (sim.op_count, i);
+ if (i == 0)
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_first);
+ else
+ {
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_current);
+ error |= compare_entry_state (&entry_first, &entry_current, i + 1);
+ }
+ }
+
+ switch (error)
+ {
+ case 0: fprintf (sim.fout, "all runs left the entry in the same state\n");
+ break;
+ case 1: fprintf (sim.fout, "while value presence is consistent across all runs, "
+ "the exact state does not match\n");
+ break;
+ case 3: fprintf (sim.fout, "the runs left entries in an inconsistent state\n");
+ break;
+ }
+
+ free_operations (&sim.ops);
+}
+
+void generate_operations (Operation **ops, int *op_count)
+{
+ int i;
+ int last_dn_index = 0;
+
+ /* generate number operations in the sequence */
+ *op_count = slapi_rand () % (MAX_OPS / 2) + 1;
+ *ops = (Operation *)malloc (*op_count * sizeof (Operation));
+
+ for (i = 0; i < *op_count; i ++)
+ {
+ generate_operation (&((*ops)[i]), i + 1, &last_dn_index);
+ }
+}
+
+void generate_operation (Operation *op, int csn, int *last_dn_index)
+{
+ /* generate operation type */
+ op->type = slapi_rand () % OP_END;
+
+ /* generate value to which operation applies */
+ op->value_index = slapi_rand () % sim.value_count;
+
+ op->csn = csn;
+
+ /* generate new distinguished value */
+ if (op->type == OP_RENAME_ENTRY)
+ {
+ op->old_dn_index = *last_dn_index;
+ while (op->value_index == op->old_dn_index)
+ op->value_index = slapi_rand () % sim.value_count;
+ *last_dn_index = op->value_index;
+ }
+}
+
+int* generate_operation_order (int op_count, int seq_num)
+{
+ static int **perm_table = NULL;
+
+ /* first request - generate pemutation table */
+ if (seq_num == 0)
+ {
+ int elements [MAX_OPS];
+ int i;
+
+ if (perm_table)
+ free_perm_table (&perm_table, op_count);
+ perm_table = new_perm_table (op_count);
+
+ for (i = 0; i < op_count; i++)
+ elements [i] = i;
+
+ generate_perm_table (elements, op_count, 0 /* static part */,
+ perm_table);
+ /* dump_perm_table (perm_table, op_count);*/
+ }
+
+ return perm_table [seq_num];
+}
+
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry)
+{
+ int i;
+
+ init_entry_state (entry);
+
+ if (!sim.verbose)
+ {
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "operation_sequence for this run:\n");
+ dump_operations (ops, op_count, order);
+ }
+ }
+
+ for (i = 0; i < op_count; i++)
+ {
+ apply_operation (entry, &(ops [order[i]]));
+ }
+
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "final entry state :\n");
+ dump_entry_state (entry);
+ }
+
+}
+
+void init_entry_state (Entry_State *entry)
+{
+ int i;
+
+ memset (entry, 0, sizeof (*entry));
+ entry->attr_delete_csn = NOT_PRESENT;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ init_value_state (&(entry->values[i]), i);
+ }
+}
+
+void init_value_state (Value_State *val, int seq_num)
+{
+ memset (val, 0, sizeof (*val));
+ val->value_index = seq_num;
+ val->present = 1;
+ val->delete_csn = NOT_PRESENT;
+ if (seq_num > 0) /* only first value is distinguished */
+ val->distinguished_csn = -1;
+}
+
+void apply_operation (Entry_State *entry, Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE: apply_add_operation (entry, op);
+ break;
+
+ case OP_DELETE_VALUE: apply_value_delete_operation (entry, op);
+ break;
+
+ case OP_DELETE_ATTR: apply_attr_delete_operation (entry, op);
+ break;
+
+ case OP_RENAME_ENTRY: apply_rename_operation (entry, op);
+ break;
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "operation: ");
+ dump_operation (op);
+ fprintf (sim.fout, "\n");
+ dump_entry_state (entry);
+ }
+}
+
+void free_operations (Operation **ops)
+{
+ free (*ops);
+ *ops = NULL;
+}
+
+int **new_perm_table (int op_count)
+{
+ int i;
+ int **perm_table;
+ int perm_count = get_perm_count (op_count);
+
+ perm_table = (int**)malloc (perm_count * sizeof (int*));
+ for (i = 0; i < perm_count; i ++)
+ perm_table [i] = (int*) malloc (op_count * sizeof (int));
+
+ return perm_table;
+}
+
+void free_perm_table (int ***perm_table, int op_count)
+{
+ int i;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < perm_count; i ++)
+ free ((*perm_table)[i]);
+
+ free (*perm_table);
+ *perm_table = NULL;
+}
+
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table)
+{
+ int i;
+ int elements_copy [MAX_OPS];
+ int start_pos;
+
+ if (element_count - 1 == static_part)
+ {
+ memcpy (*perm_table, elements, element_count * sizeof (int));
+ return;
+ }
+
+ start_pos = 0;
+ for (i = 0; i < element_count - static_part; i ++)
+ {
+ memcpy (elements_copy, elements, element_count * sizeof (int));
+ elements_copy [static_part] = elements [static_part + i];
+ elements_copy [static_part + i] = elements [static_part];
+ generate_perm_table (elements_copy, element_count, static_part + 1,
+ &perm_table [start_pos]);
+ start_pos += get_perm_count (element_count - static_part - 1);
+ }
+}
+
+int get_perm_count (int op_count)
+{
+ int i;
+ int perm_count = 1;
+
+ for (i = 2; i <= op_count; i ++)
+ perm_count *= i;
+
+ return perm_count;
+}
+
+void apply_add_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].presense_csn < op->csn)
+ {
+ entry->values[op->value_index].presense_csn = op->csn;
+ entry->values[op->value_index].present = 1;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_value_delete_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].delete_csn < op->csn)
+ {
+ entry->values[op->value_index].delete_csn = op->csn;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_attr_delete_operation (Entry_State *entry, Operation *op)
+{
+ int i;
+
+ if (entry->attr_delete_csn < op->csn)
+ {
+ entry->attr_delete_csn = op->csn;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ resolve_value_state (entry, i);
+ }
+ }
+}
+
+void apply_rename_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->dn_csn < op->csn)
+ {
+ entry->dn_index = op->value_index;
+ entry->dn_csn = op->csn;
+ }
+
+ make_value_non_distinguished (op->csn, entry, op->old_dn_index);
+ make_value_distinguished (op->csn, entry, op->value_index);
+}
+
+void make_value_distinguished (int op_csn, Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+
+ if (value->distinguished_csn < op_csn)
+ {
+ value->distinguished_csn = op_csn;
+
+ if (value->presense_csn < op_csn)
+ {
+ value->present = 1;
+ value->presense_csn = op_csn;
+ }
+
+ resolve_value_state (entry, value_index);
+ }
+}
+
+void make_value_non_distinguished (int op_csn, Entry_State *entry, int value_index)
+{
+ int i = 0;
+ int index;
+ Value_State *value = &(entry->values[value_index]);
+
+ if (op_csn < value->distinguished_csn)
+ return;
+
+ /* insert into sorted list */
+ while (value->non_distinguished_csns[i] && value->non_distinguished_csns[i] < op_csn)
+ i++;
+
+ if (value->non_distinguished_csns[i] == 0)
+ value->non_distinguished_csns[i] = op_csn;
+ else
+ {
+ index = i;
+
+ while (value->non_distinguished_csns[i])
+ i++;
+
+ memcpy (&(value->non_distinguished_csns[index + 1]),
+ &(value->non_distinguished_csns[index]), (i - index) * sizeof (int));
+
+ value->non_distinguished_csns[index] = op_csn;
+ }
+
+ resolve_value_state (entry, value_index);
+}
+
+void purge_value_state (Value_State *value)
+{
+ /* value state information can be purged once a value was
+ readed/made distinguished because at that point we know that the value
+ existed/was distinguished */
+
+ purge_non_distinguished_csns (value);
+
+ if (value->delete_csn < max_val (value->distinguished_csn, value->presense_csn))
+ value->delete_csn = NOT_PRESENT;
+}
+
+void purge_non_distinguished_csns (Value_State *value)
+{
+ int i = 0;
+ int index;
+
+ while (value->non_distinguished_csns[i] &&
+ value->non_distinguished_csns[i] < value->distinguished_csn)
+ i ++;
+
+ if (i > 0)
+ {
+ index = i-1;
+ while (value->non_distinguished_csns[i])
+ i ++;
+
+ if (i > index + 1)
+ {
+ memcpy (value->non_distinguished_csns, &value->non_distinguished_csns[index+1],
+ (i - index - 1) * sizeof (int));
+ memset (&(value->non_distinguished_csns[index+1]), 0, sizeof (int) * (i - index - 1));
+ }
+ else
+ {
+ memset (value->non_distinguished_csns, 0, sizeof (int) * i);
+ }
+ }
+}
+
+int is_list_empty (int *list)
+{
+ return (list[0] == 0);
+}
+
+int min_list_val (int *list)
+{
+ return (list [0]);
+}
+
+void resolve_value_state (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+
+ purge_value_state (value);
+
+ /* no deletes that effect the state */
+ if (max_val (value->delete_csn, entry->attr_delete_csn) <
+ max_val (value->distinguished_csn, value->presense_csn))
+ return;
+
+ if (value->present) /* check if it should be removed based on the current state */
+ {
+ if (!value_distinguished_at_delete (value, entry->attr_delete_csn))
+ {
+ /* note that we keep presence csn because we might have to restore
+ the value in the future */
+ value->present = 0;
+ }
+ }
+ else /* not present - check if it should be restored */
+ {
+ if (value_distinguished_at_delete (value, entry->attr_delete_csn))
+ {
+ value->present = 1;
+ }
+ }
+}
+
+/* Note we can't trim distinguished_csn (even during regular trimming)
+ because in some cases we would not be able to figure out whether
+ a value was distinguished or not at the time of deletion.
+
+ Example 1: Example2:
+ csn order operation csn order operation
+ 1 1 make V distinguished 1 1 make V distinguished
+ 3 3 delete V 2 2 make V non distinguished
+ 4 2 make V non-distinguished 3 4 delete V
+ 4 3 make V non distinguished (on another server)
+
+ if the csns up to 2 were triimed, when delete operation is received, the state
+ is exactly the same in both examples but in example one delete should not go
+ through while in example 2 it should
+
+ */
+int value_distinguished_at_delete (Value_State *value, int attr_delete_csn)
+{
+ if (value->distinguished_csn >= 0 &&
+ (is_list_empty (value->non_distinguished_csns) ||
+ min_list_val (value->non_distinguished_csns) >
+ max_val (value->delete_csn, attr_delete_csn)))
+ return 1;
+ else
+ return 0;
+}
+
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int j;
+ int error = 0;
+
+ /* first - quick check for present / not present */
+ for (j = 0; j < sim.value_count; j++)
+ {
+ if (entry1->values[j].present != entry2->values[j].present)
+ {
+ fprintf (sim.fout,
+ "value %s is %s present in the first run but %s present in the %d run\n",
+ g_values[j], entry1->values[j].present ? "" : "not",
+ entry2->values[j].present ? "" : "not", run);
+ error = 1;
+ }
+ }
+
+ if (error)
+ return 3;
+
+ /* compare value state */
+ error = 0;
+ if (entry1->attr_delete_csn != entry2->attr_delete_csn)
+ {
+ fprintf (sim.fout, "attribute delete csn is %d for run 1 "
+ "but is %d for run %d\n", entry1->attr_delete_csn,
+ entry2->attr_delete_csn, run);
+ error = 1;
+ }
+
+ for (j = 0; j < sim.value_count; j++)
+ {
+ if (entry1->values[j].presense_csn != entry2->values[j].presense_csn)
+ {
+ fprintf (sim.fout, "presence csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[j], entry1->values[j].presense_csn,
+ entry2->values[j].presense_csn, run);
+ error = 1;
+ }
+
+ if (entry1->values[j].distinguished_csn != entry2->values[j].distinguished_csn)
+ {
+ fprintf (sim.fout, "distinguished csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[j], entry1->values[j].distinguished_csn,
+ entry2->values[j].distinguished_csn, run);
+ error = 1;
+ }
+
+ if (entry1->values[j].delete_csn != entry2->values[j].delete_csn)
+ {
+ fprintf (sim.fout, "delete csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[j], entry1->values[j].delete_csn,
+ entry2->values[j].delete_csn, run);
+ error = 1;
+ }
+
+ if (!list_equal (entry1->values[j].non_distinguished_csns,
+ entry2->values[j].non_distinguished_csns))
+ {
+ fprintf (sim.fout, "pending list mismatch for valye %s in runs 1 and %d\n",
+ g_values[j], run);
+ dump_list (entry1->values[j].non_distinguished_csns);
+ dump_list (entry2->values[j].non_distinguished_csns);
+ }
+ }
+
+ if (error != 0)
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+void dump_operations (Operation *ops, int op_count, int *order)
+{
+ int index;
+ int i;
+
+ for (i = 0; i < op_count; i ++)
+ {
+ if (order == NULL) /* current order */
+ index = i;
+ else
+ index = order [i];
+
+ dump_operation (&ops[index]);
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_operation (Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE:
+ fprintf (sim.fout, "\t%d add value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_VALUE:
+ fprintf (sim.fout, "\t%d delete value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_ATTR:
+ fprintf (sim.fout, "\t%d delete attribute\n", op->csn);
+ break;
+ case OP_RENAME_ENTRY:
+ fprintf (sim.fout, "\t%d rename entry from %s to %s\n", op->csn,
+ g_values [op->old_dn_index], g_values [op->value_index]);
+ break;
+ }
+}
+
+void dump_perm_table (int **perm_table, int op_count)
+{
+ int i, j;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < op_count; i++)
+ {
+ for (j = 0; j < perm_count; j++)
+ {
+ fprintf (sim.fout, "%d ", perm_table [j][i]);
+ }
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_entry_state (Entry_State *entry)
+{
+ int i;
+ fprintf (sim.fout, "\tentry dn: %s; dn csn - %d\n",
+ g_values [entry->dn_index], entry->dn_csn);
+
+ if (sim.verbose)
+ fprintf (sim.fout, "\n");
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ dump_value_state (&(entry->values[i]));
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_value_state (Value_State *value)
+{
+ fprintf (sim.fout, "\tvalue %s is %s\n", g_values[value->value_index],
+ value->present ? "present" : "not present");
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tpresent csn: %d\n", value->presense_csn);
+ fprintf (sim.fout, "\t\tdistinguished csn: %d\n", value->distinguished_csn);
+ fprintf (sim.fout, "\t\tdelete value csn: %d\n", value->delete_csn);
+ fprintf (sim.fout, "\t\tnon distinguished csns: ");
+
+ dump_list (value->non_distinguished_csns);
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_list (int *list)
+{
+ int i = 0;
+
+ while (list[i])
+ {
+ fprintf (sim.fout, "%d ", list[i]);
+ i ++;
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+/* misc functions */
+int max_val (int a, int b)
+{
+ if (a >= b)
+ return a;
+ else
+ return b;
+}
+
+int list_equal (int *list1, int *list2)
+{
+ int i;
+
+ i = 0;
+ while (list1[i] && list2[i])
+ {
+ if (list1[i] != list2[i])
+ return 0;
+
+ i ++;
+ }
+
+ if (list1[i] != list2[i])
+ return 0;
+ else
+ return 1;
+}
diff --git a/ldap/servers/plugins/replication/tests/dnp_sim2.c b/ldap/servers/plugins/replication/tests/dnp_sim2.c
new file mode 100644
index 00000000..e1838aa6
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/dnp_sim2.c
@@ -0,0 +1,972 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dnp_simulation.c - this file varifies the correctness of dnp algorithm
+ by generating random sequences of operations, applying
+ the algorithm and outputing the result
+
+ usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]
+ -h - print usage information.
+ -n <number of simulations> - how many simulations to perform; default - 1.
+ -v - verbose mode (prints full entry state after each operation execution)
+ -f <output file> - file where results are stored; by default results are
+ printed to the screen.
+ -o <op file> - file that contains operation sequence to execute; by default,
+ random sequence is generated.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#define MAX_OPS 18 /* maximum number of operations in a simulation */
+#define MAX_VALS 10 /* maximum number of values is entry or dn */
+#define NOT_PRESENT -1
+
+/* data types */
+typedef struct value_state
+{
+ int value_index; /* value */
+ int presence_csn; /* last time at which we know the value was present */
+ int distinguished_index; /* index into dncsn list */
+ int delete_csn; /* last attempt to delete this value */
+ int present; /* flag that tells whether the value iscurrently present */
+} Value_State;
+
+typedef struct dn_csn
+{
+ int csn; /* dn csn */
+ int value_index; /* dn value */
+} Dn_Csn;
+
+typedef struct entry_state
+{
+ Dn_Csn dn_csns [MAX_VALS + 1]; /* list of dn csns for this entry */
+ int dn_csn_count; /* csn of the current dn */
+ Value_State values[MAX_VALS]; /* values of the attribute */
+ int attr_delete_csn; /* last attempt to delete the entire attribute */
+} Entry_State;
+
+typedef enum
+{
+ OP_ADD_VALUE,
+ OP_DELETE_VALUE,
+ OP_RENAME_ENTRY,
+ OP_DELETE_ATTR,
+ OP_END
+} Operation_Type;
+
+typedef struct operation
+{
+ Operation_Type type; /* operation type */
+ int csn; /* operation type */
+ int value_index; /* value to add, remove or rename from */
+}Operation;
+
+typedef struct simulator_params
+{
+ int runs; /* number of runs */
+ FILE *fout; /* output file */
+ int value_count; /* number of values */
+ int verbose; /* verbose mode */
+ Operation *ops; /* operation sequence to apply */
+ int op_count;
+}Simulator_Params;
+
+
+/* gloabl data */
+Simulator_Params sim;
+char *g_values[] =
+{
+ "v",
+ "u",
+ "w",
+ NULL
+};
+
+/* forward declarations */
+
+/* initialization */
+void process_cmd (int argc, char **argv);
+void set_default_sim_params ();
+int count_values ();
+void parse_operations_file (char *name);
+void parse_operation (char *line, int pos);
+int value2index (char *value);
+void print_usage ();
+
+/* simulation run */
+void run_simulation ();
+void generate_operations (Operation **ops, int *op_count);
+void generate_operation (Operation *op, int csn);
+int* generate_operation_order (int op_count, int seq_num);
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry);
+void init_entry_state (Entry_State *entry);
+void init_value_state (Value_State *val, int seq_num);
+void apply_operation (Entry_State *entry, Operation *op);
+void free_operations (Operation **ops);
+int ** new_perm_table (int op_count);
+void free_perm_table (int ***perm_table, int op_count);
+int get_perm_count (int op_count);
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table);
+void apply_add_operation (Entry_State *entry, Operation *op);
+void apply_value_delete_operation (Entry_State *entry, Operation *op);
+void apply_attr_delete_operation (Entry_State *entry, Operation *op);
+void apply_rename_operation (Entry_State *entry, Operation *op);
+void purge_value_state (Entry_State *entry, int index);
+void resolve_value_state (Entry_State *entry, int value_index);
+int value_distinguished_at_delete (Entry_State *entry, int value_index);
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run);
+
+/* dnc_csn handling */
+int dn_csn_add (Entry_State *entry, int value_index, int csn);
+int get_value_dn_csn (Entry_State *entry, int value_index);
+
+/* data tracing */
+void dump_operations (Operation *ops, int op_count, int *order);
+void dump_operation (Operation *op);
+void dump_perm_table (int **perm_table, int op_count);
+void dump_entry_state (Entry_State *entry);
+void dump_value_state (Value_State *value);
+void dump_dn_csn_list (Entry_State *entry);
+
+/* misc functions */
+int max_val (int a, int b);
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ process_cmd (argc, argv);
+
+ for (i = 0; i < sim.runs; i++)
+ {
+ fprintf (sim.fout, "*******running simulation #%d ...\n\n", i+1);
+ run_simulation ();
+ fprintf (sim.fout, "\n*******done with simulation #%d ...\n\n", i+1);
+ }
+
+ if (sim.fout != stdout)
+ fclose (sim.fout);
+
+ return 0;
+}
+
+void process_cmd (int argc, char **argv)
+{
+ int i;
+
+ set_default_sim_params ();
+
+ if (argc == 1)
+ {
+ return;
+ }
+
+ if (strcmp (argv[1], "-h") == 0) /* print help */
+ {
+ print_usage ();
+ exit (0);
+ }
+
+ i = 1;
+ while (i < argc)
+ {
+ if (strcmp (argv[i], "-v") == 0) /* verbose mode */
+ {
+ sim.verbose = 1;
+ i ++;
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (i < argc - 1)
+ {
+ int runs = atoi (argv[i + 1]);
+ if (runs > 0)
+ sim.runs = runs;
+ i+=2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i++;
+ }
+ }
+ else if (strcmp (argv[i], "-f") == 0) /* output file */
+ {
+ if (i < argc - 1)
+ {
+ FILE *f = fopen (argv[i + 1], "w");
+ if (f == 0)
+ {
+ printf ("failed to open output file; error - %s, using stdout\n",
+ strerror(errno));
+ }
+ else
+ {
+ /* ONREPL print warning */
+ sim.fout = f;
+ }
+
+ i += 2;
+ }
+ else
+ i++;
+ }
+ else if (strcmp (argv[i], "-o") == 0) /* file with operation sequence */
+ {
+ if (i < argc - 1)
+ {
+ parse_operations_file (argv[i+1]);
+ i += 2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i ++;
+ }
+ }
+ else /* unknown option */
+ {
+ printf ("unknown option - %s; ignored\n", argv[i]);
+ i ++;
+ }
+
+ }
+}
+
+void set_default_sim_params ()
+{
+ memset (&sim, 0, sizeof (sim));
+ sim.runs = 1;
+ sim.fout = stdout;
+ sim.value_count = count_values ();
+}
+
+/* file format: <op count>
+ add <value>
+ delete <value>
+ delete attribute
+ rename to <value>
+
+ all spaces are significant
+ */
+void parse_operations_file (char *name)
+{
+ FILE *file = fopen (name, "r");
+ char line [256];
+ int i;
+
+ if (file == NULL)
+ {
+ printf ("failed to open operations file %s: error = %d\n", name, errno);
+ print_usage ();
+ exit (1);
+ }
+
+ i = 0;
+ while (fgets (line, sizeof (line), file))
+ {
+ if (i == 0)
+ {
+ /* read operation count */
+ sim.op_count = atoi (line);
+ if (sim.op_count < 1 || sim.op_count > MAX_OPS/2)
+ {
+ printf ("invalid operation count - %d; value must be between 1 and %d\n",
+ sim.op_count, MAX_OPS/2);
+ print_usage ();
+ exit (1);
+ }
+ else
+ {
+ sim.ops = (Operation*)malloc (sim.op_count * sizeof (Operation));
+ }
+ }
+ else
+ {
+ if (strlen (line) == 0) /* skip empty lines */
+ continue;
+ parse_operation (line, i);
+ }
+
+ i ++;
+ }
+}
+
+void parse_operation (char *line, int i)
+{
+ sim.ops [i - 1].csn = i;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ if (strncmp (line, "add", 3) == 0)
+ {
+ sim.ops [i - 1].type = OP_ADD_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[4]);
+ }
+ else if (strncmp (line, "delete attribute", 16) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_ATTR;
+ }
+ else if (strncmp (line, "delete", 6) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[7]);
+ }
+ else if (strncmp (line, "rename to", 6) == 0)
+ {
+ sim.ops [i - 1].type = OP_RENAME_ENTRY;
+ sim.ops [i - 1].value_index = value2index (&line[10]);
+ }
+ else
+ {
+ /* invalid line */
+ printf ("invalid operation: %s\n", line);
+ exit (1);
+ }
+}
+
+int value2index (char *value)
+{
+ int i;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (strcmp (g_values[i], value) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void print_usage ()
+{
+ printf ("usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]\n"
+ "\t-h - print usage information\n"
+ "\t-n <number of simulations>; default - 1\n"
+ "\t-v - verbose mode\n"
+ "\t-f <output file> - by default, results are printed to the screen\n"
+ "\t-o <op file> - file that contains operation sequence to execute;\n"
+ "\tby default, random sequence is generated.\n");
+}
+
+int count_values ()
+{
+ int i;
+
+ for (i = 0; g_values[i]; i++);
+
+ return i;
+}
+
+void run_simulation ()
+{
+ int *order;
+ int i;
+ int perm_count;
+ Entry_State entry_first, entry_current;
+ int error = 0;
+
+ init_entry_state (&entry_first);
+ fprintf (sim.fout, "initial entry state :\n");
+ dump_entry_state (&entry_first);
+
+ if (sim.ops == NULL)
+ {
+ generate_operations (&sim.ops, &sim.op_count);
+ }
+ fprintf (sim.fout, "initial operation set:\n");
+ dump_operations (sim.ops, sim.op_count, NULL/* order */);
+
+ //DebugBreak ();
+
+ perm_count = get_perm_count (sim.op_count);
+ for (i = 0; i < perm_count; i++)
+ {
+ fprintf (sim.fout, "--------------------------------\n");
+ fprintf (sim.fout, "simulation run %d\n", i + 1);
+ fprintf (sim.fout, "--------------------------------\n");
+ order = generate_operation_order (sim.op_count, i);
+ if (i == 0)
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_first);
+ else
+ {
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_current);
+ error |= compare_entry_state (&entry_first, &entry_current, i + 1);
+ }
+ }
+
+ switch (error)
+ {
+ case 0: fprintf (sim.fout, "all runs left the entry in the same state\n");
+ break;
+ case 1: fprintf (sim.fout, "while value presence is consistent across all runs, "
+ "the exact state does not match\n");
+ break;
+ case 3: fprintf (sim.fout, "the runs left entries in an inconsistent state\n");
+ break;
+ }
+
+ free_operations (&sim.ops);
+}
+
+void generate_operations (Operation **ops, int *op_count)
+{
+ int i;
+
+ /* generate number operations in the sequence */
+ *op_count = slapi_rand () % (MAX_OPS / 2) + 1;
+ *ops = (Operation *)malloc (*op_count * sizeof (Operation));
+
+ for (i = 0; i < *op_count; i ++)
+ {
+ generate_operation (&((*ops)[i]), i + 1);
+ }
+}
+
+void generate_operation (Operation *op, int csn)
+{
+ /* generate operation type */
+ op->type = slapi_rand () % OP_END;
+
+ /* generate value to which operation applies */
+ op->value_index = slapi_rand () % sim.value_count;
+
+ op->csn = csn;
+}
+
+int* generate_operation_order (int op_count, int seq_num)
+{
+ static int **perm_table = NULL;
+
+ /* first request - generate pemutation table */
+ if (seq_num == 0)
+ {
+ int elements [MAX_OPS];
+ int i;
+
+ if (perm_table)
+ free_perm_table (&perm_table, op_count);
+ perm_table = new_perm_table (op_count);
+
+ for (i = 0; i < op_count; i++)
+ elements [i] = i;
+
+ generate_perm_table (elements, op_count, 0 /* static part */,
+ perm_table);
+ }
+
+ return perm_table [seq_num];
+}
+
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry)
+{
+ int i;
+
+ init_entry_state (entry);
+
+ if (!sim.verbose)
+ {
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "operation_sequence for this run:\n");
+ dump_operations (ops, op_count, order);
+ }
+ }
+
+ for (i = 0; i < op_count; i++)
+ {
+ apply_operation (entry, &(ops [order[i]]));
+ }
+
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "final entry state :\n");
+ dump_entry_state (entry);
+ }
+
+}
+
+void init_entry_state (Entry_State *entry)
+{
+ int i;
+
+ memset (entry, 0, sizeof (*entry));
+ entry->attr_delete_csn = NOT_PRESENT;
+ entry->dn_csn_count = 1;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ init_value_state (&(entry->values[i]), i);
+ }
+}
+
+void init_value_state (Value_State *val, int seq_num)
+{
+ memset (val, 0, sizeof (*val));
+ val->value_index = seq_num;
+ val->present = 1;
+ val->delete_csn = NOT_PRESENT;
+ if (seq_num > 0) /* only first value is distinguished */
+ val->distinguished_index = -1;
+}
+
+void apply_operation (Entry_State *entry, Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE: apply_add_operation (entry, op);
+ break;
+
+ case OP_DELETE_VALUE: apply_value_delete_operation (entry, op);
+ break;
+
+ case OP_DELETE_ATTR: apply_attr_delete_operation (entry, op);
+ break;
+
+ case OP_RENAME_ENTRY: apply_rename_operation (entry, op);
+ break;
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "operation: ");
+ dump_operation (op);
+ fprintf (sim.fout, "\n");
+ dump_entry_state (entry);
+ }
+}
+
+void free_operations (Operation **ops)
+{
+ free (*ops);
+ *ops = NULL;
+}
+
+int **new_perm_table (int op_count)
+{
+ int i;
+ int **perm_table;
+ int perm_count = get_perm_count (op_count);
+
+ perm_table = (int**)malloc (perm_count * sizeof (int*));
+ for (i = 0; i < perm_count; i ++)
+ perm_table [i] = (int*) malloc (op_count * sizeof (int));
+
+ return perm_table;
+}
+
+void free_perm_table (int ***perm_table, int op_count)
+{
+ int i;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < perm_count; i ++)
+ free ((*perm_table)[i]);
+
+ free (*perm_table);
+ *perm_table = NULL;
+}
+
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table)
+{
+ int i;
+ int elements_copy [MAX_OPS];
+ int start_pos;
+
+ if (element_count - 1 == static_part)
+ {
+ memcpy (*perm_table, elements, element_count * sizeof (int));
+ return;
+ }
+
+ start_pos = 0;
+ for (i = 0; i < element_count - static_part; i ++)
+ {
+ memcpy (elements_copy, elements, element_count * sizeof (int));
+ elements_copy [static_part] = elements [static_part + i];
+ elements_copy [static_part + i] = elements [static_part];
+ generate_perm_table (elements_copy, element_count, static_part + 1,
+ &perm_table [start_pos]);
+ start_pos += get_perm_count (element_count - static_part - 1);
+ }
+}
+
+int get_perm_count (int op_count)
+{
+ int i;
+ int perm_count = 1;
+
+ for (i = 2; i <= op_count; i ++)
+ perm_count *= i;
+
+ return perm_count;
+}
+
+void apply_add_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].presence_csn < op->csn)
+ {
+ entry->values[op->value_index].presence_csn = op->csn;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_value_delete_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].delete_csn < op->csn)
+ {
+ entry->values[op->value_index].delete_csn = op->csn;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_attr_delete_operation (Entry_State *entry, Operation *op)
+{
+ int i;
+
+ if (entry->attr_delete_csn < op->csn)
+ {
+ entry->attr_delete_csn = op->csn;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ resolve_value_state (entry, i);
+ }
+ }
+}
+
+void apply_rename_operation (Entry_State *entry, Operation *op)
+{
+ int index;
+
+ if (entry->values[op->value_index].presence_csn == NOT_PRESENT)
+ entry->values[op->value_index].presence_csn = op->csn;
+
+ index = dn_csn_add (entry, op->value_index, op->csn);
+
+ if (index > 0)
+ resolve_value_state (entry, entry->dn_csns[index - 1].value_index);
+
+ resolve_value_state (entry, entry->dn_csns[index].value_index);
+}
+
+void purge_value_state (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+ int value_distinguished_csn = get_value_dn_csn (entry, value_index);
+
+ if (value_distinguished_csn == -1 && value->presence_csn > value->delete_csn)
+ value->delete_csn = NOT_PRESENT;
+ else if (value->delete_csn < max_val (value_distinguished_csn, value->presence_csn))
+ value->delete_csn = NOT_PRESENT;
+}
+
+void resolve_value_state (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+ int value_distinguished_csn = get_value_dn_csn (entry, value_index);
+
+ purge_value_state (entry, value_index);
+
+ /* no deletes that effect the state */
+ if (max_val (value->delete_csn, entry->attr_delete_csn) <
+ max_val (value_distinguished_csn, value->presence_csn))
+ {
+ value->present = 1;
+ return;
+ }
+
+ if (value->present) /* check if it should be removed based on the current state */
+ {
+ if (!value_distinguished_at_delete (entry, value_index))
+ {
+ value->present = 0;
+ }
+ }
+ else /* not present - check if it should be restored */
+ {
+ if (value_distinguished_at_delete (entry, value_index))
+ {
+ value->present = 1;
+ }
+ }
+}
+
+int value_distinguished_at_delete (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+ int value_distinguished_csn = get_value_dn_csn (entry, value_index);
+ int delete_csn;
+ int i;
+
+ /* value has never been distinguished */
+ if (value_distinguished_csn == -1)
+ return 0;
+
+ delete_csn = max_val (entry->attr_delete_csn, value->delete_csn);
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > delete_csn)
+ break;
+ }
+
+ /* i is never equal to 0 because the csn of the first element is always
+ smaller than csn of any operation we can receive */
+ return (entry->dn_csns[i-1].value_index == value_index);
+}
+
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int i;
+ int error = 0;
+
+ /* first - quick check for present / not present */
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (entry1->values[i].present != entry2->values[i].present)
+ {
+ fprintf (sim.fout,
+ "value %s is %s present in the first run but %s present in the %d run\n",
+ g_values[i], entry1->values[i].present ? "" : "not",
+ entry2->values[i].present ? "" : "not", run);
+ error = 1;
+ }
+ }
+
+ if (error)
+ return 3;
+
+ /* compare dnc_csn list */
+ if (entry1->dn_csn_count != entry2->dn_csn_count)
+ {
+ fprintf (sim.fout, "dn_csn count is %d for run 1 and %d for run %d\n",
+ entry1->dn_csn_count, entry2->dn_csn_count, run);
+ error = 1;
+ }
+
+ for (i = 0; i < entry1->dn_csn_count; i++)
+ {
+ if (entry1->dn_csns [i].csn != entry2->dn_csns [i].csn ||
+ entry1->dn_csns [i].value_index != entry2->dn_csns [i].value_index)
+ {
+ fprintf (sim.fout,"elements %d of dn csn list are different:\n"
+ "\tfirst run: csn - %d, value - %s\n"
+ "\t%d run: csn - %d, value - %s\n", i,
+ entry1->dn_csns [i].csn,
+ g_values[entry1->dn_csns [i].value_index],
+ run, entry2->dn_csns [i].csn,
+ g_values[entry2->dn_csns [i].value_index]);
+
+ error = 1;
+ }
+ }
+
+ /* compare value state */
+ if (entry1->attr_delete_csn != entry2->attr_delete_csn)
+ {
+ fprintf (sim.fout, "attribute delete csn is %d for run 1 "
+ "but is %d for run %d\n", entry1->attr_delete_csn,
+ entry2->attr_delete_csn, run);
+ error = 1;
+ }
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (entry1->values[i].presence_csn != entry2->values[i].presence_csn)
+ {
+ fprintf (sim.fout, "presence csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[i], entry1->values[i].presence_csn,
+ entry2->values[i].presence_csn, run);
+ error = 1;
+ }
+
+ if (entry1->values[i].distinguished_index != entry2->values[i].distinguished_index)
+ {
+ fprintf (sim.fout, "distinguished index for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[i],
+ entry1->values[i].distinguished_index,
+ entry2->values[i].distinguished_index, run);
+ error = 1;
+ }
+
+ if (entry1->values[i].delete_csn != entry2->values[i].delete_csn)
+ {
+ fprintf (sim.fout, "delete csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[i], entry1->values[i].delete_csn,
+ entry2->values[i].delete_csn, run);
+ error = 1;
+ }
+ }
+
+ if (error != 0)
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+int dn_csn_add (Entry_State *entry, int value_index, int csn)
+{
+ int i, j;
+ int distinguished_index;
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > csn)
+ break;
+ }
+
+ if (i < entry->dn_csn_count)
+ {
+ distinguished_index = i;
+ for (j = i; j < entry->dn_csn_count; j ++)
+ {
+ if (entry->dn_csns[j].value_index == value_index)
+ distinguished_index = j + 1;
+
+ if (entry->values [entry->dn_csns[j].value_index].distinguished_index == j)
+ entry->values [entry->dn_csns[j].value_index].distinguished_index ++;
+ }
+
+ memcpy (&(entry->dn_csns[i+1]), &(entry->dn_csns[i]),
+ (entry->dn_csn_count - i) * sizeof (Dn_Csn));
+ }
+ else
+ {
+ distinguished_index = entry->dn_csn_count;
+ }
+
+ entry->values[value_index].distinguished_index = distinguished_index;
+ entry->dn_csns[i].csn = csn;
+ entry->dn_csns[i].value_index = value_index;
+ entry->dn_csn_count ++;
+
+ return i;
+}
+
+int get_value_dn_csn (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values [value_index]);
+
+ if (value->distinguished_index == -1)
+ return -1;
+ else
+ return entry->dn_csns [value->distinguished_index].csn;
+}
+
+void dump_operations (Operation *ops, int op_count, int *order)
+{
+ int index;
+ int i;
+
+ for (i = 0; i < op_count; i ++)
+ {
+ if (order == NULL) /* current order */
+ index = i;
+ else
+ index = order [i];
+
+ dump_operation (&ops[index]);
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_operation (Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE:
+ fprintf (sim.fout, "\t%d add value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_VALUE:
+ fprintf (sim.fout, "\t%d delete value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_ATTR:
+ fprintf (sim.fout, "\t%d delete attribute\n", op->csn);
+ break;
+ case OP_RENAME_ENTRY:
+ fprintf (sim.fout, "\t%d rename entry to %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ }
+}
+
+void dump_perm_table (int **perm_table, int op_count)
+{
+ int i, j;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < op_count; i++)
+ {
+ for (j = 0; j < perm_count; j++)
+ {
+ fprintf (sim.fout, "%d ", perm_table [j][i]);
+ }
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_entry_state (Entry_State *entry)
+{
+ int i;
+
+ dump_dn_csn_list (entry);
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ dump_value_state (&(entry->values[i]));
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_value_state (Value_State *value)
+{
+ fprintf (sim.fout, "\tvalue %s is %s\n", g_values[value->value_index],
+ value->present ? "present" : "not present");
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tpresent csn: %d\n", value->presence_csn);
+ fprintf (sim.fout, "\t\tdistinguished index: %d\n", value->distinguished_index);
+ fprintf (sim.fout, "\t\tdelete value csn: %d\n", value->delete_csn);
+ }
+}
+
+void dump_dn_csn_list (Entry_State *entry)
+{
+ int i;
+
+ fprintf (sim.fout, "\tdn csn list: \n");
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ fprintf (sim.fout, "\t\tvalue: %s, csn: %d\n",
+ g_values[entry->dn_csns[i].value_index], entry->dn_csns[i].csn);
+ }
+}
+
+/* misc functions */
+int max_val (int a, int b)
+{
+ if (a >= b)
+ return a;
+ else
+ return b;
+}
diff --git a/ldap/servers/plugins/replication/tests/dnp_sim3.c b/ldap/servers/plugins/replication/tests/dnp_sim3.c
new file mode 100644
index 00000000..d018597a
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/dnp_sim3.c
@@ -0,0 +1,1489 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dnp_simulation.c - this file varifies the correctness of dnp algorithm
+ by generating random sequences of operations, applying
+ the algorithm and outputing the result
+
+ usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]
+ -h - print usage information.
+ -n <number of simulations> - how many simulations to perform; default - 1.
+ -v - verbose mode (prints full entry state after each operation execution)
+ -f <output file> - file where results are stored; by default results are
+ printed to the screen.
+ -o <op file> - file that contains operation sequence to execute; by default,
+ random sequence is generated.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#define MAX_OPS 18 /* maximum number of operations in a simulation */
+#define MAX_VALS 10 /* maximum number of values is entry or dn */
+#define MAX_ATTR_NAME 16 /* max length of the attribute name */
+#define NOT_PRESENT -1
+#define SV_ATTR_NAME "sv_attr" /* name of the singlevalued attribute */
+#define MV_ATTR_NAME "mv_attr" /* name of the multivalued attribute */
+
+/* data types */
+
+/* value */
+typedef struct value_state
+{
+ int value_index; /* value */
+ int presence_csn; /* last time at which we know the value was present */
+ int delete_csn; /* last attempt to delete this value */
+ int present; /* flag that tells whether the value is present */
+} Value_State;
+
+/* shared attribute state */
+typedef struct attr_state
+{
+ int delete_csn; /* last deletion csn */
+ int present; /* flag that tells whether the attribute is present */
+}Attr_State;
+
+/* singlevalued attribute */
+typedef struct sv_attr_state
+{
+ Attr_State attr_state; /* shared attribute state */
+ Value_State current_value; /* current attribute value */
+ Value_State *pending_value; /* latest pending value */
+} SV_Attr_State;
+
+/* maltivalued attribute */
+typedef struct mv_attr_state
+{
+ Attr_State attr_state; /* shared attribute state */
+ Value_State values [MAX_VALS]; /* latest pending value */
+ int value_count; /* number of values in the array */
+} MV_Attr_State;
+
+/* node of dn_csn_list */
+typedef struct dn_csn
+{
+ int csn; /* dn csn */
+ int sv_attr; /* is this single valued or multivalued attr */
+ int value_index; /* dn value */
+} Dn_Csn;
+
+typedef struct entry_state
+{
+ Dn_Csn dn_csns [MAX_VALS + 1]; /* list of dn csns for this entry */
+ int dn_csn_count; /* csn of the current dn */
+ SV_Attr_State sv_attr; /* singlevalued attribute */
+ MV_Attr_State mv_attr; /* singlevalued attribute */
+} Entry_State;
+
+typedef enum
+{
+ OP_ADD_VALUE,
+ OP_DELETE_VALUE,
+ OP_RENAME_ENTRY,
+ OP_DELETE_ATTR,
+ OP_END
+} Operation_Type;
+
+typedef struct operation
+{
+ Operation_Type type; /* operation type */
+ int csn; /* operation csn */
+ int sv_attr; /* is this applied to singlevalued attribute */
+ int value_index; /* value to add, remove or rename from */
+ int delete_old_rdn; /* rename only */
+ int old_rdn_sv_attr; /* is oldrdn a singlevalued attribute */
+ int old_rdn_value_index; /* index of old_rdn */
+}Operation;
+
+typedef struct simulator_params
+{
+ int runs; /* number of runs */
+ FILE *fout; /* output file */
+ int value_count; /* number of values */
+ int verbose; /* verbose mode */
+ Operation *ops; /* operation sequence to apply */
+ int op_count;
+}Simulator_Params;
+
+
+/* gloabl data */
+Simulator_Params sim;
+char *g_values[] =
+{
+ "v",
+ "u",
+ "w",
+ NULL
+};
+
+/* forward declarations */
+
+/* initialization */
+void process_cmd (int argc, char **argv);
+void set_default_sim_params ();
+int count_values ();
+void parse_operations_file (char *name);
+void parse_operation (char *line, int pos);
+int value2index (char *value);
+void print_usage ();
+
+/* simulation run */
+void run_simulation ();
+void generate_operations (Operation **ops, int *op_count);
+void generate_operation (Operation *op, int csn);
+int* generate_operation_order (int op_count, int seq_num);
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry);
+void init_entry_state (Entry_State *entry);
+void init_sv_attr_state (SV_Attr_State *sv_attr);
+void init_mv_attr_state (MV_Attr_State *mv_attr);
+void init_value_state (Value_State *val, int seq_num);
+void free_operations (Operation **ops);
+int ** new_perm_table (int op_count);
+void free_perm_table (int ***perm_table, int op_count);
+int get_perm_count (int op_count);
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table);
+void apply_operation (Entry_State *entry, Operation *op);
+void apply_add_operation (Entry_State *entry, Operation *op);
+void apply_value_delete_operation (Entry_State *entry, Operation *op);
+void apply_attr_delete_operation (Entry_State *entry, Operation *op);
+void apply_rename_operation (Entry_State *entry, Operation *op);
+void resolve_mv_attr_state (Entry_State *entry, Value_State *value);
+void resolve_sv_attr_state (Entry_State *entry, Value_State *value);
+void purge_sv_attr_state (Entry_State *entry);
+void purge_mv_attr_state (Entry_State *entry, Value_State *value);
+int value_distinguished_at_csn (Entry_State *entry, int sv_attr, Value_State *value, int csn);
+
+/* state comparison */
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run);
+int compare_entry_state_quick (Entry_State *entry1, Entry_State *entry2, int run);
+int compare_sv_attr_state_quick (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run);
+int compare_mv_attr_state_quick (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run);
+int compare_sv_attr_state (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run);
+int compare_mv_attr_state (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run);
+int compare_value_state (Value_State *value1, Value_State *value2, int run);
+
+/* dnc_csn handling */
+int dn_csn_add (Entry_State *entry, int sv_attr, int value_index, int csn);
+
+/* data tracing */
+void dump_operations (Operation *ops, int op_count, int *order);
+void dump_operation (Operation *op);
+void dump_perm_table (int **perm_table, int op_count);
+void dump_entry_state (Entry_State *entry);
+void dump_sv_attr_state (SV_Attr_State *sv_attr);
+void dump_mv_attr_state (MV_Attr_State *mv_attr);
+void dump_value_state (Value_State *value, int sv_attr);
+void dump_dn_csn_list (Entry_State *entry);
+
+/* misc functions */
+int max_val (int a, int b);
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ process_cmd (argc, argv);
+
+ for (i = 0; i < sim.runs; i++)
+ {
+ fprintf (sim.fout, "*******running simulation #%d ...\n\n", i+1);
+ run_simulation ();
+ fprintf (sim.fout, "\n*******done with simulation #%d ...\n\n", i+1);
+ }
+
+ if (sim.fout != stdout)
+ fclose (sim.fout);
+
+ return 0;
+}
+
+void process_cmd (int argc, char **argv)
+{
+ int i;
+
+ set_default_sim_params ();
+
+ if (argc == 1)
+ {
+ return;
+ }
+
+ if (strcmp (argv[1], "-h") == 0) /* print help */
+ {
+ print_usage ();
+ exit (0);
+ }
+
+ i = 1;
+ while (i < argc)
+ {
+ if (strcmp (argv[i], "-v") == 0) /* verbose mode */
+ {
+ sim.verbose = 1;
+ i ++;
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (i < argc - 1)
+ {
+ int runs = atoi (argv[i + 1]);
+ if (runs > 0)
+ sim.runs = runs;
+ i+=2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i++;
+ }
+ }
+ else if (strcmp (argv[i], "-f") == 0) /* output file */
+ {
+ if (i < argc - 1)
+ {
+ FILE *f = fopen (argv[i + 1], "w");
+ if (f == 0)
+ {
+ printf ("failed to open output file; error - %s, using stdout\n",
+ strerror(errno));
+ }
+ else
+ {
+ /* ONREPL print warning */
+ sim.fout = f;
+ }
+
+ i += 2;
+ }
+ else
+ i++;
+ }
+ else if (strcmp (argv[i], "-o") == 0) /* file with operation sequence */
+ {
+ if (i < argc - 1)
+ {
+ parse_operations_file (argv[i+1]);
+ i += 2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i ++;
+ }
+ }
+ else /* unknown option */
+ {
+ printf ("unknown option - %s; ignored\n", argv[i]);
+ i ++;
+ }
+
+ }
+}
+
+void set_default_sim_params ()
+{
+ memset (&sim, 0, sizeof (sim));
+ sim.runs = 1;
+ sim.fout = stdout;
+ sim.value_count = count_values ();
+}
+
+/* file format: <operation count>
+ add <attribute> <value>
+ delete <attribute>[ <value>]
+ rename to <attribute> <value>[ delete <attribute> <value>]
+
+ all spaces are significant
+ */
+void parse_operations_file (char *name)
+{
+ FILE *file = fopen (name, "r");
+ char line [256];
+ int i;
+
+ if (file == NULL)
+ {
+ printf ("failed to open operations file %s: error = %d\n", name, errno);
+ print_usage ();
+ exit (1);
+ }
+
+ i = 0;
+ while (fgets (line, sizeof (line), file))
+ {
+ if (i == 0)
+ {
+ /* read operation count */
+ sim.op_count = atoi (line);
+ if (sim.op_count < 1 || sim.op_count > MAX_OPS/2)
+ {
+ printf ("invalid operation count - %d; value must be between 1 and %d\n",
+ sim.op_count, MAX_OPS/2);
+ print_usage ();
+ exit (1);
+ }
+ else
+ {
+ sim.ops = (Operation*)malloc (sim.op_count * sizeof (Operation));
+ }
+ }
+ else
+ {
+ if (strlen (line) == 0) /* skip empty lines */
+ continue;
+ parse_operation (line, i);
+ }
+
+ i ++;
+ }
+}
+
+#define ADD_KEYWORD "add "
+#define DELETE_KEYWORD "delete "
+#define RENAME_KEYWORD "rename to "
+#define DELET_OLD_RDN_KEYWORD " delete "
+
+void parse_operation (char *line, int i)
+{
+ int rc = 0;
+ char *pos;
+ char buff [64];
+
+ sim.ops [i - 1].csn = i;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ /* add <attribute> <value> */
+ if (strncmp (line, ADD_KEYWORD, strlen (ADD_KEYWORD)) == 0)
+ {
+ sim.ops [i - 1].type = OP_ADD_VALUE;
+ pos = strchr (&line[strlen (ADD_KEYWORD)], ' ');
+ if (pos == NULL)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, &line[strlen (ADD_KEYWORD)], pos - &line[strlen (ADD_KEYWORD)]);
+ sim.ops [i - 1].sv_attr = strcmp (buff, MV_ATTR_NAME);
+ sim.ops [i - 1].value_index = value2index (pos + 1);
+ }
+ /* delete <attribute>[ <value>] */
+ else if (strncmp (line, DELETE_KEYWORD, strlen (DELETE_KEYWORD)) == 0)
+ {
+ pos = strchr (&line[strlen (DELETE_KEYWORD)], ' ');
+ if (pos == NULL) /* delete attribute version */
+ {
+ sim.ops [i - 1].type = OP_DELETE_ATTR;
+ sim.ops [i - 1].sv_attr = strcmp (&line[strlen (DELETE_KEYWORD)],
+ MV_ATTR_NAME);
+ }
+ else /* delete value version */
+ {
+ memset (buff, 0, sizeof (buff));
+ sim.ops [i - 1].type = OP_DELETE_VALUE;
+ strncpy (buff, &line[strlen (DELETE_KEYWORD)],
+ pos - &line[strlen (DELETE_KEYWORD)]);
+ sim.ops [i - 1].sv_attr = strcmp (buff, MV_ATTR_NAME);
+ sim.ops [i - 1].value_index = value2index (pos + 1);
+ }
+ }
+ /* rename to <attribute> <valued>[ delete <attribute> <value>] */
+ else if (strncmp (line, RENAME_KEYWORD, 10) == 0)
+ {
+ char *pos2;
+
+ sim.ops [i - 1].type = OP_RENAME_ENTRY;
+
+ pos = strchr (&line[strlen (RENAME_KEYWORD)], ' ');
+ if (pos == NULL)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, &line[strlen (RENAME_KEYWORD)], pos - &line[strlen (RENAME_KEYWORD)]);
+ sim.ops [i - 1].sv_attr = strcmp (buff, MV_ATTR_NAME);
+
+ pos2 = strstr (pos + 1, DELET_OLD_RDN_KEYWORD);
+ if (pos2 == NULL) /* no delete old rdn part */
+ {
+ sim.ops [i - 1].value_index = value2index (pos + 1);
+ sim.ops [i - 1].delete_old_rdn = 0;
+ }
+ else
+ {
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, pos + 1, pos2 - pos - 1);
+ sim.ops [i - 1].value_index = value2index (buff);
+ pos2 += strlen (DELET_OLD_RDN_KEYWORD);
+ pos = strchr (pos2, ' ');
+ if (pos == NULL)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, pos2, pos - pos2);
+ sim.ops [i - 1].delete_old_rdn = 1;
+ sim.ops [i - 1].old_rdn_sv_attr = strcmp (buff, MV_ATTR_NAME);
+ sim.ops [i - 1].old_rdn_value_index = value2index (pos + 1);
+ }
+ }
+ else
+ {
+ /* error */
+ rc = -1;
+ }
+
+done:
+ if (rc)
+ {
+ /* invalid line */
+ printf ("invalid operation: %s\n", line);
+ exit (1);
+ }
+}
+int value2index (char *value)
+{
+ int i;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (strcmp (g_values[i], value) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void print_usage ()
+{
+ printf ("usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]\n"
+ "\t-h - print usage information\n"
+ "\t-n <number of simulations>; default - 1\n"
+ "\t-v - verbose mode\n"
+ "\t-f <output file> - by default, results are printed to the screen\n"
+ "\t-o <op file> - file that contains operation sequence to execute;\n"
+ "\tby default, random sequence is generated.\n");
+}
+
+int count_values ()
+{
+ int i;
+
+ for (i = 0; g_values[i]; i++);
+
+ return i;
+}
+
+void run_simulation ()
+{
+ int *order;
+ int i;
+ int perm_count;
+ Entry_State entry_first, entry_current;
+ int error = 0;
+
+ init_entry_state (&entry_first);
+ fprintf (sim.fout, "initial entry state :\n");
+ dump_entry_state (&entry_first);
+
+ if (sim.ops == NULL)
+ {
+ generate_operations (&sim.ops, &sim.op_count);
+ }
+ fprintf (sim.fout, "initial operation set:\n");
+ dump_operations (sim.ops, sim.op_count, NULL/* order */);
+
+ perm_count = get_perm_count (sim.op_count);
+ for (i = 0; i < perm_count; i++)
+ {
+ fprintf (sim.fout, "--------------------------------\n");
+ fprintf (sim.fout, "simulation run %d\n", i + 1);
+ fprintf (sim.fout, "--------------------------------\n");
+ order = generate_operation_order (sim.op_count, i);
+ if (i == 0)
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_first);
+ else
+ {
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_current);
+ error |= compare_entry_state (&entry_first, &entry_current, i + 1);
+ }
+ }
+
+ switch (error)
+ {
+ case 0: fprintf (sim.fout, "all runs left the entry in the same state\n");
+ break;
+ case 1: fprintf (sim.fout, "while value presence is consistent across all runs, "
+ "the exact state does not match\n");
+ break;
+ case 3: fprintf (sim.fout, "the runs left entries in an inconsistent state\n");
+ break;
+ }
+
+ free_operations (&sim.ops);
+}
+
+void generate_operations (Operation **ops, int *op_count)
+{
+ int i;
+
+ /* generate number operations in the sequence */
+ *op_count = slapi_rand () % (MAX_OPS / 2) + 1;
+ *ops = (Operation *)malloc (*op_count * sizeof (Operation));
+
+ for (i = 0; i < *op_count; i ++)
+ {
+ generate_operation (&((*ops)[i]), i + 1);
+ }
+}
+
+void generate_operation (Operation *op, int csn)
+{
+ /* generate operation type */
+ op->type = slapi_rand () % OP_END;
+ op->csn = csn;
+
+ /* choose if the operation applies to the single value or
+ the multivalued attribute */
+ op->sv_attr = slapi_rand () % 2;
+
+ /* generate value to which operation applies */
+ op->value_index = slapi_rand () % sim.value_count;
+
+ if (op->type == OP_RENAME_ENTRY)
+ {
+ op->delete_old_rdn = slapi_rand () % 2;
+ if (op->delete_old_rdn)
+ {
+ op->old_rdn_sv_attr = slapi_rand () % 2;
+ op->old_rdn_value_index = slapi_rand () % sim.value_count;
+
+ while (op->old_rdn_sv_attr == op->sv_attr &&
+ op->old_rdn_value_index == op->value_index)
+ {
+ op->old_rdn_sv_attr = slapi_rand () % 2;
+ op->old_rdn_value_index = slapi_rand () % sim.value_count;
+ }
+ }
+ }
+}
+
+int* generate_operation_order (int op_count, int seq_num)
+{
+ static int **perm_table = NULL;
+
+ /* first request - generate pemutation table */
+ if (seq_num == 0)
+ {
+ int elements [MAX_OPS];
+ int i;
+
+ if (perm_table)
+ free_perm_table (&perm_table, op_count);
+ perm_table = new_perm_table (op_count);
+
+ for (i = 0; i < op_count; i++)
+ elements [i] = i;
+
+ generate_perm_table (elements, op_count, 0 /* static part */,
+ perm_table);
+ }
+
+ return perm_table [seq_num];
+}
+
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry)
+{
+ int i;
+
+ init_entry_state (entry);
+
+ if (!sim.verbose)
+ {
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "operation_sequence for this run:\n");
+ dump_operations (ops, op_count, order);
+ }
+ }
+
+ for (i = 0; i < op_count; i++)
+ {
+ apply_operation (entry, &(ops [order[i]]));
+ }
+
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "final entry state :\n");
+ dump_entry_state (entry);
+ }
+}
+
+void init_entry_state (Entry_State *entry)
+{
+ memset (entry, 0, sizeof (*entry));
+ entry->dn_csn_count = 1;
+
+ init_sv_attr_state (&entry->sv_attr);
+ init_mv_attr_state (&entry->mv_attr);
+}
+
+void init_sv_attr_state (SV_Attr_State *sv_attr)
+{
+ memset (sv_attr, 0, sizeof (*sv_attr));
+ sv_attr->attr_state.delete_csn = NOT_PRESENT;
+ sv_attr->attr_state.present = 1;
+ init_value_state (&sv_attr->current_value, 1);
+}
+
+void init_mv_attr_state (MV_Attr_State *mv_attr)
+{
+ int i;
+
+ memset (mv_attr, 0, sizeof (*mv_attr));
+ mv_attr->attr_state.delete_csn = NOT_PRESENT;
+ mv_attr->attr_state.present = 1;
+ mv_attr->value_count = sim.value_count;
+
+ for (i = 0; i < mv_attr->value_count; i++)
+ {
+ init_value_state (&(mv_attr->values[i]), i);
+ }
+}
+
+void init_value_state (Value_State *val, int seq_num)
+{
+ memset (val, 0, sizeof (*val));
+ val->value_index = seq_num;
+ val->present = 1;
+ val->delete_csn = NOT_PRESENT;
+}
+
+void apply_operation (Entry_State *entry, Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE: apply_add_operation (entry, op);
+ break;
+
+ case OP_DELETE_VALUE: apply_value_delete_operation (entry, op);
+ break;
+
+ case OP_DELETE_ATTR: apply_attr_delete_operation (entry, op);
+ break;
+
+ case OP_RENAME_ENTRY: apply_rename_operation (entry, op);
+ break;
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "operation: ");
+ dump_operation (op);
+ fprintf (sim.fout, "\n");
+ dump_entry_state (entry);
+ }
+}
+
+void free_operations (Operation **ops)
+{
+ free (*ops);
+ *ops = NULL;
+}
+
+int **new_perm_table (int op_count)
+{
+ int i;
+ int **perm_table;
+ int perm_count = get_perm_count (op_count);
+
+ perm_table = (int**)malloc (perm_count * sizeof (int*));
+ for (i = 0; i < perm_count; i ++)
+ perm_table [i] = (int*) malloc (op_count * sizeof (int));
+
+ return perm_table;
+}
+
+void free_perm_table (int ***perm_table, int op_count)
+{
+ int i;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < perm_count; i ++)
+ free ((*perm_table)[i]);
+
+ free (*perm_table);
+ *perm_table = NULL;
+}
+
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table)
+{
+ int i;
+ int elements_copy [MAX_OPS];
+ int start_pos;
+
+ if (element_count - 1 == static_part)
+ {
+ memcpy (*perm_table, elements, element_count * sizeof (int));
+ return;
+ }
+
+ start_pos = 0;
+ for (i = 0; i < element_count - static_part; i ++)
+ {
+ memcpy (elements_copy, elements, element_count * sizeof (int));
+ elements_copy [static_part] = elements [static_part + i];
+ elements_copy [static_part + i] = elements [static_part];
+ generate_perm_table (elements_copy, element_count, static_part + 1,
+ &perm_table [start_pos]);
+ start_pos += get_perm_count (element_count - static_part - 1);
+ }
+}
+
+int get_perm_count (int op_count)
+{
+ int i;
+ int perm_count = 1;
+
+ for (i = 2; i <= op_count; i ++)
+ perm_count *= i;
+
+ return perm_count;
+}
+
+void apply_add_operation (Entry_State *entry, Operation *op)
+{
+ if (op->sv_attr)
+ {
+ Value_State *val;
+ Value_State temp_val;
+
+ if (op->value_index == entry->sv_attr.current_value.value_index)
+ {
+ val = &entry->sv_attr.current_value;
+ }
+ else if (entry->sv_attr.pending_value &&
+ op->value_index == entry->sv_attr.pending_value->value_index)
+ {
+ val = entry->sv_attr.pending_value;
+ }
+ else /* new value */
+ {
+ init_value_state (&temp_val, op->value_index);
+ val = &temp_val;
+ }
+
+ if (val->presence_csn < op->csn)
+ val->presence_csn = op->csn;
+
+ resolve_sv_attr_state (entry, val);
+ }
+ else
+ {
+ if (entry->mv_attr.values[op->value_index].presence_csn < op->csn)
+ {
+ entry->mv_attr.values[op->value_index].presence_csn = op->csn;
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[op->value_index]));
+ }
+ }
+}
+
+void apply_value_delete_operation (Entry_State *entry, Operation *op)
+{
+ if (op->sv_attr)
+ {
+ if (entry->sv_attr.attr_state.delete_csn < op->csn)
+ {
+ entry->sv_attr.attr_state.delete_csn = op->csn;
+ resolve_sv_attr_state (entry, NULL);
+ }
+ }
+ else /* mv attr */
+ {
+ if (entry->mv_attr.values[op->value_index].delete_csn < op->csn)
+ {
+ entry->mv_attr.values[op->value_index].delete_csn = op->csn;
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[op->value_index]));
+ }
+ }
+}
+
+void apply_attr_delete_operation (Entry_State *entry, Operation *op)
+{
+ int i;
+
+ if (op->sv_attr)
+ {
+ if (entry->sv_attr.attr_state.delete_csn < op->csn)
+ {
+ entry->sv_attr.attr_state.delete_csn = op->csn;
+ resolve_sv_attr_state (entry, NULL);
+ }
+ }
+ else /* mv attr */
+ {
+ if (entry->mv_attr.attr_state.delete_csn < op->csn)
+ {
+ entry->mv_attr.attr_state.delete_csn = op->csn;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[i]));
+ }
+ }
+ }
+}
+
+void apply_rename_operation (Entry_State *entry, Operation *op)
+{
+ int index;
+ Operation del_op;
+
+ /* insert new dn into dn_csn_list */
+ index = dn_csn_add (entry, op->sv_attr, op->value_index, op->csn);
+
+ /* issue delete value operation for the old rdn */
+ if (op->delete_old_rdn)
+ {
+ del_op.type = OP_DELETE_VALUE;
+ del_op.csn = op->csn;
+ del_op.sv_attr = op->old_rdn_sv_attr;
+ del_op.value_index = op->old_rdn_value_index;
+
+ apply_value_delete_operation (entry, &del_op);
+ }
+
+ /* resolve state of the previous node in dn_csn_list */
+ if (index > 0)
+ {
+ if (entry->dn_csns[index-1].sv_attr)
+ {
+ if (entry->dn_csns[index-1].value_index ==
+ entry->sv_attr.current_value.value_index)
+ {
+ resolve_sv_attr_state (entry, &(entry->sv_attr.current_value));
+ }
+ else if (entry->sv_attr.pending_value &&
+ entry->dn_csns[index-1].value_index ==
+ entry->sv_attr.pending_value->value_index)
+ {
+ resolve_sv_attr_state (entry, entry->sv_attr.pending_value);
+ }
+ }
+ else
+ {
+ int i = entry->dn_csns[index-1].value_index;
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[i]));
+ }
+ }
+
+ /* resolve state of the new dn */
+ if (op->sv_attr)
+ {
+ Value_State *value;
+ Value_State temp_val;
+ if (op->value_index == entry->sv_attr.current_value.value_index)
+ {
+ value = &entry->sv_attr.current_value;
+ }
+ else if (entry->sv_attr.pending_value &&
+ op->value_index == entry->sv_attr.pending_value->value_index)
+ {
+ value = entry->sv_attr.pending_value;
+ }
+ else /* new value */
+ {
+ init_value_state (&temp_val, op->value_index);
+ value = &temp_val;
+ }
+
+ if (value->presence_csn == NOT_PRESENT || value->presence_csn < op->csn)
+ value->presence_csn = op->csn;
+ resolve_sv_attr_state (entry, value);
+ }
+ else
+ {
+ if (entry->mv_attr.values[op->value_index].presence_csn == NOT_PRESENT ||
+ entry->mv_attr.values[op->value_index].presence_csn < op->csn)
+ entry->mv_attr.values[op->value_index].presence_csn = op->csn;
+
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[op->value_index]));
+ }
+}
+
+void purge_mv_attr_state (Entry_State *entry, Value_State *value)
+{
+ if (value->presence_csn > value->delete_csn)
+ value->delete_csn = NOT_PRESENT;
+}
+
+void purge_sv_attr_state (Entry_State *entry)
+{
+ if (entry->sv_attr.attr_state.delete_csn != NOT_PRESENT)
+ {
+ if (entry->sv_attr.pending_value)
+ {
+ if (entry->sv_attr.attr_state.delete_csn <
+ entry->sv_attr.pending_value->presence_csn)
+ {
+ entry->sv_attr.attr_state.delete_csn = NOT_PRESENT;
+ }
+ }
+ else
+ {
+ if (entry->sv_attr.attr_state.delete_csn <
+ entry->sv_attr.current_value.presence_csn)
+ entry->sv_attr.attr_state.delete_csn = NOT_PRESENT;
+ }
+ }
+}
+
+void resolve_mv_attr_state (Entry_State *entry, Value_State *value)
+{
+ purge_mv_attr_state (entry, value);
+
+ /* no deletes that effect the state */
+ if (max_val (value->delete_csn, entry->mv_attr.attr_state.delete_csn) <
+ value->presence_csn)
+ {
+ value->present = 1;
+ return;
+ }
+
+ if (value->present) /* check if it should be removed based on the current state */
+ {
+ if (!value_distinguished_at_csn (entry, 0, value,
+ max (value->delete_csn, entry->mv_attr.attr_state.delete_csn)))
+ {
+ value->present = 0;
+ }
+ }
+ else /* not present - check if it should be restored */
+ {
+ if (value_distinguished_at_csn (entry, 0, value,
+ max (value->delete_csn, entry->mv_attr.attr_state.delete_csn)))
+ {
+ value->present = 1;
+ }
+ }
+
+ if (entry->mv_attr.attr_state.delete_csn == NOT_PRESENT)
+ {
+ entry->mv_attr.attr_state.present = 1;
+ }
+ else
+ {
+ int i;
+ int distinguished = 0;
+
+ for (i = 0; i < entry->mv_attr.value_count; i ++)
+ {
+ distinguished |= value_distinguished_at_csn (entry, 0,
+ &(entry->mv_attr.values[i]),
+ entry->mv_attr.attr_state.delete_csn);
+ }
+
+ entry->mv_attr.attr_state.present = distinguished;
+ }
+}
+
+void resolve_sv_attr_state (Entry_State *entry, Value_State *value)
+{
+ purge_sv_attr_state (entry);
+
+ if (value)
+ {
+ /* existing value is modified */
+ if (value == &(entry->sv_attr.current_value) ||
+ value == entry->sv_attr.pending_value)
+ {
+ /* check if current value should be replaced with the pending value */
+ if (entry->sv_attr.pending_value)
+ {
+ if (!value_distinguished_at_csn (entry, 1, &entry->sv_attr.current_value,
+ entry->sv_attr.current_value.presence_csn))
+ {
+ /* replace current value with the pending value */
+ memcpy (&entry->sv_attr.current_value, entry->sv_attr.pending_value,
+ sizeof (Value_State));
+ free (entry->sv_attr.pending_value);
+ entry->sv_attr.pending_value = NULL;
+ }
+ }
+ }
+ else /* addition of a new value */
+ {
+ /* new value is before the current value; note that, for new value,
+ presence_csn is the same as distinguished_csn */
+ if (value->presence_csn < entry->sv_attr.current_value.presence_csn)
+ {
+ /* if new value is distinguished, it should become current and the
+ current can become pending */
+ if (value_distinguished_at_csn (entry, 1, value,
+ entry->sv_attr.current_value.presence_csn))
+ {
+ if (entry->sv_attr.pending_value == NULL)
+ {
+ entry->sv_attr.pending_value = (Value_State*)
+ malloc (sizeof (Value_State));
+ memcpy (entry->sv_attr.pending_value, &entry->sv_attr.current_value,
+ sizeof (Value_State));
+ }
+
+ memcpy (&entry->sv_attr.current_value, value, sizeof (Value_State));
+ }
+ }
+ else /* new value is after the current value */
+ {
+ /* if current value is not distinguished, new value should
+ become distinguished */
+ if (!value_distinguished_at_csn (entry, 1, &entry->sv_attr.current_value,
+ value->presence_csn))
+ {
+ memcpy (&entry->sv_attr.current_value, value, sizeof (Value_State));
+ }
+ else /* current value is distinguished - check if new value should replace
+ the pending value */
+ { if (entry->sv_attr.pending_value)
+ {
+ if (value->presence_csn > entry->sv_attr.pending_value->presence_csn)
+ {
+ memcpy (entry->sv_attr.pending_value, value, sizeof (Value_State));
+ }
+ }
+ else
+ {
+ entry->sv_attr.pending_value = (Value_State*)malloc (sizeof (Value_State));
+ memcpy (entry->sv_attr.pending_value, value, sizeof (Value_State));
+ }
+ }
+ }
+ }
+ }
+
+ /* update the attribute state */
+ purge_sv_attr_state (entry);
+
+ /* set attribute state */
+ if (entry->sv_attr.attr_state.delete_csn != NOT_PRESENT &&
+ !value_distinguished_at_csn (entry, 1, &entry->sv_attr.current_value,
+ entry->sv_attr.attr_state.delete_csn))
+ {
+ entry->sv_attr.attr_state.present = 0;
+ }
+ else
+ {
+ entry->sv_attr.attr_state.present = 1;
+ }
+}
+
+int value_distinguished_at_csn (Entry_State *entry, int sv_attr, Value_State *value, int csn)
+{
+ int i;
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > csn)
+ break;
+ }
+
+ /* i is never equal to 0 because the csn of the first element is always
+ smaller than csn of any operation we can receive */
+ return (entry->dn_csns[i-1].value_index == value->value_index &&
+ entry->dn_csns[i-1].sv_attr == sv_attr);
+}
+
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int i;
+ int error = 0;
+
+ error = compare_entry_state_quick (entry1, entry2, run);
+
+ if (error)
+ return 3;
+
+ /* compare dnc_csn list */
+ if (entry1->dn_csn_count != entry2->dn_csn_count)
+ {
+ fprintf (sim.fout, "dn_csn count is %d for run 1 and %d for run %d\n",
+ entry1->dn_csn_count, entry2->dn_csn_count, run);
+ error = 1;
+ }
+
+ for (i = 0; i < entry1->dn_csn_count; i++)
+ {
+ if (entry1->dn_csns [i].csn != entry2->dn_csns [i].csn ||
+ entry1->dn_csns [i].sv_attr != entry2->dn_csns [i].sv_attr ||
+ entry1->dn_csns [i].value_index != entry2->dn_csns [i].value_index)
+ {
+ fprintf (sim.fout,"elements %d of dn csn list are different:\n"
+ "\tfirst run: csn - %d, attr - %s, value - %s\n"
+ "\t%d run: csn - %d, attr - %s value - %s\n", i,
+ entry1->dn_csns [i].csn,
+ entry1->dn_csns [i].sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values[entry1->dn_csns [i].value_index],
+ run, entry2->dn_csns [i].csn,
+ entry2->dn_csns [i].sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values[entry2->dn_csns [i].value_index]);
+
+ error = 1;
+ }
+ }
+
+ error |= compare_sv_attr_state (&entry1->sv_attr, &entry2->sv_attr, run);
+
+ error |= compare_mv_attr_state (&entry1->mv_attr, &entry2->mv_attr, run);
+
+ if (error != 0)
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* just compare if the same attributes and values are present */
+int compare_entry_state_quick (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int error;
+
+ error = compare_sv_attr_state_quick (&entry1->sv_attr, &entry2->sv_attr, run);
+
+ error |= compare_mv_attr_state_quick (&entry1->mv_attr, &entry2->mv_attr, run);
+
+ return error;
+}
+
+int compare_sv_attr_state_quick (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run)
+{
+ int error = 0;
+ if (sv_attr1->attr_state.present != sv_attr2->attr_state.present)
+ {
+ fprintf (sim.fout, "singlevalued attribute is %s present in the first run "
+ "but is %s present in the %d run\n",
+ sv_attr1->attr_state.present ? "" : "not",
+ sv_attr2->attr_state.present ? "" : "not", run);
+ return 1;
+ }
+
+ if (sv_attr1->attr_state.present &&
+ sv_attr1->current_value.value_index != sv_attr2->current_value.value_index)
+ {
+ fprintf (sim.fout, "different values for singlevalued attribute: %s for the \n"
+ "first run and %s for the %d run\n",
+ g_values [sv_attr1->current_value.value_index],
+ g_values [sv_attr2->current_value.value_index], run);
+ return 1;
+ }
+
+ return 0;
+}
+
+int compare_mv_attr_state_quick (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run)
+{
+ int i;
+ int error = 0;
+
+ if (mv_attr1->attr_state.present != mv_attr2->attr_state.present)
+ {
+ fprintf (sim.fout, "multivalued attribute is %s present in the first run "
+ "but is %s present in the %d run\n",
+ mv_attr1->attr_state.present ? "" : "not",
+ mv_attr2->attr_state.present ? "" : "not", run);
+ return 1;
+ }
+
+ /* value count does not change during the iteration, so we don't have
+ to check if the count is the same for both attributes */
+ for (i = 0; i < mv_attr1->value_count; i++)
+ {
+ if (mv_attr1->values[i].present != mv_attr2->values[i].present)
+ {
+ fprintf (sim.fout, "value %s is %s present in the multivalued attribute\n"
+ "in the first run but %s present in the %d run\n",
+ g_values[i], mv_attr1->values[i].present ? "" : "not",
+ mv_attr2->values[i].present ? "" : "not", run);
+ error = 1;
+ }
+ }
+
+ return error;
+}
+
+int compare_sv_attr_state (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run)
+{
+ int error = 0;
+
+ if (sv_attr1->attr_state.delete_csn != sv_attr2->attr_state.delete_csn)
+ {
+ fprintf (sim.fout, "singlevalued attribute deletion csn is %d for run 1 "
+ "but is %d for run %d\n", sv_attr1->attr_state.delete_csn,
+ sv_attr2->attr_state.delete_csn, run);
+ error = 1;
+ }
+
+ error |= compare_value_state (&sv_attr1->current_value, &sv_attr2->current_value, run);
+
+ if ((sv_attr1->pending_value && !sv_attr1->pending_value) ||
+ (!sv_attr1->pending_value && sv_attr1->pending_value))
+ {
+ fprintf (sim.fout, "pending value is %s present in the singlevalued attribute\n"
+ " in the first run but is %s in the %d run\n",
+ sv_attr1->pending_value ? "" : "not",
+ sv_attr2->pending_value ? "" : "not", run);
+
+ return 1;
+ }
+
+ if (sv_attr1->pending_value)
+ error |= compare_value_state (sv_attr1->pending_value, sv_attr2->pending_value, run);
+
+ return 0;
+}
+
+int compare_mv_attr_state (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run)
+{
+ int error = 0;
+ int i;
+
+ if (mv_attr1->attr_state.delete_csn != mv_attr2->attr_state.delete_csn)
+ {
+ fprintf (sim.fout, "multivalued attribute deletion csn is %d for run 1 "
+ "but is %d for run %d\n", mv_attr1->attr_state.delete_csn,
+ mv_attr2->attr_state.delete_csn, run);
+ error = 1;
+ }
+
+ for (i = 0; i < mv_attr1->value_count; i++)
+ {
+ error |= compare_value_state (&mv_attr1->values[i], &mv_attr2->values[i], run);
+ }
+
+ return error;
+}
+
+int compare_value_state (Value_State *value1, Value_State *value2, int run)
+{
+ int error = 0;
+
+ if (value1->presence_csn != value2->presence_csn)
+ {
+ fprintf (sim.fout, "multivalued attribute: presence csn for value %s is %d "
+ "in run 1 but is %d in run %d\n", g_values[value1->value_index],
+ value1->presence_csn, value2->presence_csn, run);
+ error = 1;
+ }
+
+ if (value1->delete_csn != value2->delete_csn)
+ {
+ fprintf (sim.fout, "multivalued attribute: delete csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[value1->value_index],
+ value1->delete_csn, value2->delete_csn, run);
+ error = 1;
+ }
+
+ return error;
+}
+
+int dn_csn_add (Entry_State *entry, int sv_attr, int value_index, int csn)
+{
+ int i;
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > csn)
+ break;
+ }
+
+ if (i < entry->dn_csn_count)
+ {
+ memcpy (&(entry->dn_csns[i+1]), &(entry->dn_csns[i]),
+ (entry->dn_csn_count - i) * sizeof (Dn_Csn));
+ }
+
+ entry->dn_csns[i].csn = csn;
+ entry->dn_csns[i].sv_attr = sv_attr;
+ entry->dn_csns[i].value_index = value_index;
+ entry->dn_csn_count ++;
+
+ return i;
+}
+
+void dump_operations (Operation *ops, int op_count, int *order)
+{
+ int index;
+ int i;
+
+ for (i = 0; i < op_count; i ++)
+ {
+ if (order == NULL) /* current order */
+ index = i;
+ else
+ index = order [i];
+
+ dump_operation (&ops[index]);
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_operation (Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE:
+ fprintf (sim.fout, "\t%d add value %s to %s\n", op->csn,
+ g_values [op->value_index],
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME);
+ break;
+ case OP_DELETE_VALUE:
+ fprintf (sim.fout, "\t%d delete value %s from %s\n", op->csn,
+ g_values [op->value_index],
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME);
+ break;
+ case OP_DELETE_ATTR:
+ fprintf (sim.fout, "\t%d delete %s attribute\n", op->csn,
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME);
+ break;
+ case OP_RENAME_ENTRY:
+ fprintf (sim.fout, "\t%d rename entry to %s=%s", op->csn,
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values [op->value_index]);
+ if (op->delete_old_rdn)
+ fprintf (sim.fout, " delete old rdn %s=%s\n",
+ op->old_rdn_sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values [op->old_rdn_value_index]);
+ else
+ fprintf (sim.fout, "\n");
+ break;
+ }
+}
+
+void dump_perm_table (int **perm_table, int op_count)
+{
+ int i, j;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < op_count; i++)
+ {
+ for (j = 0; j < perm_count; j++)
+ {
+ fprintf (sim.fout, "%d ", perm_table [j][i]);
+ }
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_entry_state (Entry_State *entry)
+{
+ dump_dn_csn_list (entry);
+
+ dump_sv_attr_state (&entry->sv_attr);
+ dump_mv_attr_state (&entry->mv_attr);
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_sv_attr_state (SV_Attr_State *sv_attr)
+{
+ fprintf (sim.fout, "\tattribute %s is %s present", SV_ATTR_NAME,
+ sv_attr->attr_state.present ? "" : "not");
+ if (sv_attr->attr_state.present)
+ {
+ fprintf (sim.fout, " and has the value of %s\n",
+ g_values[sv_attr->current_value.value_index]);
+ }
+ else
+ {
+ fprintf (sim.fout, "\n");
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tdeletion csn: %d\n", sv_attr->attr_state.delete_csn);
+ fprintf (sim.fout, "\t\tcurrent value: ");
+ dump_value_state (&sv_attr->current_value, 1/* for single valued attr */);
+ if (sv_attr->pending_value)
+ {
+ fprintf (sim.fout, "\t\tpending value: ");
+ dump_value_state (sv_attr->pending_value, 1/* for single valued attr */);
+ }
+ }
+}
+
+void dump_mv_attr_state (MV_Attr_State *mv_attr)
+{
+ int i;
+
+ fprintf (sim.fout, "\tattribute %s is %s present\n", MV_ATTR_NAME,
+ mv_attr->attr_state.present ? "" : "not");
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tdeletion csn: %d\n", mv_attr->attr_state.delete_csn);
+ }
+
+ for (i = 0; i < mv_attr->value_count; i++)
+ {
+ dump_value_state (&(mv_attr->values[i]), 0);
+ }
+}
+
+void dump_value_state (Value_State *value, int sv_attr)
+{
+ if (!sv_attr)
+ {
+ fprintf (sim.fout, "\tvalue %s is %s present\n", g_values[value->value_index],
+ value->present ? "" : "not");
+ }
+ else
+ {
+ fprintf (sim.fout, "%s\n", g_values[value->value_index]);
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\t\tpresence csn: %d\n", value->presence_csn);
+ fprintf (sim.fout, "\t\t\tdeletion value csn: %d\n", value->delete_csn);
+ }
+}
+
+void dump_dn_csn_list (Entry_State *entry)
+{
+ int i;
+
+ fprintf (sim.fout, "\tdn csn list: \n");
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ fprintf (sim.fout, "\t\t %s=%s, csn: %d\n",
+ entry->dn_csns[i].sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values[entry->dn_csns[i].value_index], entry->dn_csns[i].csn);
+ }
+}
+
+/* misc functions */
+int max_val (int a, int b)
+{
+ if (a >= b)
+ return a;
+ else
+ return b;
+}
diff --git a/ldap/servers/plugins/replication/tests/makesim b/ldap/servers/plugins/replication/tests/makesim
new file mode 100755
index 00000000..0cedd6e1
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/makesim
@@ -0,0 +1,58 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# gnu makefile for LDAP Server tools.
+#
+
+MCOM_ROOT = ../../../../../..
+LDAP_SRC = ../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+
+OBJDEST = $(OBJDIR)/lib/replication-plugin
+BINDIR = $(OBJDIR)/bin
+
+include $(MCOM_ROOT)/netsite/nsdefs.mk
+include $(MCOM_ROOT)/netsite/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+LDFLAGS += $(EXLDFLAGS)
+
+ifeq ($(ARCH), WINNT)
+SUBSYSTEM=console
+endif
+
+DEPLIBS=
+
+EXTRA_LIBS_DEP =
+
+EXTRA_LIBS =
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS += user32.lib
+endif
+
+DNP_SIM = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, dnp_sim))
+
+
+all: $(OBJDEST) $(BINDIR) $(DNP_SIM)
+
+$(DNP_SIM): $(OBJDEST)/dnp_sim3.o $(EXTRA_LIBS_DEP)
+ $(LINK_EXE) $(OBJDEST)/dnp_sim3.o \
+ $(EXTRA_LIBS) $<
+
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(BINDIR):
+ $(MKDIR) $(BINDIR)
+
+clean:
+ -$(RM) $(ALL_OBJS)
+ -$(RM) $(BINS)