Merge branch 'add-pbx-group' of https://github.com/Mitko-Kerezov/node-xcode into Mitko-Kerezov-add-pbx-group
diff --git a/lib/pbxProject.js b/lib/pbxProject.js
index 0df7979..9f7c026 100644
--- a/lib/pbxProject.js
+++ b/lib/pbxProject.js
@@ -274,6 +274,57 @@
     delete this.pbxBuildFileSection()[commentKey];
 }
 
+pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceTree) {
+    var groups = this.hash.project.objects['PBXGroup'],
+        pbxGroupUuid = this.generateUuid(),
+        commentKey = f("%s_comment", pbxGroupUuid),
+        pbxGroup = {
+            isa: 'PBXGroup',
+            children: [],
+            name: name,
+            path: path,
+            sourceTree: sourceTree ? sourceTree : '"<group>"'
+        },
+        fileReferenceSection = this.pbxFileReferenceSection(),
+        filePathToReference = {};
+        
+    for (var key in fileReferenceSection) {
+        // only look for comments
+        if (!COMMENT_KEY.test(key)) continue;
+        
+        var fileReferenceKey = key.split(COMMENT_KEY)[0],
+            fileReference = fileReferenceSection[fileReferenceKey];
+        
+        filePathToReference[fileReference.path] = {fileRef: fileReferenceKey, basename: fileReferenceSection[key]};
+    }
+
+    for (var index = 0; index < filePathsArray.length; index++) {
+        var filePath = filePathsArray[index],
+            filePathQuoted = "\"" + filePath + "\"";
+        if (filePathToReference[filePath]) {
+            pbxGroup.children.push(pbxGroupChild(filePathToReference[filePath]));
+            continue;
+        } else if (filePathToReference[filePathQuoted]) {
+            pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted]));
+            continue;
+        }
+        
+        var file = new pbxFile(filePath);
+        file.uuid = this.generateUuid();
+        file.fileRef = this.generateUuid();
+        this.addToPbxFileReferenceSection(file);    // PBXFileReference
+        this.addToPbxBuildFileSection(file);        // PBXBuildFile
+        pbxGroup.children.push(pbxGroupChild(file));
+    }
+    
+    if (groups) {
+        groups[pbxGroupUuid] = pbxGroup;
+        groups[commentKey] = name;
+    }
+    
+    return {uuid: pbxGroupUuid, pbxGroup: pbxGroup};
+}
+
 pbxProject.prototype.addToPbxFileReferenceSection = function (file) {
     var commentKey = f("%s_comment", file.fileRef);
 
diff --git a/test/addPbxGroup.js b/test/addPbxGroup.js
new file mode 100644
index 0000000..433d30e
--- /dev/null
+++ b/test/addPbxGroup.js
@@ -0,0 +1,149 @@
+var fullProject = require('./fixtures/full-project')
+    fullProjectStr = JSON.stringify(fullProject),
+    pbx = require('../lib/pbxProject'),
+    proj = new pbx('.');
+
+function cleanHash() {
+    return JSON.parse(fullProjectStr);
+}
+
+exports.setUp = function (callback) {
+    proj.hash = cleanHash();
+    callback();
+}
+
+exports.addPbxGroup = {
+    'should return a pbxGroup': function (test) {
+        var pbxGroup = proj.addPbxGroup(['file.m'], 'MyGroup', 'Application', 'Application', '"<group>"');
+        
+        test.ok(typeof pbxGroup === 'object');
+        test.done()
+    },
+    'should set a uuid on the pbxGroup': function (test) {
+        var pbxGroup = proj.addPbxGroup(['file.m'], 'MyGroup', 'Application', 'Application', '"<group>"');
+
+        test.ok(pbxGroup.uuid);
+        test.done()
+    },
+    'should add all files to pbxGroup': function (test) {
+        var pbxGroup = proj.addPbxGroup(['file.m'], 'MyGroup', 'Application', 'Application', '"<group>"');
+        for (var index = 0; index < pbxGroup.pbxGroup.children.length; index++) {
+            var file = pbxGroup.pbxGroup.children[index];
+            test.ok(file.value);
+        }
+        
+        test.done()
+    },
+    'should add the PBXGroup object correctly': function (test) {
+        var pbxGroup = proj.addPbxGroup(['file.m'], 'MyGroup', 'Application', '"<group>"');
+            pbxGroupInPbx = proj.pbxGroupByName('MyGroup');
+
+        test.equal(pbxGroupInPbx.children, pbxGroup.pbxGroup.children);
+        test.equal(pbxGroupInPbx.isa, 'PBXGroup');
+        test.equal(pbxGroupInPbx.path, 'Application');
+        test.equal(pbxGroupInPbx.sourceTree, '"<group>"');
+        test.done();
+    },
+    'should add <group> sourceTree if no other specified': function (test) {
+        var pbxGroup = proj.addPbxGroup(['file.m'], 'MyGroup', 'Application');
+            pbxGroupInPbx = proj.pbxGroupByName('MyGroup');
+
+        test.equal(pbxGroupInPbx.sourceTree, '"<group>"');
+        test.done();
+    },
+    'should add each of the files to PBXBuildFile section': function (test) {
+        var buildFileSection = proj.pbxBuildFileSection();
+        for (var key in buildFileSection) {
+            test.notEqual(buildFileSection[key].fileRef_comment, 'file.m');
+            test.notEqual(buildFileSection[key].fileRef_comment, 'assets.bundle');
+        }
+        
+        var initialBuildFileSectionItemsCount = Object.keys(buildFileSection),
+            pbxGroup = proj.addPbxGroup(['file.m', 'assets.bundle'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(buildFileSection);
+
+        // for each file added in the build file section two keyes are added - one for the object and one for the comment
+        test.equal(initialBuildFileSectionItemsCount.length, afterAdditionBuildFileSectionItemsCount.length - 4);
+        test.done();
+    },
+    'should not add any of the files to PBXBuildFile section if already added': function (test) {
+        var buildFileSection = proj.pbxBuildFileSection(),
+            initialBuildFileSectionItemsCount = Object.keys(buildFileSection),
+            pbxGroup = proj.addPbxGroup(['AppDelegate.m', 'AppDelegate.h'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(buildFileSection);
+            
+        test.deepEqual(initialBuildFileSectionItemsCount, afterAdditionBuildFileSectionItemsCount);
+        test.done();
+    },
+    'should not add any of the files to PBXBuildFile section when they contain special symbols and are already added': function (test) {
+        var buildFileSection = proj.pbxBuildFileSection(),
+            initialBuildFileSectionItemsCount = Object.keys(buildFileSection),
+            pbxGroup = proj.addPbxGroup(['KitchenSinktablet.app'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(buildFileSection);
+            
+        test.deepEqual(initialBuildFileSectionItemsCount, afterAdditionBuildFileSectionItemsCount);
+        test.done();
+    },
+    'should add all files which are not added and not add files already added to PBXBuildFile section': function (test) {
+        var buildFileSection = proj.pbxBuildFileSection();
+        for (var key in buildFileSection) {
+            test.notEqual(buildFileSection[key].fileRef_comment, 'file.m');
+            test.notEqual(buildFileSection[key].fileRef_comment, 'assets.bundle');
+        }
+        
+        var initialBuildFileSectionItemsCount = Object.keys(buildFileSection),
+            pbxGroup = proj.addPbxGroup(['AppDelegate.m', 'AppDelegate.h', 'file.m', 'assets.bundle'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(buildFileSection);
+        
+        // for each file added in the build file section two keyes are added - one for the object and one for the comment
+        test.equal(initialBuildFileSectionItemsCount.length, afterAdditionBuildFileSectionItemsCount.length - 4);
+        test.done();
+    },
+    'should add each of the files to PBXFileReference section': function (test) {
+        var fileReference = proj.pbxFileReferenceSection();
+        for (var key in fileReference) {
+            test.notEqual(fileReference[key].fileRef_comment, 'file.m');
+            test.notEqual(fileReference[key].fileRef_comment, 'assets.bundle');
+        }
+        var pbxGroup = proj.addPbxGroup(['file.m', 'assets.bundle'], 'MyGroup', 'Application', '"<group>"');
+        for (var index = 0; index < pbxGroup.pbxGroup.children.length; index++) {
+            var file = pbxGroup.pbxGroup.children[index];
+            test.ok(fileReference[file.value]);
+        }  
+
+        test.done();
+    },
+    'should not add any of the files to PBXFileReference section if already added': function (test) {
+        var fileReference = proj.pbxFileReferenceSection (),
+            initialBuildFileSectionItemsCount = Object.keys(fileReference),
+            pbxGroup = proj.addPbxGroup(['AppDelegate.m', 'AppDelegate.h'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(fileReference);
+            
+        test.deepEqual(initialBuildFileSectionItemsCount, afterAdditionBuildFileSectionItemsCount);
+        test.done();
+    },
+    'should not add any of the files to PBXFileReference section when they contain special symbols and are already added': function (test) {
+        var fileReference = proj.pbxFileReferenceSection (),
+            initialBuildFileSectionItemsCount = Object.keys(fileReference),
+            pbxGroup = proj.addPbxGroup(['KitchenSinktablet.app'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(fileReference);
+            
+        test.deepEqual(initialBuildFileSectionItemsCount, afterAdditionBuildFileSectionItemsCount);
+        test.done();
+    },
+    'should add all files which are not added and not add files already added to PBXFileReference section': function (test) {
+        var fileReference = proj.pbxFileReferenceSection ();
+        for (var key in fileReference) {
+            test.notEqual(fileReference[key].fileRef_comment, 'file.m');
+            test.notEqual(fileReference[key].fileRef_comment, 'assets.bundle');
+        }
+        
+        var initialBuildFileSectionItemsCount = Object.keys(fileReference),
+            pbxGroup = proj.addPbxGroup(['AppDelegate.m', 'AppDelegate.h', 'file.m', 'assets.bundle'], 'MyGroup', 'Application', '"<group>"'),
+            afterAdditionBuildFileSectionItemsCount = Object.keys(fileReference);
+        
+        // for each file added in the file reference section two keyes are added - one for the object and one for the comment
+        test.equal(initialBuildFileSectionItemsCount.length, afterAdditionBuildFileSectionItemsCount.length - 4);
+        test.done();
+    }
+}