Fix null-access errors in "addTo/removeFrom*PbxGroup" methods
The project exposes a few methods to add/remove children from PbxGroups
* `addToPluginsPbxGroup` / `removeFromPluginsPbxGroup`
* `addToResourcesPbxGroup` / `removeFromResourcesPbxGroup`
* `addToFrameworksPbxGroup` / `removeFromFrameworksPbxGroup`
* `addToProductsPbxGroup` / `removeFromProductsPbxGroup`
which are used internally in:
* `addPluginFile` / `removePluginFile`
* `addResourceFile` / `removeResourceFile`
* `addProductFile` / `removeProductFile`
* `addFramework` / `removeFramework`
But neither of these methods do a check whether the `PbxGroup` actually
exists before trying to modify it - this can result in a possible
null-access error.
I ran into this problem when trying to add a framework to a project
which didn't have a `Frameworks` `PbxGroup`.
This commit adds checks to these methods to test for existence of the
`PbxGroup`s and creates them if they don't exist or does an early exit
in the case of removal.
This closes #3
diff --git a/lib/pbxProject.js b/lib/pbxProject.js
index e2e88aa..4243f65 100644
--- a/lib/pbxProject.js
+++ b/lib/pbxProject.js
@@ -608,10 +608,17 @@
pbxProject.prototype.addToPluginsPbxGroup = function(file) {
var pluginsGroup = this.pbxGroupByName('Plugins');
- pluginsGroup.children.push(pbxGroupChild(file));
+ if (!pluginsGroup) {
+ this.addPbxGroup([file.path], 'Plugins');
+ } else {
+ pluginsGroup.children.push(pbxGroupChild(file));
+ }
}
pbxProject.prototype.removeFromPluginsPbxGroup = function(file) {
+ if (!this.pbxGroupByName('Plugins')) {
+ return null;
+ }
var pluginsGroupChildren = this.pbxGroupByName('Plugins').children, i;
for (i in pluginsGroupChildren) {
if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
@@ -624,10 +631,17 @@
pbxProject.prototype.addToResourcesPbxGroup = function(file) {
var pluginsGroup = this.pbxGroupByName('Resources');
- pluginsGroup.children.push(pbxGroupChild(file));
+ if (!pluginsGroup) {
+ this.addPbxGroup([file.path], 'Resources');
+ } else {
+ pluginsGroup.children.push(pbxGroupChild(file));
+ }
}
pbxProject.prototype.removeFromResourcesPbxGroup = function(file) {
+ if (!this.pbxGroupByName('Resources')) {
+ return null;
+ }
var pluginsGroupChildren = this.pbxGroupByName('Resources').children, i;
for (i in pluginsGroupChildren) {
if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
@@ -640,7 +654,11 @@
pbxProject.prototype.addToFrameworksPbxGroup = function(file) {
var pluginsGroup = this.pbxGroupByName('Frameworks');
- pluginsGroup.children.push(pbxGroupChild(file));
+ if (!pluginsGroup) {
+ this.addPbxGroup([file.path], 'Frameworks');
+ } else {
+ pluginsGroup.children.push(pbxGroupChild(file));
+ }
}
pbxProject.prototype.removeFromFrameworksPbxGroup = function(file) {
@@ -680,10 +698,17 @@
pbxProject.prototype.addToProductsPbxGroup = function(file) {
var productsGroup = this.pbxGroupByName('Products');
- productsGroup.children.push(pbxGroupChild(file));
+ if (!productsGroup) {
+ this.addPbxGroup([file.path], 'Products');
+ } else {
+ productsGroup.children.push(pbxGroupChild(file));
+ }
}
pbxProject.prototype.removeFromProductsPbxGroup = function(file) {
+ if (!this.pbxGroupByName('Products')) {
+ return null;
+ }
var productsGroupChildren = this.pbxGroupByName('Products').children, i;
for (i in productsGroupChildren) {
if (pbxGroupChild(file).value == productsGroupChildren[i].value &&
diff --git a/test/group.js b/test/group.js
index b907c7e..b23d18e 100644
--- a/test/group.js
+++ b/test/group.js
@@ -1,4 +1,5 @@
var pbx = require('../lib/pbxProject'),
+ pbxFile = require('../lib/pbxFile'),
project,
projectHash;
@@ -137,6 +138,85 @@
}
}
+exports.predefinedPbxGroups = {
+ setUp: function(callback) {
+ project = new pbx('test/parser/projects/empty-groups.pbxproj').parseSync();
+
+ this.file = new pbxFile('some-file.m');
+ this.file.fileRef = project.generateUuid();
+ project.addToPbxFileReferenceSection(this.file);
+
+ callback();
+ },
+
+ 'should add a file to "Plugins" group': function(test) {
+ project.addToPluginsPbxGroup(this.file);
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Plugins'), this.file.fileRef);
+ test.ok(foundInGroup);
+ test.done();
+ },
+
+ 'should remove a file from "Plugins" group': function(test) {
+ project.addToPluginsPbxGroup(this.file);
+ project.removeFromPluginsPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Plugins'), this.file.fileRef);
+ test.ok(!foundInGroup);
+ test.done();
+ },
+
+ 'should add a file to "Resources" group': function(test) {
+ project.addToResourcesPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Resources'), this.file.fileRef);
+ test.ok(foundInGroup);
+ test.done();
+ },
+
+ 'should remove a file from "Resources" group': function(test) {
+ project.addToResourcesPbxGroup(this.file);
+ project.removeFromResourcesPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Resources'), this.file.fileRef);
+ test.ok(!foundInGroup);
+ test.done();
+ },
+
+ 'should add a file to "Frameworks" group': function(test) {
+ project.addToFrameworksPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Frameworks'), this.file.fileRef);
+ test.ok(foundInGroup);
+ test.done();
+ },
+
+ 'should remove a file from "Frameworks" group': function(test) {
+ project.addToFrameworksPbxGroup(this.file);
+ project.removeFromFrameworksPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Frameworks'), this.file.fileRef);
+ test.ok(!foundInGroup);
+ test.done();
+ },
+
+ 'should add a file to "Products" group': function(test) {
+ project.addToProductsPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Products'), this.file.fileRef);
+ test.ok(foundInGroup);
+ test.done();
+ },
+
+ 'should remove a file from "Products" group': function(test) {
+ project.addToProductsPbxGroup(this.file);
+ project.removeFromProductsPbxGroup(this.file);
+
+ var foundInGroup = findChildInGroup(project.pbxGroupByName('Products'), this.file.fileRef);
+ test.ok(!foundInGroup);
+ test.done();
+ }
+};
+
exports.addSourceFileToGroup = {
'should create group + add source file' : function(test) {
var testKey = project.pbxCreateGroup('Test', 'Test');
diff --git a/test/parser/projects/empty-groups.pbxproj b/test/parser/projects/empty-groups.pbxproj
new file mode 100644
index 0000000..6322052
--- /dev/null
+++ b/test/parser/projects/empty-groups.pbxproj
@@ -0,0 +1,16 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+/* Begin PBXFileReference section */
+/* End PBXFileReference section */
+/* Begin PBXBuildFile section */
+/* End PBXBuildFile section */
+/* Begin PBXGroup section */
+/* End PBXGroup section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}