// ========================================================================== // Project: SproutCore - JavaScript Application Framework // Copyright: ©2006-2011 Strobe Inc. and contributors. // ©2008-2011 Apple Inc. All rights reserved. // License: Licensed under MIT license (see license.js) // ========================================================================== // ======================================================================== // SC.Set Tests // ======================================================================== /*globals module test ok isObj equals expects */

var a, b, c ; // global variables

module(“creating SC.Set instances”, {

setup: function() {
  // create objects...
  a = { name: "a" } ;
  b = { name: "b" } ;
  c = { name: "c" } ;
},

teardown: function() {
  a = undefined ;
  b = undefined ;
  c = undefined ;
}

});

test(“SC.Set.create() should create empty set”, function() {

var set = SC.Set.create() ;
equals(set.length, 0) ;

});

test(“SC.Set.getEach() should work”, function () {

var set = SC.Set.create([a,b,c]),
  names = set.getEach('name');

ok(names.contains("a"), "Set.getEach array should contain 'a'");
ok(names.contains("b"), "Set.getEach array should contain 'b'");
ok(names.contains("c"), "Set.getEach array should contain 'c'");

});

test(“SC.Set.create() should create set with three items in them”, function() {

var set = SC.Set.create([a,b,c]) ;
equals(set.length, 3) ;
equals(set.contains(a), YES) ;
equals(set.contains(b), YES) ;
equals(set.contains(c), YES) ;

});

test(“SC.Set.create() should accept anything that implements SC.Array”, function() {

var arrayLikeObject = SC.Object.create(SC.Array, {
  _content: [a,b,c],
  length: 3,
  objectAt: function(idx) { return this._content[idx]; }
}) ;

var set = SC.Set.create(arrayLikeObject) ;
equals(set.length, 3) ;
equals(set.contains(a), YES) ;
equals(set.contains(b), YES) ;
equals(set.contains(c), YES) ;

});

var set ; // global variables

// The tests below also end up testing the contains() method pretty // exhaustively. module(“SC.Set.add + SC.Set.contains”, {

setup: function() {
  set = SC.Set.create() ;
},

teardown: function() {
  set = undefined ;
}

});

test(“should add an SC.Object”, function() {

var obj = SC.Object.create() ;

var oldLength = set.length ;
set.add(obj) ;
equals(set.contains(obj), YES, "contains()") ;
equals(set.length, oldLength+1, "new set length") ;

});

test(“should add a regular hash”, function() {

var obj = {} ;

var oldLength = set.length ;
set.add(obj) ;
equals(set.contains(obj), YES, "contains()") ;
equals(set.length, oldLength+1, "new set length") ;

});

test(“should add a string”, function() {

var obj = "String!" ;

var oldLength = set.length ;
set.add(obj) ;
equals(set.contains(obj), YES, "contains()") ;
equals(set.length, oldLength+1, "new set length") ;

});

test(“should add a number”, function() {

var obj = 23 ;

var oldLength = set.length ;
set.add(obj) ;
equals(set.contains(obj), YES, "contains()") ;
equals(set.length, oldLength+1, "new set length") ;

});

test(“should add bools”, function() {

var oldLength = set.length ;

set.add(true) ;
equals(set.contains(true), YES, "contains(true)");
equals(set.length, oldLength+1, "new set length");

set.add(false);
equals(set.contains(false), YES, "contains(false)");
equals(set.length, oldLength+2, "new set length");

});

test(“should add 0”, function() {

var oldLength = set.length ;

set.add(0) ;
equals(set.contains(0), YES, "contains(0)");
equals(set.length, oldLength+1, "new set length");

});

test(“should add a function”, function() {

var obj = function() { return "Test function"; } ;

var oldLength = set.length ;
set.add(obj) ;
equals(set.contains(obj), YES, "contains()") ;
equals(set.length, oldLength+1, "new set length") ;

});

test(“should NOT add a null”, function() {

set.add(null) ;
equals(set.length, 0) ;
equals(set.contains(null), NO) ;

});

test(“should NOT add an undefined”, function() {

set.add(undefined) ;
equals(set.length, 0) ;
equals(set.contains(undefined), NO) ;

});

test(“adding an item, removing it, adding another item”, function() {

var item1 = "item1" ;
var item2 = "item2" ;

set.add(item1) ; // add to set
set.remove(item1) ; //remove from set
set.add(item2) ;

equals(set.contains(item1), NO, "set.contains(item1)") ;

set.add(item1) ; // re-add to set
equals(set.length, 2, "set.length") ;

});

/**

This test illustrates a problem with SC.Set.  It stored references to objects
at increasing indexes and removed references to the objects by ignoring the
index and overwriting it with a new object if it comes along.  However, if
a lot of objects are added very quickly, they will be retained indefinitely
even after remove is called, until the same number of new objects are added
later.

*/ test(“adding and removing items should not retain references to removed objects”, function() {

var guid1, guid2,
  idx1, idx2,
  item1 = "item1",
  item2 = "item2";

guid1 = SC.guidFor(item1);
guid2 = SC.guidFor(item2);

// add to set
set.add(item1);
set.add(item2);

idx1 = set[guid1];
idx2 = set[guid2];

equals(set.length, 2, "set.length");
equals(set[idx1], item1, "item1 is at index %@ on the set".fmt(idx1));
equals(set[idx2], item2, "item2 is at index %@ on the set".fmt(idx2));
equals(set[guid1], 0, "guid for item1, %@, references index %@ on the set".fmt(guid1, idx1));
equals(set[guid2], 1, "guid for item2, %@, references index %@ on the set".fmt(guid2, idx2));

// remove from set
set.remove(item1);
set.remove(item2);

equals(set.length, 0, "set.length");
equals(set[idx1], undefined, "item1 is no longer at index %@ on the set".fmt(idx1));
equals(set[idx2], undefined, "item2 is no longer at index %@ on the set".fmt(idx2));
equals(set[guid1], undefined, "guid for item1, %@, is no longer on the set".fmt(guid1));
equals(set[guid2], undefined, "guid for item2, %@, is no longer on the set".fmt(guid2));

// add to set
set.add(item1);
set.add(item2);

idx1 = set[guid1];
idx2 = set[guid2];

equals(set.length, 2, "set.length");
equals(set[idx1], item1, "item1 is at index %@ on the set".fmt(idx1));
equals(set[idx2], item2, "item2 is at index %@ on the set".fmt(idx2));
equals(set[guid1], 0, "guid for item1, %@, references index %@ on the set".fmt(guid1, idx1));
equals(set[guid2], 1, "guid for item2, %@, references index %@ on the set".fmt(guid2, idx2));

// remove from set in reverse order
set.remove(item2);
set.remove(item1);

equals(set.length, 0, "set.length");
equals(set[idx1], undefined, "item1 is no longer at index %@ on the set".fmt(idx1));
equals(set[idx2], undefined, "item2 is no longer at index %@ on the set".fmt(idx2));
equals(set[guid1], undefined, "guid for item1, %@, is no longer on the set".fmt(guid1));
equals(set[guid2], undefined, "guid for item2, %@, is no longer on the set".fmt(guid2));

});

module(“SC.Set.remove + SC.Set.contains”, {

// generate a set with every type of object, but none of the specific
// ones we add in the tests below...
setup: function() {
  set = SC.Set.create([
    SC.Object.create({ dummy: YES }),
    { isHash: YES },
    "Not the String",
    16, true, false, 0]) ;
},

teardown: function() {
  set = undefined ;
}

});

test(“should remove an SC.Object and reduce length”, function() {

var obj = SC.Object.create() ;
set.add(obj) ;
equals(set.contains(obj), YES) ;
var oldLength = set.length ;

set.remove(obj) ;
equals(set.contains(obj), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

});

test(“should remove a regular hash and reduce length”, function() {

var obj = {} ;
set.add(obj) ;
equals(set.contains(obj), YES) ;
var oldLength = set.length ;

set.remove(obj) ;
equals(set.contains(obj), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

});

test(“should remove a string and reduce length”, function() {

var obj = "String!" ;
set.add(obj) ;
equals(set.contains(obj), YES) ;
var oldLength = set.length ;

set.remove(obj) ;
equals(set.contains(obj), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

});

test(“should remove a number and reduce length”, function() {

var obj = 23 ;
set.add(obj) ;
equals(set.contains(obj), YES) ;
var oldLength = set.length ;

set.remove(obj) ;
equals(set.contains(obj), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

});

test(“should remove a bools and reduce length”, function() {

var oldLength = set.length ;
set.remove(true) ;
equals(set.contains(true), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

set.remove(false);
equals(set.contains(false), NO, "should be removed") ;
equals(set.length, oldLength-2, "should be 2 shorter") ;

});

test(“should remove 0 and reduce length”, function(){

var oldLength = set.length;
set.remove(0) ;
equals(set.contains(0), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

});

test(“should remove a function and reduce length”, function() {

var obj = function() { return "Test function"; } ;
set.add(obj) ;
equals(set.contains(obj), YES) ;
var oldLength = set.length ;

set.remove(obj) ;
equals(set.contains(obj), NO, "should be removed") ;
equals(set.length, oldLength-1, "should be 1 shorter") ;

});

test(“should NOT remove a null”, function() {

var oldLength = set.length ;
set.remove(null) ;
equals(set.length, oldLength) ;

});

test(“should NOT remove an undefined”, function() {

var oldLength = set.length ;
set.remove(undefined) ;
equals(set.length, oldLength) ;

});

test(“should ignore removing an object not in the set”, function() {

var obj = SC.Object.create() ;
var oldLength = set.length ;
set.remove(obj) ;
equals(set.length, oldLength) ;

});

// test(“should remove all the elements in the set”, function() { // var obj = [2,3,4]; // set.add(obj) ; // var oldLength = set.length ; // equals(oldLength, 6); // a = set.removeEach(obj); // equals(a.length, 0); // });

module(“SC.Set.pop + SC.Set.clone”, { // generate a set with every type of object, but none of the specific // ones we add in the tests below…

setup: function() {
        set = SC.Set.create([
                SC.Object.create({ dummy: YES }),
                { isHash: YES },
                "Not the String",
                16, false]) ;
        },

        teardown: function() {
                set = undefined ;
        }

});

test(“the pop() should remove an arbitrary object from the set”, function() {

var oldLength = set.length ;
var obj = set.pop();
ok(!SC.none(obj), 'pops up an item');
equals(set.length, oldLength-1, 'length shorter by 1');

});

test(“should pop false and 0”, function(){

set = SC.Set.create([false]);
ok(set.pop() === false, "should pop false");

set = SC.Set.create([0]);
ok(set.pop() === 0, "should pop 0");

});

test(“the clone() should return an indentical set”, function() {

var oldLength = set.length ;
var obj = set.clone();
equals(oldLength,obj.length,'length of the clone should be same');
equals(obj.contains(set[0]), YES);
equals(obj.contains(set[1]), YES);
equals(obj.contains(set[2]), YES);
equals(obj.contains(set[3]), YES);
equals(obj.contains(set[4]), YES);

});