Revision b666b39a

b/trunk/Pithos.Core.Test/SnapshotDifferencerTest.cs
28 28
        public void Setup()
29 29
        {
30 30
            _previous = new []{
31
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
32
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
33
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1},
34
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1},
35
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1},
36
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
37
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
38
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
39
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
40
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1},
41
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1},
42
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
43
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
31
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="1"},
32
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
33
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="3"},
34
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="4"},
35
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="5"},
36
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
37
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},
38
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="8"},
39
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
40
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
41
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="11"},
42
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
43
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},
44 44
                            };
45 45
            _current = new []{
46
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
47
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1},
48
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=1234,Hash="aa45",Version=1},
49
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name5",Bytes=123,Hash="aa5",Version=1},
50
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1},
51
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
52
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
53
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
54
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1},
55
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=1234,Hash="aa45",Version=1},
56
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name5",Bytes=123,Hash="aa5",Version=1},
57
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
58
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
46
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
47
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="3"},
48
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=1234,Hash="aa45",Version=1,UUID="4"},
49
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name5",Bytes=123,Hash="aa5",Version=1,UUID="14"},
50
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="5"},
51
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
52
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},
53
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
54
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
55
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=1234,Hash="aa45",Version=1,UUID="11"},
56
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name5",Bytes=123,Hash="aa5",Version=1,UUID="15"},
57
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
58
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},
59 59
                           };
60 60
            
61 61
        }
......
68 68
            Assert.That(differencer.Deleted,Is.Empty);
69 69
            Assert.That(differencer.Created,Is.EquivalentTo(_previous));
70 70
            Assert.That(differencer.Changed,Is.Empty);
71
            Assert.That(differencer.Moved, Is.Empty, "should have no moves");
71 72
            Assert.That(differencer.Unchanged,Is.Empty);
72 73
        }
73 74

  
......
78 79
            var differencer=d1.Post(_previous).Post(_current);
79 80
            var deleted=new[]
80 81
                            {
81
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
82
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
82
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="1"},
83
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="8"},
83 84
                            };
84 85
            Assert.That(differencer.Deleted.ToList(),Is.EquivalentTo(deleted)
85 86
                .Using((IEqualityComparer) new ObjectInfoComparer()));
......
91 92
            var differencer=d1.Post(_previous).Post(_current);
92 93
            var created = new[]
93 94
                              {
94
                                  new ObjectInfo { Account = "acc1", Container = "Cont1", Name = "Name5", Bytes = 123, Hash = "aa5", Version = 1 },
95
                                  new ObjectInfo { Account = "acc1", Container = "Cont2", Name = "Name5", Bytes = 123, Hash = "aa5", Version = 1 },
95
                                  new ObjectInfo { Account = "acc1", Container = "Cont1", Name = "Name5", Bytes = 123, Hash = "aa5", Version = 1,UUID="14" },
96
                                  new ObjectInfo { Account = "acc1", Container = "Cont2", Name = "Name5", Bytes = 123, Hash = "aa5", Version = 1,UUID="15" },
96 97
                              };
97 98
            Assert.That(differencer.Created.ToList(), Is.EquivalentTo(created)
98 99
                .Using((IEqualityComparer)new ObjectInfoComparer()));
......
103 104
            var d1 = new SnapshotDifferencer();            
104 105
            var differencer=d1.Post(_previous).Post(_current);
105 106
            var changed = new[] { 
106
                new ObjectInfo { Account = "acc1", Container = "Cont1", Name = "Name4", Bytes = 1234, Hash = "aa45", PreviousHash="aa4", Version = 1 },
107
                new ObjectInfo { Account = "acc1", Container = "Cont2", Name = "Name4", Bytes = 1234, Hash = "aa45", PreviousHash="aa4", Version = 1 },
107
                new ObjectInfo { Account = "acc1", Container = "Cont1", Name = "Name4", Bytes = 1234, Hash = "aa45", PreviousHash="aa4", Version = 1,UUID="4" },
108
                new ObjectInfo { Account = "acc1", Container = "Cont2", Name = "Name4", Bytes = 1234, Hash = "aa45", PreviousHash="aa4", Version = 1,UUID="11" },
108 109
            };
109 110

  
110 111
            Comparison<ObjectInfo> comparer = (x, y) =>
......
122 123
        }
123 124

  
124 125
        [Test]
126
        public void when_adding_a_second_snapshot_with_a_rename()
127
        {
128
            var d1 = new SnapshotDifferencer();
129
            var current = new[]{
130
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="1"},
131
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
132
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3a",Bytes=123,Hash="aa3",Version=1,UUID="3"},
133
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="4"},
134
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="5"},
135
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
136
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},
137
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="8"},
138
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
139
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
140
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="11"},
141
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
142
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},
143
                            };
144

  
145
            var differencer=d1.Post(_previous).Post(current);
146
            var moved = new[] { 
147
                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3a",Bytes=123,Hash="aa3",PreviousHash="aa3",Version=1,UUID="3"}
148
            };
149

  
150
            Comparison<ObjectInfo> comparer = (x, y) =>
151
                                                  {
152
                                                      if (x.Account == y.Account
153
                                                          && x.Container == y.Container
154
                                                          && x.Name == y.Name
155
                                                          && x.Hash == y.Hash
156
                                                          && x.PreviousHash == y.PreviousHash)
157
                                                          return 0;
158
                                                      return 1;
159
                                                  };
160
            Assert.That(differencer.Moved.ToList(), Is.EquivalentTo(moved).Using(comparer));
161
            Assert.That(differencer.Deleted, Is.Empty);
162
            Assert.That(differencer.Created, Is.Empty);
163
            Assert.That(differencer.Changed, Is.Empty);
164
            //Assert.That(differencer.Unchanged, Is.Empty);
165
            Assert.That(differencer.Moved.ToList(), Is.EquivalentTo(moved).Using(comparer));
166
        }
167

  
168
        [Test]
169
        public void when_adding_a_second_snapshot_with_a_changed_rename()
170
        {
171
            var d1 = new SnapshotDifferencer();
172
            var current = new[]{
173
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="1"},
174
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
175
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3a",Bytes=123,Hash="aa35",Version=1,UUID="3"},
176
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="4"},
177
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="5"},
178
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
179
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},
180
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="8"},
181
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
182
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
183
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="11"},
184
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
185
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},
186
                            };
187

  
188
            var differencer = d1.Post(_previous).Post(current);
189
            var moved = new[] { 
190
                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3a",Bytes=123,Hash="aa35",PreviousHash="aa3",Version=1,UUID="3"}
191
            };
192

  
193
            Comparison<ObjectInfo> comparer = (x, y) =>
194
            {
195
                if (x.Account == y.Account
196
                    && x.Container == y.Container
197
                    && x.Name == y.Name
198
                    && x.Hash == y.Hash
199
                    && x.PreviousHash == y.PreviousHash)
200
                    return 0;
201
                return 1;
202
            };
203
            Assert.That(differencer.Moved.ToList(), Is.EquivalentTo(moved).Using(comparer));
204
            Assert.That(differencer.Deleted, Is.Empty);
205
            Assert.That(differencer.Created, Is.Empty);
206
            Assert.That(differencer.Changed, Is.Empty);
207
            //Assert.That(differencer.Unchanged, Is.Empty);
208
            Assert.That(differencer.Moved.ToList(), Is.EquivalentTo(moved).Using(comparer));
209
        }
210

  
211
        [Test]
212
        public void when_adding_a_second_snapshot_with_a_move()
213
        {
214
            var d1 = new SnapshotDifferencer();
215
            var current = new[]{
216
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="1"},
217
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
218
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name3",Bytes=123,Hash="aa3",Version=1,UUID="3"},
219
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="4"},
220
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="5"},
221
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
222
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},
223
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="8"},
224
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
225
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
226
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="11"},
227
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
228
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},
229
                            };
230

  
231
            var differencer=d1.Post(_previous).Post(current);
232
            var moved = new[] { 
233
                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name3",Bytes=123,Hash="aa3",PreviousHash="aa3",Version=1},
234
            };
235

  
236
            Comparison<ObjectInfo> comparer = (x, y) =>
237
                                                  {
238
                                                      if (x.Account == y.Account
239
                                                          && x.Container == y.Container
240
                                                          && x.Name == y.Name
241
                                                          && x.Hash == y.Hash
242
                                                          && x.PreviousHash == y.PreviousHash)
243
                                                          return 0;
244
                                                      return 1;
245
                                                  };
246

  
247
            Assert.That(differencer.Deleted, Is.Empty);
248
            Assert.That(differencer.Created, Is.Empty);
249
            Assert.That(differencer.Changed, Is.Empty);
250
            //Assert.That(differencer.Unchanged, Is.Empty);
251
            Assert.That(differencer.Moved.ToList(), Is.EquivalentTo(moved).Using(comparer));
252
        }
253

  
254
        [Test]
125 255
        public void when_adding_a_second_snapshot_with_unchanged()
126 256
        {
127 257
            var d1 = new SnapshotDifferencer();            
128 258
            var differencer=d1.Post(_previous).Post(_current);
129 259
            var unchanged = new[]
130 260
                                {
131
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
132
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1},
133
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
134
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},  
135
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1},  
136
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
137
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1},
138
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
139
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},                                    
261
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
262
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="3"},
263
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
264
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},  
265
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="5"},  
266
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
267
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
268
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
269
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},                                    
140 270
                                };            
141 271
            Assert.That(differencer.Unchanged.ToList(), Is.EquivalentTo(unchanged)
142 272
                .Using((IEqualityComparer)new ObjectInfoComparer()));
......
150 280
            Assert.That(differencer.Deleted, Is.Empty);
151 281
            Assert.That(differencer.Created, Is.Empty);
152 282
            Assert.That(differencer.Changed, Is.Empty);
283
            Assert.That(differencer.Moved, Is.Empty, "should have no moves");
153 284
            Assert.That(differencer.Unchanged, Is.Empty);
154 285
        }
155 286

  
......
161 292
            Assert.That(differencer.Deleted, Is.EquivalentTo(_previous));
162 293
            Assert.That(differencer.Created, Is.Empty);
163 294
            Assert.That(differencer.Changed, Is.Empty);
295
            Assert.That(differencer.Moved, Is.Empty, "should have no moves");
164 296
            Assert.That(differencer.Unchanged, Is.Empty);
165 297
        }
166 298

  
......
181 313
            Assert.That(differencer.Deleted, Is.Empty,"should have no deletions");
182 314
            Assert.That(differencer.Created, Is.Empty,"should have no insertions");
183 315
            Assert.That(differencer.Changed, Is.Empty,"should have no changes");
316
            Assert.That(differencer.Moved, Is.Empty, "should have no moves");
184 317
            Assert.That(differencer.Unchanged, Is.EquivalentTo(_previous),"should be equivalent to previous");
185 318
            
186 319
        }
......
199 332
                                };
200 333
            var deleted = new[]
201 334
                            {
202
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
203
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
204
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1},
205
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1},
335
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="1"},
336
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="2"},
337
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="3"},
338
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="4"},
206 339
                            };
207 340
            var unchanged = new[]{
208
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1},
209
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1},
210
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1},
211
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
212
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
213
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1},
214
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1},
215
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1},
216
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1},
341
                                new ObjectInfo{Account="acc1",Container="Cont1",Name="Folder1/Name1",Bytes=123,Hash="aa4",Version=1,UUID="1"},
342
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="6"},
343
                                new ObjectInfo{Account="acc2",Container="Cont1",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="7"},
344
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="8"},
345
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="9"},
346
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name3",Bytes=123,Hash="aa3",Version=1,UUID="10"},
347
                                new ObjectInfo{Account="acc1",Container="Cont2",Name="Name4",Bytes=123,Hash="aa4",Version=1,UUID="11"},
348
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name1",Bytes=123,Hash="aa1",Version=1,UUID="12"},
349
                                new ObjectInfo{Account="acc2",Container="Cont2",Name="Name2",Bytes=123,Hash="aa2",Version=1,UUID="13"},
217 350
                            };
218 351
            var differencer = d1.Post(_previous)
219 352
                .Post(noModItems);
......
222 355
            Assert.That(differencer.Deleted.ToList(), Is.EquivalentTo(deleted).Using(_comparer), "should have deletions only from acc1/cont1");
223 356
            Assert.That(differencer.Created, Is.Empty,"should have no insertions");
224 357
            Assert.That(differencer.Changed, Is.Empty,"should have no changes");
358
            Assert.That(differencer.Moved, Is.Empty, "should have no moves");
225 359
            Assert.That(differencer.Unchanged, Is.EquivalentTo(unchanged).Using(_comparer),"should be equivalent to previous except the direct leafs of acc1/cont1");
226 360
            
227 361
        }
b/trunk/Pithos.Core/Agents/CloudTransferAction.cs
55 55
        DownloadUnconditional,
56 56
        DeleteLocal,
57 57
        DeleteCloud,
58
        RenameCloud
58
        RenameCloud,
59
        RenameLocal
59 60
    }
60 61

  
61 62
    public class CloudAction
b/trunk/Pithos.Core/Agents/FileAgent.cs
71 71

  
72 72
        private AccountInfo AccountInfo { get; set; }
73 73

  
74
        private string RootPath { get;  set; }
74
        internal string RootPath { get;  set; }
75 75

  
76 76
        private static readonly ILog Log = LogManager.GetLogger("FileAgent");
77 77

  
b/trunk/Pithos.Core/Agents/NetworkAgent.cs
161 161
                                var moveAction = (CloudMoveAction)action;
162 162
                                RenameCloudFile(accountInfo, moveAction);
163 163
                                break;
164
                            case CloudActionType.RenameLocal:
165
                                RenameLocalFile(accountInfo, action);
166
                                break;
164 167
                            case CloudActionType.MustSynch:
165 168
                                if (!File.Exists(downloadPath) && !Directory.Exists(downloadPath))
166 169
                                {
......
214 217
            }
215 218
        }
216 219

  
220

  
217 221
        private void UpdateStatus(PithosStatus status)
218 222
        {
219 223
            StatusKeeper.SetPithosStatus(status);
220 224
            StatusNotification.Notify(new Notification());
221 225
        }
222 226

  
223
        
227
        private void RenameLocalFile(AccountInfo accountInfo, CloudAction action)
228
        {
229
            if (accountInfo == null)
230
                throw new ArgumentNullException("accountInfo");
231
            if (action == null)
232
                throw new ArgumentNullException("action");
233
            if (action.LocalFile == null)
234
                throw new ArgumentException("The action's local file is not specified", "action");
235
            if (!Path.IsPathRooted(action.LocalFile.FullName))
236
                throw new ArgumentException("The action's local file path must be absolute", "action");
237
            if (action.CloudFile == null)
238
                throw new ArgumentException("The action's cloud file is not specified", "action");
239
            Contract.EndContractBlock();
240

  
241
            //We assume that the local file already exists, otherwise the poll agent
242
            //would have issued a download request
243

  
244
            var currentInfo = action.CloudFile;
245
            var previousInfo = action.CloudFile.Previous;
246
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
247

  
248
            var previousRelativepath = previousInfo.RelativeUrlToFilePath(accountInfo.UserName);
249
            var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
250

  
251
            //In every case we need to move the local file first
252
            MoveLocalFile(accountInfo, previousFile, fileAgent, currentInfo);
253

  
254
        }
255

  
256
        private void MoveLocalFile(AccountInfo accountInfo, FileSystemInfo previousFile, FileAgent fileAgent,
257
                                   ObjectInfo currentInfo)
258
        {
259
            var currentRelativepath = currentInfo.RelativeUrlToFilePath(accountInfo.UserName);
260
            var newPath = Path.Combine(fileAgent.RootPath, currentRelativepath);
261

  
262
            var isFile= (previousFile is FileInfo);
263
            var previousFullPath = isFile? 
264
                FileInfoExtensions.GetProperFilePathCapitalization(previousFile.FullName):
265
                FileInfoExtensions.GetProperDirectoryCapitalization(previousFile.FullName);                
266
            
267
            using (var gateOld = NetworkGate.Acquire(previousFullPath, NetworkOperation.Renaming))
268
            using (var gateNew = NetworkGate.Acquire(newPath,NetworkOperation.Renaming))
269
            using (new SessionScope(FlushAction.Auto))
270
            {
271
                if (isFile)
272
                    (previousFile as FileInfo).MoveTo(newPath);
273
                else
274
                {
275
                    (previousFile as DirectoryInfo).MoveTo(newPath);
276
                }
277
                var state = StatusKeeper.GetStateByFilePath(previousFullPath);
278
                state.FilePath = newPath;
279
                state.SaveCopy();
280
                StatusKeeper.SetFileState(previousFullPath,FileStatus.Deleted,FileOverlayStatus.Deleted);
281
            }            
282
        }
283

  
224 284
        private async Task SyncFiles(AccountInfo accountInfo,CloudAction action)
225 285
        {
226 286
            if (accountInfo == null)
b/trunk/Pithos.Core/Agents/PollAgent.cs
270 270
                        
271 271

  
272 272
                        //Create a list of actions from the remote files
273
                        var allActions = ChangesToActions(accountInfo, differencer.Changed.FilterDirectlyBelow(SelectiveUris))
273
                        var allActions = MovesToActions(accountInfo,differencer.Moved.FilterDirectlyBelow(SelectiveUris))
274
                                        .Union(
275
                                        ChangesToActions(accountInfo, differencer.Changed.FilterDirectlyBelow(SelectiveUris)))
274 276
                                        .Union(
275 277
                                        CreatesToActions(accountInfo, differencer.Created.FilterDirectlyBelow(SelectiveUris)));
276 278

  
......
379 381

  
380 382
        }
381 383

  
382
        //Creates an appropriate action for each server file
384
        /// <summary>
385
        /// Creates a Sync action for each changed server file
386
        /// </summary>
387
        /// <param name="accountInfo"></param>
388
        /// <param name="changes"></param>
389
        /// <returns></returns>
383 390
        private IEnumerable<CloudAction> ChangesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> changes)
384 391
        {
385 392
            if (changes == null)
......
392 399
            foreach (var objectInfo in changes)
393 400
            {
394 401
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
395
                //and remove any matching objects from the list, adding them to the commonObjects list
402
                //If a directory object already exists, we may need to sync it
396 403
                if (fileAgent.Exists(relativePath))
397 404
                {
398
                    //If a directory object already exists, we don't need to perform any other action                    
399 405
                    var localFile = fileAgent.GetFileSystemInfo(relativePath);
406
                    //We don't need to sync directories
400 407
                    if (objectInfo.Content_Type == @"application/directory" && localFile is DirectoryInfo)
401 408
                        continue;
402 409
                    using (new SessionScope(FlushAction.Never))
......
418 425
            }
419 426
        }
420 427

  
428
        /// <summary>
429
        /// Creates a Local Move action for each moved server file
430
        /// </summary>
431
        /// <param name="accountInfo"></param>
432
        /// <param name="moves"></param>
433
        /// <returns></returns>
434
        private IEnumerable<CloudAction> MovesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> moves)
435
        {
436
            if (moves == null)
437
                throw new ArgumentNullException();
438
            Contract.EndContractBlock();
439
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
440

  
441
            //In order to avoid multiple iterations over the files, we iterate only once
442
            //over the remote files
443
            foreach (var objectInfo in moves)
444
            {
445
                var previousRelativepath = objectInfo.Previous.RelativeUrlToFilePath(accountInfo.UserName);
446
                //If the previous file already exists, we can execute a Move operation
447
                if (fileAgent.Exists(previousRelativepath))
448
                {
449
                    var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
450
                    using (new SessionScope(FlushAction.Never))
451
                    {
452
                        var state = StatusKeeper.GetStateByFilePath(previousFile.FullName);
453
                        _lastSeen[previousFile.FullName] = DateTime.Now;
454

  
455
                        //For each moved object we need to move both the local file and update                                                
456
                        yield return new CloudAction(accountInfo, CloudActionType.RenameLocal,
457
                                                     previousFile, objectInfo, state, accountInfo.BlockSize,
458
                                                     accountInfo.BlockHash);
459
                        //For modified files, we need to download the changes as well
460
                        if (objectInfo.Hash!=objectInfo.PreviousHash)
461
                            yield return new CloudDownloadAction(accountInfo,objectInfo);
462
                    }
463
                }
464
                //If the previous file does not exist, we need to download it in the new location
465
                else
466
                {
467
                    //Remote files should be downloaded
468
                    yield return new CloudDownloadAction(accountInfo, objectInfo);
469
                }
470
            }
471
        }
472

  
473

  
474
        /// <summary>
475
        /// Creates a download action for each new server file
476
        /// </summary>
477
        /// <param name="accountInfo"></param>
478
        /// <param name="creates"></param>
479
        /// <returns></returns>
421 480
        private IEnumerable<CloudAction> CreatesToActions(AccountInfo accountInfo, IEnumerable<ObjectInfo> creates)
422 481
        {
423 482
            if (creates == null)
......
430 489
            foreach (var objectInfo in creates)
431 490
            {
432 491
                var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
433
                //and remove any matching objects from the list, adding them to the commonObjects list
492
                //If the object already exists, we probably have a conflict
434 493
                if (fileAgent.Exists(relativePath))
435 494
                {
436
                    //If the object already exists, we probably have a conflict
437 495
                    //If a directory object already exists, we don't need to perform any other action                    
438 496
                    var localFile = fileAgent.GetFileSystemInfo(relativePath);
439 497
                    StatusKeeper.SetFileState(localFile.FullName, FileStatus.Conflict, FileOverlayStatus.Conflict);
b/trunk/Pithos.Core/Agents/SnapshotDifferencer.cs
40 40
 */
41 41
#endregion
42 42
using System.Collections.Concurrent;
43
using System.Diagnostics.Contracts;
43 44
using Pithos.Interfaces;
44 45
using Pithos.Network;
45 46
using System;
......
71 72
            /// </summary>
72 73
            public readonly ObjectInfo[] Current;
73 74

  
75
            public readonly Dictionary<string, ObjectInfo> CurrentDict;
76

  
77
            public readonly Dictionary<string, ObjectInfo> PreviousDict;
78

  
79

  
74 80
            /// <summary>
75 81
            /// Common objects, lazily evalueated. 
76 82
            /// The common objects are used to calculate both the Changed and Unchanged objects
......
79 85

  
80 86
            private readonly static ObjectInfo[] Empty = new ObjectInfo[0];
81 87

  
88
            [ContractInvariantMethod]
89
            private void StatInvariant()
90
            {
91
// ReSharper disable InvocationIsSkipped
92
                Contract.Invariant(Current!=null);
93
                Contract.Invariant(Previous!=null);
94
                Contract.Invariant(CurrentDict!=null);
95
                Contract.Invariant(PreviousDict!=null);
96
                Contract.Invariant(Common!=null);
97
// ReSharper restore InvocationIsSkipped
98
            }
99

  
82 100
            public State(ObjectInfo[] previous, ObjectInfo[] current)
83 101
            {
84 102
                Previous = previous ?? Empty;
85 103
                Current = current ?? Empty;
104
                CurrentDict = Current.ToDictionary(info => info.UUID);
105
                PreviousDict = Previous.ToDictionary(info => info.UUID);
86 106

  
87 107
                Common=new Lazy<IEnumerable<ObjectInfo>>(() =>
88 108
                    Current.Join(Previous,
89 109
                                outKey => new { outKey.Account, outKey.Container, outKey.Name },
90 110
                                inKey => new { inKey.Account, inKey.Container, inKey.Name },
91
                                (outer, inner) =>
92
                                {
93
                                    outer.PreviousHash = inner.Hash;
94
                                    return outer;
95
                                }));            
111
                                (outer, inner) =>outer.SetPrevious(inner)));            
96 112
            }
97 113
        }
98 114

  
......
138 154
            return this;
139 155
        }
140 156
        
157
        /// <summary>
158
        /// Deleted objects are those that existed in the Previous listing
159
        /// but are not found in the Current listing, and their UUIDs do not
160
        /// appear in the Current listing (ie they were not renamed/moved)
161
        /// </summary>
141 162
        public IEnumerable<ObjectInfo> Deleted
142 163
        {
143
            get { return _state.Previous.Except(_state.Current,_comparer); }
164
            get { return _state.Previous.Except(_state.Current,_comparer)
165
                .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }
144 166
        }
167

  
168
        /// <summary>
169
        /// Created objects are those that exist in the Current listing
170
        /// but are not found in the Previous listing, and their UUIDs do not
171
        /// appear in the Previous listing (ie they were not renamed/moved)
172
        /// </summary>
145 173
        public IEnumerable<ObjectInfo> Created
146 174
        {
147
            get { return _state.Current.Except(_state.Previous,_comparer); }
175
            get { return _state.Current.Except(_state.Previous,_comparer)
176
                .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }
148 177
        }
149 178
                
150 179
        public IEnumerable<ObjectInfo> Common
......
160 189
        {
161 190
            get{ return Common.Where(i => i.PreviousHash == i.Hash);}
162 191
        }
192

  
193
        public IEnumerable<ObjectInfo>  Moved
194
        {
195
            get
196
            {
197
                                
198
                return _state.Current.Join(_state.Previous,
199
                                    outer => outer.UUID,
200
                                    inner => inner.UUID,
201
                                    (outer, inner) => (outer.Name == inner.Name ? null : outer.SetPrevious(inner)))
202
                    .Where(t => t != null);                
203
            }
204
        }
163 205
    }
164 206

  
165 207
    public class AccountsDifferencer
b/trunk/Pithos.Interfaces/ObjectInfo.cs
321 321
                   && other.Container == this.Container
322 322
                   && (this.Name == null || other.Name.StartsWith(this.Name));
323 323
        }
324

  
325

  
326
        public ObjectInfo Previous { get; private set; }
327

  
328
        public ObjectInfo SetPrevious(ObjectInfo previous)
329
        {            
330
            Previous = previous;
331
            PreviousHash = previous.Hash;
332
            return this;
333
        }
324 334
    }
325 335
}

Also available in: Unified diff