test_device_sync_diff.js
Go to the documentation of this file.
1 /* vim: set sw=2 :miv */
2 /*
3  *=BEGIN SONGBIRD GPL
4  *
5  * This file is part of the Songbird web player.
6  *
7  * Copyright(c) 2005-2011 POTI, Inc.
8  * http://www.songbirdnest.com
9  *
10  * This file may be licensed under the terms of of the
11  * GNU General Public License Version 2 (the ``GPL'').
12  *
13  * Software distributed under the License is distributed
14  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
15  * express or implied. See the GPL for the specific language
16  * governing rights and limitations.
17  *
18  * You should have received a copy of the GPL along with this
19  * program. If not, go to http://www.gnu.org/licenses/gpl.html
20  * or write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *=END SONGBIRD GPL
24  */
25 
26 
31 Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
32 
33 // These are totally bogus times, but we don't care. We only ever compare them
34 const TimeLastSynced = 1000;
37 
38 var mainLibItems = {};
39 [
40  ["track1", "Track 1", "Artist 1", "Album 1", TimeBefore],
41  ["track2", "Track 2", "Artist 1", "Album 1", TimeBefore],
42  ["track3", "Track 3", "Artist 1", "Album 1", TimeBefore],
43  ["track5", "Track 5", "Artist 1", "Album 1", TimeBefore],
44  ["track6", "Track 6", "Artist 1", "Album 1", TimeBefore],
45  ["track7", "Track 7", "Artist 1", "Album 1", TimeBefore],
46  ["track8", "Track 8", "Artist 1", "Album 1", TimeAfter],
47 ].forEach(function (data) {
48  mainLibItems[data[0]] = {
49  track: data[1],
50  artist: data[2],
51  album: data[3],
52  lastModified: data[4]
53  };
54 });
55 
56 var mainLibLists = {};
57 [
58  ["list1", "A list", false, TimeBefore],
59  ["list2", "B list", true, TimeBefore],
60  ["list3", "C list", true, TimeAfter],
61  ["list4", "D list", false, TimeAfter],
62  ["list5", "E list", false, TimeBefore],
63 ].forEach(function (data) {
64  mainLibLists[data[0]] = {
65  name: data[1],
66  isSmart: data[2],
67  lastModified: data[3],
68  };
69 });
70 
71 var deviceItems = {};
72 [
73  // 1: Present in both, linked by origin GUID
74  ["track1", "Track 1", "Artist 1", "Album 1", "track1"],
75  // 2: Present in both, not linked by origin GUID
76  ["track2", "Track 2", "Artist 1", "Album 1", null],
77  // 3: Track 3 isn't here: it's in the main library only
78  // 4: Only present on device
79  ["track4", "Track 4", "Artist 1", "Album 1", null],
80  // 5: Present in both, but with different filenames. Not linked by origin GUID
81  ["track5b", "Track 5", "Artist 1", "Album 1", null],
82  // 6: Same filename in both, but different title, and no origin GUID. This
83  // should be considered a different track entirely.
84  ["track6", "Track 6b", "Artist 1", "Album 1", null],
85  // 7: Same filename, different title, but has an origin GUID. Should be
86  // considered the same track.
87  ["track7", "Track 7b", "Artist 1", "Album 1", "track7"],
88  // 8: Present in both, but has been modified since last sync, so should
89  // re-sync to device.
90  ["track8", "Track 8", "Artist 1", "Album 1", "track8"],
91 ].forEach(function (data) {
92  deviceItems[data[0]] = {
93  track: data[1],
94  artist: data[2],
95  album: data[3],
96  originPointsTo: mainLibItems[data[4]]
97  };
98 });
99 
100 var deviceLists = {};
101 [
102  ["list1", "A list", "list1"],
103  ["list2", "B list", "list2"],
104  ["list3", "C list", "list3"],
105  ["list4", "D list", "list4"],
106  ["list6", "F list", null],
107 ].forEach(function (data) {
108  deviceLists[data[0]] = {
109  name: data[1],
110  originPointsTo: mainLibLists[data[2]]
111  };
112 });
113 
114 const ADDED = Ci.sbIChangeOperation.ADDED;
115 const MODIFIED = Ci.sbIChangeOperation.MODIFIED;
116 
118  {change: ADDED, source: mainLibItems['track3']},
119  {change: ADDED, source: mainLibItems['track6']},
120  {change: MODIFIED, source: mainLibItems['track8'],
121  dest: deviceItems['track8']},
122 
123  {change: MODIFIED, source: mainLibLists['list3'],
124  dest: deviceLists['list3']},
125  {change: MODIFIED, source: mainLibLists['list4'],
126  dest: deviceLists['list4']},
127  {change: ADDED, source: mainLibLists['list5']},
128 ];
129 
131  {change: ADDED, source: deviceItems['track4']},
132  {change: ADDED, source: deviceItems['track6']},
133 
134  {change: MODIFIED, source: deviceLists['list1'],
135  dest: mainLibLists['list1']},
136  {change: ADDED, source: deviceLists['list6']},
137 ];
138 
140 {
141  assertEqual(actual.changes.length, expected.length);
142 
143  for (var i = 0; i < actual.changes.length; i++)
144  {
145  var change = actual.changes.queryElementAt(i, Ci.sbILibraryChange);
146 
147  assertEqual(change.operation, expected[i].change);
148 
149  assertEqual(change.sourceItem.guid, expected[i].source.item.guid);
150  if (change.operation == MODIFIED) {
151  assertEqual(change.destinationItem.guid, expected[i].dest.item.guid);
152  }
153  }
154 
155 }
156 
157 function populateLibraries(mainLib, deviceLib) {
158 
159  deviceLib.setProperty("http://songbirdnest.com/data/1.0#lastSyncTime",
161 
162  for (var itemFilename in mainLibItems) {
163  var itemData = mainLibItems[itemFilename];
164  var properties = SBProperties.createArray([
165  [SBProperties.artistName, itemData.artist],
166  [SBProperties.albumName, itemData.album],
167  [SBProperties.trackName, itemData.track]]);
168 
169  var mainItem = mainLib.createMediaItem(
170  newURI("file:///main/"+itemFilename),
171  properties);
172 
173  // Have to set this separately, or it gets overwritten
174  mainItem.setProperty(SBProperties.updated, itemData.lastModified);
175 
176  // Now store this
177  itemData.item = mainItem;
178  }
179 
180  for (var listId in mainLibLists) {
181  var listData = mainLibLists[listId];
182 
183  var type = listData.isSmart ? "smart" : "simple";
184  var item = mainLib.createMediaList(type);
185 
186  if (listData.isSmart) {
187  // We actually have to have a condition attached to the smart media list
188  // for various functions on it to work correctly. It doesn't matter what
189  // the condition(s) are.
190  var propMan =
191  Cc["@songbirdnest.com/Songbird/Properties/PropertyManager;1"]
192  .getService(Ci.sbIPropertyManager);
193  var prop = SBProperties.albumName;
194  var op = propMan.getPropertyInfo(prop).getOperator("=");
195  item.appendCondition(prop, op, "Album Foo", null, "a");
196  }
197 
198  item.name = listData.name;
199  item.setProperty(SBProperties.updated, listData.lastModified);
200 
201  listData.item = item;
202  }
203 
204  for (var itemFilename in deviceItems) {
205  var itemData = deviceItems[itemFilename];
206  var properties = SBProperties.createArray([
207  [SBProperties.artistName, itemData.artist],
208  [SBProperties.albumName, itemData.album],
209  [SBProperties.trackName, itemData.track]]);
210  if (itemData.originPointsTo) {
211  properties.appendProperty(SBProperties.originLibraryGuid, mainLib.guid);
212  properties.appendProperty(SBProperties.originItemGuid,
213  itemData.originPointsTo.item.guid);
214  }
215  var devItem = deviceLib.createMediaItem(
216  newURI("file:///device/"+itemFilename),
217  properties);
218  itemData.item = devItem;
219  }
220 
221  for (var listId in deviceLists) {
222  var listData = deviceLists[listId];
223 
224  var properties = null;
225  if (listData.originPointsTo)
226  properties = SBProperties.createArray([
227  [SBProperties.originLibraryGuid, mainLib.guid],
228  [SBProperties.originItemGuid, listData.originPointsTo.item.guid]]);
229  var item = deviceLib.createMediaList("simple", properties);
230  item.name = listData.name;
231 
232  listData.item = item;
233  }
234 
235 }
236 
237 function dumpChanges(title, changeset)
238 {
239  dump("Dumping "+title+" ("+changeset.changes.length +" changes)\n");
240 
241  for (var i = 0; i < changeset.changes.length; i++)
242  {
243  var change = changeset.changes.queryElementAt(i, Ci.sbILibraryChange);
244 
245  if (change.operation == Ci.sbIChangeOperation.ADDED) {
246  dump("Added: "+change.sourceItem.contentSrc.spec+"\n");
247  }
248  else if (change.operation == Ci.sbIChangeOperation.MODIFIED) {
249  dump("Clobber: "+change.sourceItem.contentSrc.spec+
250  " --> "+change.destinationItem.contentSrc.spec+"\n");
251  }
252  else {
253  dump("INTERNAL ERROR\n");
254  }
255 
256  }
257 }
258 
259 function runTest () {
260  var sync = Cc["@songbirdnest.com/Songbird/Device/DeviceLibrarySyncDiff;1"]
261  .createInstance(Ci.sbIDeviceLibrarySyncDiff);
262 
263  // These are not the actual device/main libraries, just named so because in
264  // this test they work as if they were.
265  var mainLib = createLibrary("test_sync_main", null, false);
266  var deviceLib = createLibrary("test_sync_device", null, false);
267 
268  mainLib.clear();
269  deviceLib.clear();
270 
271  populateLibraries(mainLib, deviceLib);
272 
273  var exportChanges = {};
274  var importChanges = {};
275  sync.generateSyncLists(Ci.sbIDeviceLibrarySyncDiff.SYNC_TYPE_AUDIO,
276  Ci.sbIDeviceLibrarySyncDiff.SYNC_TYPE_AUDIO,
277  mainLib,
278  deviceLib,
279  null,
280  exportChanges,
281  importChanges);
282 
283  //dumpChanges("Export Changes", exportChanges.value);
284  //dumpChanges("Import Changes", importChanges.value);
285 
286  verifyChanges(exportChanges.value, expectedExportChanges);
287  verifyChanges(importChanges.value, expectedImportChanges);
288 }
289 
var deviceItems
const Cc
var deviceLists
var expectedImportChanges
var mainLibLists
const TimeAfter
function dumpChanges(title, changeset)
foldersync sync
Definition: sync.js:14
const MODIFIED
function assertEqual(aExpected, aActual, aMessage)
var expectedExportChanges
var mainLibItems
var getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('Songbird SBProperties artist
Definition: tuner2.js:40
const TimeLastSynced
const TimeBefore
return null
Definition: FeedWriter.js:1143
function createLibrary(databaseGuid, databaseLocation)
Definition: test_load.js:151
function newURI(aURLString)
var expected
function populateLibraries(mainLib, deviceLib)
const ADDED
const Ci
observe data
Definition: FeedWriter.js:1329
var actual
_getSelectedPageStyle s i
function verifyChanges(actual, expected)
function runTest()
Advanced DataRemote unit tests.