aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax <post@wickenrode.com>2017-02-12 20:49:31 +0100
committerMax <post@wickenrode.com>2017-02-12 20:49:31 +0100
commitd2b1a5b84cb295eba8617f7e80681e0eeca46f0d (patch)
tree82dd73fb5c23849be2aa8c5df3695293410229e7
parentaee6a009a5355aa3c7c91f885685868e5294e71c (diff)
downloadsequelpro-d2b1a5b84cb295eba8617f7e80681e0eeca46f0d.tar.gz
sequelpro-d2b1a5b84cb295eba8617f7e80681e0eeca46f0d.tar.bz2
sequelpro-d2b1a5b84cb295eba8617f7e80681e0eeca46f0d.zip
Add unit tests and fix a bug in JSON formatter
-rw-r--r--Source/SPJSONFormatter.m8
-rw-r--r--UnitTests/SPJSONFormatterTests.m141
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj6
3 files changed, 151 insertions, 4 deletions
diff --git a/Source/SPJSONFormatter.m b/Source/SPJSONFormatter.m
index 05cc2992..258845c9 100644
--- a/Source/SPJSONFormatter.m
+++ b/Source/SPJSONFormatter.m
@@ -62,11 +62,11 @@ static char GetNextANSIChar(SPJSONTokenizerState *stateInfo);
return nil;
}
- if(curToken.tok == JSON_TOK_SQUARE_BRACE_CLOSE || curToken.tok == JSON_TOK_CURLY_BRACE_CLOSE)
+ if(idLevel > 0 && (curToken.tok == JSON_TOK_SQUARE_BRACE_CLOSE || curToken.tok == JSON_TOK_CURLY_BRACE_CLOSE))
idLevel--;
- //if this token is a "]" or "}" and there was no "[" or "{" directly before it, add a linebreak before
- if(prevTokenType != JSON_TOK_CURLY_BRACE_OPEN && prevTokenType != JSON_TOK_SQUARE_BRACE_OPEN && (curToken.tok == JSON_TOK_SQUARE_BRACE_CLOSE || curToken.tok == JSON_TOK_CURLY_BRACE_CLOSE)) {
+ //if this token is a "]" or "}" and there was no ",", "[" or "{" directly before it, add a linebreak before
+ if(prevTokenType != JSON_TOK_CURLY_BRACE_OPEN && prevTokenType != JSON_TOK_SQUARE_BRACE_OPEN && prevTokenType != JSON_TOK_COMMA && (curToken.tok == JSON_TOK_SQUARE_BRACE_CLOSE || curToken.tok == JSON_TOK_CURLY_BRACE_CLOSE)) {
[formatted appendString:@"\n"];
needIndent = YES;
}
@@ -255,7 +255,7 @@ int SPJSONTokenizerInit(NSString *input, SPJSONTokenizerState *stateInfo) {
stateInfo->ctxt = JSON_ROOT_CONTEXT;
stateInfo->pos = 0;
stateInfo->str = [input UTF8String];
- stateInfo->len = strlen(stateInfo->str); //we deem -[NSString UTF8String] to be a safe source
+ stateInfo->len = [input lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
return 0;
}
diff --git a/UnitTests/SPJSONFormatterTests.m b/UnitTests/SPJSONFormatterTests.m
new file mode 100644
index 00000000..658169a4
--- /dev/null
+++ b/UnitTests/SPJSONFormatterTests.m
@@ -0,0 +1,141 @@
+//
+// SPJSONFormatterTests.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 12.02.17.
+// Copyright (c) 2017 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPJSONFormatter.h"
+#import <Cocoa/Cocoa.h>
+#import <XCTest/XCTest.h>
+
+@interface SPJSONFormatterTests : XCTestCase
+
+- (void)testFormatting;
+- (void)testUnformatting;
+
+@end
+
+@implementation SPJSONFormatterTests
+
+- (void)testFormatting
+{
+
+ //invalid input
+ XCTAssertNil([SPJSONFormatter stringByFormattingString:nil],@"nil output on nil input");
+
+ //empty string
+ XCTAssertEqualObjects([SPJSONFormatter stringByFormattingString:@""], @"", @"empty string stays empty");
+
+ //scalars on their own should not get changed
+ {
+ NSArray *scalars = @[@"true",@"false",@"null",@"123.45",@"1.4e-5",@"\"string\""];
+ for (NSString *scalar in scalars) {
+ XCTAssertEqualObjects([SPJSONFormatter stringByFormattingString:scalar], scalar, @"scalar only input stays as is");
+ }
+ }
+
+ //simple test involving all types
+ {
+ NSString *unf = @"{\"key\": null, \"foo\": [true, false], \"ba\\\"r\": [{},{\"key2\": -1.98}]}";
+ NSString *fmt = @"{\n\t\"key\": null,\n\t\"foo\": [\n\t\ttrue,\n\t\tfalse\n\t],\n\t\"ba\\\"r\": [\n\t\t{},\n\t\t{\n\t\t\t\"key2\": -1.98\n\t\t}\n\t]\n}";
+
+ XCTAssertEqualObjects([SPJSONFormatter stringByFormattingString:unf], fmt, @"simple formatting test");
+ }
+
+ //other tests
+ {
+ NSArray *tests = @[
+ @[@"{\"key\": \"v\0al\"}",@"{\n\t\"key\": \"v\0al\"\n}", @"NUL in input (invalid JSON)"],
+ @[@"[\"\",\"\"\",\"", @"[\n\t\"\",\n\t\"\"\",\"", @"series of dquotes (invalid JSON)"],
+ @[@"{[{\"ab\\u0090c\",",@"{\n\t[\n\t\t{\n\t\t\t\"ab\\u0090c\",\n",@"unterminated elements (invalid JSON)"],
+ @[@"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[null]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]",@"[\n\t[\n\t\t[\n\t\t\t[\n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n]",@"34 levels of indent"],
+ @[@"{\"a\":\"bcd}",@"{\n\t\"a\": \"bcd}",@"unterminated string (invalid JSON)"],
+ @[@"[1,\"ab\ncd\",3]",@"[\n\t1,\n\t\"ab\ncd\",\n\t3\n]",@"multiline string (invalid JSON)"],
+ @[@"{}}},false]",@"{}\n}\n},\nfalse\n]",@"closing something that is not open (invalid JSON)"],
+ @[@"[[123e4}}",@"[\n\t[\n\t\t123e4\n\t}\n}",@"unmatched braces (invalid JSON)"],
+ @[@"[{]}",@"[\n\t{\n\t]\n}",@"unmatched braces 2 (invalid JSON)"],
+ @[@"[ true , \n false \t ] \t \n",@"[\n\ttrue,\n\tfalse\n]",@"whitespace reformatting"],
+ @[@"[1,2,]",@"[\n\t1,\n\t2,\n]",@"trailing comma (valid for some parsers)"],
+ @[@"{}/{|}],-\"}[|[{}\\\"]:{~]\",,}{]\{|::[\\|\"],};]*}]",@"{}/{\n\t|\n}\n],\n-\"}[|[{}\\\"]:{~]\",\n,\n}{\n]{\n\t|: : [\n\t\t\\|\"],};]*}]",@"random garbage"],
+ ];
+
+ for (NSArray *pair in tests) {
+ XCTAssertEqualObjects([SPJSONFormatter stringByFormattingString:[pair objectAtIndex:0]], [pair objectAtIndex:1], @"%@", [pair objectAtIndex:2]);
+ }
+ }
+}
+
+- (void)testUnformatting
+{
+ //invalid input
+ XCTAssertNil([SPJSONFormatter stringByUnformattingString:nil],@"nil output on nil input");
+
+ //empty string
+ XCTAssertEqualObjects([SPJSONFormatter stringByUnformattingString:@""], @"", @"empty string stays empty");
+
+ //scalars on their own should not get changed
+ {
+ NSArray *scalars = @[@"true",@"false",@"null",@"123.45",@"1.4e-5",@"\"string\""];
+ for (NSString *scalar in scalars) {
+ XCTAssertEqualObjects([SPJSONFormatter stringByUnformattingString:scalar], scalar, @"scalar only input stays as is");
+ }
+ }
+
+ //simple test involving all types
+ {
+ NSString *unf = @"{\"key\": null, \"foo\": [true, false], \"ba\\\"r\": [{}, {\"key2\": -1.98}]}";
+ NSString *fmt = @"{\n\t\"key\": null,\n\t\"foo\": [\n\t\ttrue,\n\t\tfalse\n\t],\n\t\"ba\\\"r\": [\n\t\t{},\n\t\t{\n\t\t\t\"key2\": -1.98\n\t\t}\n\t]\n}";
+
+ XCTAssertEqualObjects([SPJSONFormatter stringByUnformattingString:fmt], unf, @"simple unformatting test");
+ }
+
+ //other tests
+ {
+ NSArray *tests = @[
+ @[@"{\n\t\"key\": \"v\0al\"\n}", @"{\"key\": \"v\0al\"}", @"NUL in input (invalid JSON)"],
+ @[@"[\n\t\"\",\n\t\"\"\",\"", @"[\"\", \"\"\",\"", @"series of dquotes (invalid JSON)"],
+ @[@"{\n\t[\n\t\t{\n\t\t\t\"ab\\u0090c\",\n",@"{[{\"ab\\u0090c\", ",@"unterminated elements (invalid JSON)"],
+ @[@"[\n\t[\n\t\t[\n\t\t\t[\n\t\t\t\t[\n\t\t\t\t\t[\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t]\n]", @"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[null]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]",@"34 levels of indent"],
+ @[@"{\n\t\"a\": \"bcd}", @"{\"a\": \"bcd}",@"unterminated string (invalid JSON)"],
+ @[@"[\n\t1,\n\t\"ab\ncd\",\n\t3\n]",@"[1, \"ab\ncd\", 3]",@"multiline string (invalid JSON)"],
+ @[@"{}\n}\n},\nfalse\n]", @"{}}}, false]",@"closing something that is not open (invalid JSON)"],
+ @[@"[\n\t[\n\t\t123e4\n\t}\n}", @"[[123e4}}",@"unmatched braces (invalid JSON)"],
+ @[@"[\n\t{\n\t]\n}", @"[{]}",@"unmatched braces 2 (invalid JSON)"],
+ @[@"[ true , \n false \t ] \t \n",@"[true, false]",@"whitespace reformatting"],
+ @[@"[\n\t1,\n\t2,\n]", @"[1, 2, ]",@"trailing comma (valid for some parsers)"],
+ @[@"{}/{\n\t|\n}\n],\n-\"}[|[{}\\\"]:{~]\",\n,\n}{\n]{\n\t|: : [\n\t\t\\|\"],};]*}]", @"{}/{|}], -\"}[|[{}\\\"]:{~]\", , }{]\{|: : [\\|\"],};]*}]",@"random garbage"],
+ ];
+
+ for (NSArray *pair in tests) {
+ XCTAssertEqualObjects([SPJSONFormatter stringByUnformattingString:[pair objectAtIndex:0]], [pair objectAtIndex:1], @"%@", [pair objectAtIndex:2]);
+ }
+ }
+
+
+}
+
+@end
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 52e52d20..b84b20e4 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -224,6 +224,8 @@
5080229C1BF7C0FE0052A9B2 /* MGTemplateStandardMarkers.m in Sources */ = {isa = PBXBuildFile; fileRef = 296DC8AD0F909194002A3258 /* MGTemplateStandardMarkers.m */; };
5080229D1BF7C0FE0052A9B2 /* MGTemplateStandardFilters.m in Sources */ = {isa = PBXBuildFile; fileRef = 296DC8B40F909194002A3258 /* MGTemplateStandardFilters.m */; };
50805B0D1BF2A068005F7A99 /* SPPopUpButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 50805B0C1BF2A068005F7A99 /* SPPopUpButtonCell.m */; };
+ 50837F741E50DCD4004FAE8A /* SPJSONFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 50837F731E50DCD4004FAE8A /* SPJSONFormatterTests.m */; };
+ 50837F771E50E007004FAE8A /* SPJSONFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F70A951E4E547500636550 /* SPJSONFormatter.m */; };
5089B0271BE714E300E226CD /* SPIdMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 5089B0261BE714E300E226CD /* SPIdMenu.m */; };
50A9F8B119EAD4B90053E571 /* SPGotoDatabaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 50A9F8B019EAD4B90053E571 /* SPGotoDatabaseController.m */; };
50D3C3491A75B8A800B5429C /* GotoDatabaseDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50D3C34B1A75B8A800B5429C /* GotoDatabaseDialog.xib */; };
@@ -967,6 +969,7 @@
508022941BF7BA470052A9B2 /* English */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = English; path = English.lproj/SPQLPluginExportSettingsTemplate.html; sourceTree = "<group>"; };
50805B0B1BF2A068005F7A99 /* SPPopUpButtonCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPopUpButtonCell.h; sourceTree = "<group>"; };
50805B0C1BF2A068005F7A99 /* SPPopUpButtonCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPopUpButtonCell.m; sourceTree = "<group>"; };
+ 50837F731E50DCD4004FAE8A /* SPJSONFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPJSONFormatterTests.m; sourceTree = "<group>"; };
5089B0251BE714E300E226CD /* SPIdMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPIdMenu.h; sourceTree = "<group>"; };
5089B0261BE714E300E226CD /* SPIdMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPIdMenu.m; sourceTree = "<group>"; };
50A9F8AF19EAD4B90053E571 /* SPGotoDatabaseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPGotoDatabaseController.h; sourceTree = "<group>"; };
@@ -2472,6 +2475,7 @@
children = (
50D3C35B1A771C4C00B5429C /* SPParserUtilsTest.m */,
503B02CE1AE95C2C0060CAB1 /* SPTableFilterParserTest.m */,
+ 50837F731E50DCD4004FAE8A /* SPJSONFormatterTests.m */,
);
name = Other;
sourceTree = "<group>";
@@ -3180,6 +3184,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 50837F771E50E007004FAE8A /* SPJSONFormatter.m in Sources */,
505F56901BCEE491007467DD /* SPOSInfo.m in Sources */,
505F568F1BCEE485007467DD /* SPFunctions.m in Sources */,
502D21F81BA50966000D4CE7 /* SPDataAdditions.m in Sources */,
@@ -3189,6 +3194,7 @@
503B02CF1AE95C2C0060CAB1 /* SPTableFilterParserTest.m in Sources */,
50EA92681AB23EFC008D3C4F /* SPTableCopy.m in Sources */,
507FF1621BBF0D5000104523 /* SPTableCopyTest.m in Sources */,
+ 50837F741E50DCD4004FAE8A /* SPJSONFormatterTests.m in Sources */,
50EA926A1AB246B8008D3C4F /* SPDatabaseActionTest.m in Sources */,
50EA92651AB23EC8008D3C4F /* SPDatabaseAction.m in Sources */,
50EA92641AB23EAD008D3C4F /* SPDatabaseCopy.m in Sources */,