Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support for CouchCocoa tests.

...and a bunch of other stuff.
  • Loading branch information...
commit d6f1ff64bab7b122053965ce5d814e298908c89a 1 parent b623966
@snej snej authored
View
8 App/AppDelegate.h
@@ -16,8 +16,10 @@
@property (strong, nonatomic) IBOutlet UIWindow *window;
@property (strong, nonatomic) IBOutlet UINavigationController *navController;
-/** The database this app is using; observable.
- (The unit tests access this property; see CouchTestCase.m.) */
-@property (readonly, retain, nonatomic) CouchDatabase* database;
+/** The URL of the Couchbase server. */
+@property (readonly, retain, nonatomic) NSURL* serverURL;
@end
+
+
+extern NSString* const AppDelegateCouchRestartedNotification;
View
57 App/AppDelegate.m
@@ -7,7 +7,9 @@
//
#import "AppDelegate.h"
-#import <CouchCocoa/CouchCocoa.h>
+
+
+NSString* const AppDelegateCouchRestartedNotification = @"AppDelegateCouchRestarted";
/** This is the name of the database the app will use -- customize it as you like,
@@ -16,19 +18,19 @@
@interface AppDelegate ()
-@property (readwrite, retain, nonatomic) CouchDatabase* database; // settable internally
+@property (readwrite, retain, nonatomic) NSURL* serverURL; // settable internally
@end
@implementation AppDelegate
@synthesize window = _window, navController = _navController;
-@synthesize database = _database;
+@synthesize serverURL = _serverURL;
- (void)dealloc
{
[_window release];
- [_database release];
+ [_serverURL release];
[super dealloc];
}
@@ -61,29 +63,14 @@ - (BOOL)application:(UIApplication *)application
-(void)couchbaseMobile:(CouchbaseMobile*)couchbase didStart:(NSURL*)serverURL
{
gCouchLogLevel = 1; // You can increase this to 2 (or even 3, which is overkill)
- gRESTLogLevel = kRESTLogNothing; // You can increase this to kRESTLogRequestURLs or higher
-
- if (!self.database) {
- // Do this on launch, but not when returning to the foreground:
- CouchServer* server = [[CouchServer alloc] initWithURL:serverURL];
- // Track active operations so we can wait for their completion in didEnterBackground, below
- server.tracksActiveOperations = YES;
- CouchDatabase* database = [server databaseNamed:kDatabaseName];
-
- // Create the database on the first run of the app.
- if (![[database GET] wait]) {
- [[database create] wait];
- }
-
- self.database = database;
- [server release];
- }
-
- // For most purposes you will want to track changes.
- self.database.tracksChanges = YES;
+ gRESTLogLevel = kRESTLogRequestURLs; // You can increase this to kRESTLogRequestURLs or higher
- NSLog(@"Couchbase is ready, go!");
- // TODO: Now that the database is ready, add your setup code here.
+ if (!self.serverURL)
+ self.serverURL = serverURL;
+ else
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName: AppDelegateCouchRestartedNotification
+ object: self];
}
-(void)couchbaseMobile:(CouchbaseMobile*)couchbase failedToStart:(NSError*)error
@@ -92,22 +79,4 @@ -(void)couchbaseMobile:(CouchbaseMobile*)couchbase failedToStart:(NSError*)error
NSAssert(NO, @"Couchbase failed to initialize: %@", error);
}
-- (void)applicationDidEnterBackground:(UIApplication *)application
-{
- NSLog(@"------ applicationDidEnterBackground");
- // Turn off the _changes watcher:
- self.database.tracksChanges = NO;
-
- // Make sure all transactions complete, because going into the background will
- // close down the CouchDB server:
- [RESTOperation wait: self.database.server.activeOperations];
-}
-
-- (void)applicationWillEnterForeground:(UIApplication *)application
-{
- NSLog(@"------ applicationWillEnterForeground");
- // Don't reconnect to the server yet ... wait for it to tell us it's back up,
- // by calling couchbaseMobile:didStart: again.
-}
-
@end
View
41 App/BeeCouchTest.h
@@ -0,0 +1,41 @@
+//
+// BeeCouchTest.h
+// Worker Bee
+//
+// Created by Jens Alfke on 10/5/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "BeeTest.h"
+#import <CouchCocoa/CouchCocoa.h>
+
+
+@interface BeeCouchTest : BeeTest
+
+/** The embedded Couchbase server. */
+@property (readonly) CouchServer* server;
+
+/** A database to use for your test.
+ The database will exist but be empty at the start of the test. */
+@property (readonly) CouchDatabase* database;
+
+/** The URL of the embedded Couchbase server.
+ You can access this directly, instead of the .server property, if you don't want to use a CouchServer object. */
+@property (readonly) NSURL* serverURL;
+
+/** The name of the database to create.
+ Defaults to the class name lowercased with "-db" appended.
+ You can override this method to use a different name. */
+@property (readonly) NSString* databaseName;
+
+@property (readonly) BOOL suspended;
+
+/** Called when the application enters the background.
+ You can override this to do extra work, but call the inherited method *at the end*. */
+- (void)serverWillSuspend;
+
+/** Called when the server resumes after the app returns to the foreground.
+ You can override this to do extra work, but call the inherited method first. */
+- (void) serverDidResume;
+
+@end
View
148 App/BeeCouchTest.m
@@ -0,0 +1,148 @@
+//
+// BeeCouchTest.m
+// Worker Bee
+//
+// Created by Jens Alfke on 10/5/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "BeeCouchTest.h"
+#import "AppDelegate.h"
+
+
+@interface BeeCouchTest ()
+@property (readwrite) BOOL suspended;
+@end
+
+
+@implementation BeeCouchTest
+{
+ CouchServer* _server;
+ CouchDatabase* _database;
+ BOOL _createdDatabase;
+}
+
+
++ (BOOL) isAbstractTest {
+ return self == [BeeCouchTest class];
+}
+
+
+- (void)dealloc {
+ [_server release];
+ [super dealloc];
+}
+
+
+- (NSURL*) serverURL {
+ return ((AppDelegate*)[[UIApplication sharedApplication] delegate]).serverURL;
+}
+
+
+- (NSString*) databaseName {
+ return [[NSStringFromClass([self class]) lowercaseString] stringByAppendingString: @"-db"];
+}
+
+
+- (CouchServer*) server {
+ if (!_server) {
+ _server = [[CouchServer alloc] initWithURL: self.serverURL];
+ // Track active operations so we can wait for their completion in didEnterBackground, below
+ _server.tracksActiveOperations = YES;
+ }
+ return _server;
+}
+
+
+- (CouchDatabase*) database {
+ if (self.suspended)
+ [self logMessage: @"WARNING: Accessing database while suspended"];
+ if (!_createdDatabase) {
+ _createdDatabase = YES;
+ CouchDatabase* database = [self.server databaseNamed: self.databaseName];
+ NSAssert(database, @"Failed to create CouchDatabase object");
+ // Delete and re-create the database:
+ RESTOperation* op = [database DELETE];
+ if ([op wait] || op.httpStatus == 404) {
+ op = [database create];
+ [op wait];
+ }
+ if (op.error) {
+ self.error = op.error;
+ return nil;
+ }
+ database.tracksChanges = YES;
+ _database = [database retain];
+ }
+ return _database;
+}
+
+
+- (void) setUp {
+ [super setUp];
+
+ _createdDatabase = NO;
+ NSNotificationCenter* nctr = [NSNotificationCenter defaultCenter];
+ [nctr addObserver: self
+ selector: @selector(serverDidResume)
+ name: AppDelegateCouchRestartedNotification
+ object: nil];
+}
+
+- (void) tearDown {
+ NSNotificationCenter* nctr = [NSNotificationCenter defaultCenter];
+ [nctr removeObserver: self
+ name:AppDelegateCouchRestartedNotification
+ object: nil];
+ [_database release];
+ _database = nil;
+ [_server release];
+ _server = nil;
+ self.suspended = NO;
+
+ [super tearDown];
+}
+
+
+@synthesize suspended = _suspended;
+
+
+- (void)applicationDidEnterBackground: (NSNotification*)notification
+{
+ [self serverWillSuspend];
+}
+
+
+- (void)applicationWillEnterForeground: (NSNotification*)notification
+{
+ [super applicationWillEnterForeground: notification];
+ // Wait for notification that Couchbase server has restarted
+ self.status = @"Waiting for server to resume...";
+}
+
+
+- (void)serverWillSuspend
+{
+ [self logMessage: @"Suspending"];
+
+ self.suspended = YES;
+ self.status = @"Server suspended";
+
+ // Turn off the _changes watcher:
+ _database.tracksChanges = NO;
+
+ // Make sure all transactions complete, because going into the background will
+ // close down the CouchDB server:
+ [RESTOperation wait: _server.activeOperations];
+}
+
+
+- (void) serverDidResume {
+ [self logMessage: @"Server resumed"];
+ _database.tracksChanges = YES;
+ self.suspended = NO;
+ self.status = @"Resumed";
+}
+
+
+@end
View
20 App/BeeTest.h
@@ -28,12 +28,28 @@
@property (assign) id<BeeTestDelegate> delegate;
@property BOOL running;
-@property (readonly) NSError* error;
+@property (copy) NSString* status;
+@property (copy) NSError* error;
+@property (copy) NSString* errorMessage;
@property (readonly) NSArray* messages;
+@property (readonly, retain) NSDate* startTime;
+@property (readonly, retain) NSDate* endTime;
+
+- (void) clearMessages;
+
+#pragma mark For subclasses to override:
+
+- (void) setUp;
+- (void) tearDown;
+
+- (void)applicationDidEnterBackground: (NSNotification*)notification;
+- (void)applicationWillEnterForeground: (NSNotification*)notification;
+
+#pragma mark For subclasses to call:
+
- (void) logMessage: (NSString*)message;
- (void) logFormat: (NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
- (BOOL) addTimestamp: (NSString*)message;
-- (void) clearMessages;
@end
View
110 App/BeeTest.m
@@ -18,12 +18,20 @@ @interface BeeTest ()
BOOL _running;
NSMutableArray* _messages;
}
+@property (readwrite, retain) NSDate* startTime;
+@property (readwrite, retain) NSDate* endTime;
@property (retain) NSString* lastTimestamp;
@end
@implementation BeeTest
+
++ (BOOL) isAbstractTest {
+ return self == [BeeTest class];
+}
+
+
+ (NSArray*) allTestClasses {
static NSArray* sAllTestClasses;
if (!sAllTestClasses) {
@@ -36,24 +44,30 @@ + (NSArray*) allTestClasses {
Class c = classes[i];
if (class_getClassMethod(c, @selector(isSubclassOfClass:))
&& [c isSubclassOfClass: self]
- && c != self) {
+ && ![c isAbstractTest]) {
NSLog(@"BeeTets: Found test class %@", classes[i]);
[testClasses addObject: classes[i]];
}
}
free(classes);
-
+ [testClasses sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
+ return [NSStringFromClass(obj1) caseInsensitiveCompare: NSStringFromClass(obj2)];
+ }];
sAllTestClasses = [testClasses copy];
}
return sAllTestClasses;
}
+ (NSString*) displayName {
- return NSStringFromClass(self);
+ NSString* name = NSStringFromClass(self);
+ if ([name hasSuffix: @"Test"])
+ name = [name substringToIndex: name.length - 4];
+ return name;
}
-@synthesize delegate=_delegate, error = _error, messages = _messages, lastTimestamp = _lastTimestamp;
+@synthesize delegate=_delegate, status = _status, startTime = _startTime, endTime = _endTime, error = _error, messages = _messages, lastTimestamp = _lastTimestamp;
+
- (id)init {
self = [super init];
@@ -66,10 +80,47 @@ - (id)init {
- (void)dealloc {
[_messages release];
[_lastTimestamp release];
+ [_startTime release];
+ [_endTime release];
[_error release];
[super dealloc];
}
+
+- (NSError*) error {
+ return _error;
+}
+
+- (void) setError: (NSError*)error {
+ if (error != _error) {
+ [_error release];
+ _error = [error retain];
+ if (error) {
+ // Log the error and stop the test:
+ NSString* message = [error.domain isEqualToString: @"BeeTest"] ? self.errorMessage
+ : error.description;
+ [self logFormat: @"ERROR: %@", message];
+ self.running = NO;
+ }
+ }
+}
+
+
+- (void) setErrorMessage:(NSString *)errorMessage {
+ if (errorMessage) {
+ NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
+ errorMessage, NSLocalizedDescriptionKey, nil];
+ self.error = [NSError errorWithDomain: @"BeeTest" code: 1 userInfo: info];
+ } else {
+ self.error = nil;
+ }
+}
+
+- (NSString*) errorMessage {
+ return self.error.localizedDescription;
+}
+
+
- (BOOL) running {
return _running;
}
@@ -77,10 +128,23 @@ - (BOOL) running {
- (void) setRunning:(BOOL)run {
if (run != _running) {
_running = run;
- if (run)
+ if (run) {
+ self.startTime = [NSDate date];
+ self.endTime = nil;
+ self.error = nil;
+ self.status = nil;
[self clearMessages];
+ } else {
+ self.endTime = [NSDate date];
+ self.status = nil;
+ }
[self addTimestamp: (run ? @"STARTED" : @"STOPPED")];
[_delegate beeTest: self isRunning: _running];
+
+ if (run)
+ [self setUp];
+ else
+ [self tearDown];
}
}
@@ -140,4 +204,40 @@ - (void) clearMessages {
self.lastTimestamp = nil;
}
+
+- (void) setUp {
+ NSNotificationCenter* nctr = [NSNotificationCenter defaultCenter];
+ [nctr addObserver: self
+ selector: @selector(applicationDidEnterBackground:)
+ name: UIApplicationDidEnterBackgroundNotification
+ object: nil];
+ [nctr addObserver: self
+ selector: @selector(applicationWillEnterForeground:)
+ name: UIApplicationWillEnterForegroundNotification
+ object: nil];
+}
+
+- (void) tearDown {
+ NSNotificationCenter* nctr = [NSNotificationCenter defaultCenter];
+ [nctr removeObserver: self
+ name:UIApplicationDidEnterBackgroundNotification
+ object: nil];
+ [nctr removeObserver: self
+ name:UIApplicationWillEnterForegroundNotification
+ object: nil];
+}
+
+
+- (void)applicationDidEnterBackground: (NSNotification*)notification
+{
+ [self addTimestamp: @"BACKGROUND"];
+}
+
+
+- (void)applicationWillEnterForeground: (NSNotification*)notification
+{
+ [self addTimestamp: @"FOREGROUND"];
+}
+
+
@end
View
1  App/BeeTestController.h
@@ -15,6 +15,7 @@
@property (readonly) BeeTest* test;
@property (readonly) IBOutlet UISwitch* onOffSwitch;
+@property (readonly) IBOutlet UILabel* statusLabel;
@property (readonly) IBOutlet UIActivityIndicatorView* activityIndicator;
@property (readonly) IBOutlet UITextView* transcript;
View
43 App/BeeTestController.m
@@ -13,23 +13,28 @@
@interface BeeTestController () <BeeTestDelegate>
- (void) displayMessages;
- (void) scrollToEnd;
+- (void) showStatus;
@end
@implementation BeeTestController
-@synthesize test = _test, onOffSwitch = _onOffSwitch, activityIndicator = _activityIndicator, transcript = _transcript;
+@synthesize test = _test, onOffSwitch = _onOffSwitch, activityIndicator = _activityIndicator, transcript = _transcript, statusLabel = _statusLabel;
- (id) initWithTest: (BeeTest*)test {
self = [super initWithNibName: @"BeeTestController" bundle: nil];
if (self) {
_test = [test retain];
_test.delegate = self;
+ [_test addObserver: self forKeyPath: @"status" options: 0 context: NULL];
+ [_test addObserver: self forKeyPath: @"error" options: 0 context: NULL];
}
return self;
}
- (void)dealloc {
+ [_test removeObserver: self forKeyPath: @"status"];
+ [_test removeObserver: self forKeyPath: @"error"];
_test.delegate = nil;
[_test release];
[super dealloc];
@@ -51,14 +56,21 @@ - (void)viewWillAppear:(BOOL)animated
}
self.view.backgroundColor = sBackground;
+ [_onOffSwitch retain];
+ [_onOffSwitch removeFromSuperview];
+ UIBarButtonItem* rightItem = [[UIBarButtonItem alloc] initWithCustomView: _onOffSwitch];
+ self.navigationItem.rightBarButtonItem = rightItem;
+ [rightItem release];
+ [_onOffSwitch release];
+
[self beeTest: _test isRunning: _test.running];
[self scrollToEnd];
+ [self showStatus];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
- // Return YES for supported orientations
- return (interfaceOrientation == UIInterfaceOrientationPortrait);
+ return YES;
}
@@ -70,7 +82,6 @@ - (void) displayMessages {
- (void) scrollToEnd {
[_transcript scrollRangeToVisible: NSMakeRange(_transcript.text.length-1, 1)];
- NSLog(@"scrollToEnd: length=%u", _transcript.text.length);
}
- (void) beeTest: (BeeTest*)test loggedMessage: (NSString*)message {
@@ -100,4 +111,28 @@ - (IBAction) startStopTest:(id)sender {
_test.running = [sender isOn];
}
+- (void) showStatus {
+ NSString* status;
+ UIColor* color;
+ if (_test.error) {
+ status = _test.errorMessage;
+ color = [UIColor redColor];
+ } else {
+ status = _test.status;
+ color = [UIColor blackColor];
+ }
+ _statusLabel.text = status;
+ _statusLabel.textColor = color;
+
+}
+
+- (void) observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if ([keyPath isEqualToString: @"status"] || [keyPath isEqualToString: @"error"])
+ [self showStatus];
+}
+
@end
View
126 App/BeeTestController.xib
@@ -3,18 +3,19 @@
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">11C57</string>
- <string key="IBDocument.InterfaceBuilderVersion">1934</string>
+ <string key="IBDocument.InterfaceBuilderVersion">1938</string>
<string key="IBDocument.AppKitVersion">1138.21</string>
<string key="IBDocument.HIToolboxVersion">567.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
- <string key="NS.object.0">931</string>
+ <string key="NS.object.0">933</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBUITextView</string>
<string>IBUISwitch</string>
<string>IBUIActivityIndicatorView</string>
<string>IBUIView</string>
+ <string>IBUILabel</string>
<string>IBProxyObject</string>
</array>
<array key="IBDocument.PluginDependencies">
@@ -37,39 +38,13 @@
<reference key="NSNextResponder"/>
<int key="NSvFlags">292</int>
<array class="NSMutableArray" key="NSSubviews">
- <object class="IBUIActivityIndicatorView" id="617837538">
- <reference key="NSNextResponder" ref="562138043"/>
- <int key="NSvFlags">289</int>
- <string key="NSFrame">{{263, 48}, {37, 37}}</string>
- <reference key="NSSuperview" ref="562138043"/>
- <reference key="NSWindow"/>
- <reference key="NSNextKeyView" ref="953143795"/>
- <string key="NSReuseIdentifierKey">_NS:1030</string>
- <bool key="IBUIOpaque">NO</bool>
- <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
- <bool key="IBUIAnimating">YES</bool>
- <int key="IBUIStyle">0</int>
- </object>
- <object class="IBUISwitch" id="396093735">
- <reference key="NSNextResponder" ref="562138043"/>
- <int key="NSvFlags">293</int>
- <string key="NSFrame">{{106, 53}, {94, 27}}</string>
- <reference key="NSSuperview" ref="562138043"/>
- <reference key="NSWindow"/>
- <reference key="NSNextKeyView" ref="617837538"/>
- <string key="NSReuseIdentifierKey">_NS:606</string>
- <bool key="IBUIOpaque">NO</bool>
- <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
- <int key="IBUIContentHorizontalAlignment">0</int>
- <int key="IBUIContentVerticalAlignment">0</int>
- </object>
<object class="IBUITextView" id="953143795">
<reference key="NSNextResponder" ref="562138043"/>
<int key="NSvFlags">274</int>
- <string key="NSFrame">{{20, 93}, {280, 367}}</string>
+ <string key="NSFrame">{{9, 56}, {302, 398}}</string>
<reference key="NSSuperview" ref="562138043"/>
<reference key="NSWindow"/>
- <reference key="NSNextKeyView"/>
+ <reference key="NSNextKeyView" ref="159603070"/>
<string key="NSReuseIdentifierKey">_NS:639</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
@@ -97,11 +72,54 @@
<int key="NSfFlags">16</int>
</object>
</object>
+ <object class="IBUIActivityIndicatorView" id="617837538">
+ <reference key="NSNextResponder" ref="562138043"/>
+ <int key="NSvFlags">265</int>
+ <string key="NSFrame">{{291, 456}, {20, 20}}</string>
+ <reference key="NSSuperview" ref="562138043"/>
+ <reference key="NSWindow"/>
+ <reference key="NSNextKeyView"/>
+ <string key="NSReuseIdentifierKey">_NS:1030</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <bool key="IBUIAnimating">YES</bool>
+ </object>
+ <object class="IBUILabel" id="159603070">
+ <reference key="NSNextResponder" ref="562138043"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{9, 455}, {274, 21}}</string>
+ <reference key="NSSuperview" ref="562138043"/>
+ <reference key="NSWindow"/>
+ <reference key="NSNextKeyView" ref="617837538"/>
+ <string key="NSReuseIdentifierKey">_NS:328</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <bool key="IBUIClipsSubviews">YES</bool>
+ <int key="IBUIContentMode">7</int>
+ <bool key="IBUIUserInteractionEnabled">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <string key="IBUIText">STATUS</string>
+ <object class="NSColor" key="IBUITextColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MCAwIDAAA</bytes>
+ </object>
+ <nil key="IBUIHighlightedColor"/>
+ <int key="IBUIBaselineAdjustment">1</int>
+ <float key="IBUIMinimumFontSize">10</float>
+ <object class="IBUIFontDescription" key="IBUIFontDescription">
+ <int key="type">1</int>
+ <double key="pointSize">14</double>
+ </object>
+ <object class="NSFont" key="IBUIFont">
+ <string key="NSName">Helvetica</string>
+ <double key="NSSize">14</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ </object>
</array>
<string key="NSFrameSize">{320, 480}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
- <reference key="NSNextKeyView" ref="396093735"/>
+ <reference key="NSNextKeyView" ref="953143795"/>
<string key="NSReuseIdentifierKey">_NS:196</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
@@ -113,6 +131,19 @@
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
+ <object class="IBUISwitch" id="396093735">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">293</int>
+ <string key="NSFrameSize">{79, 27}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
+ <reference key="NSNextKeyView"/>
+ <string key="NSReuseIdentifierKey">_NS:606</string>
+ <bool key="IBUIOpaque">NO</bool>
+ <string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+ <int key="IBUIContentHorizontalAlignment">0</int>
+ <int key="IBUIContentVerticalAlignment">0</int>
+ </object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
@@ -149,6 +180,14 @@
<int key="connectionID">21</int>
</object>
<object class="IBConnectionRecord">
+ <object class="IBCocoaTouchOutletConnection" key="connection">
+ <string key="label">statusLabel</string>
+ <reference key="source" ref="372490531"/>
+ <reference key="destination" ref="159603070"/>
+ </object>
+ <int key="connectionID">24</int>
+ </object>
+ <object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">startStopTest:</string>
<reference key="source" ref="396093735"/>
@@ -181,9 +220,9 @@
<int key="objectID">8</int>
<reference key="object" ref="562138043"/>
<array class="NSMutableArray" key="children">
- <reference ref="617837538"/>
<reference ref="953143795"/>
- <reference ref="396093735"/>
+ <reference ref="617837538"/>
+ <reference ref="159603070"/>
</array>
<reference key="parent" ref="0"/>
</object>
@@ -193,13 +232,18 @@
<reference key="parent" ref="562138043"/>
</object>
<object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="953143795"/>
+ <reference key="parent" ref="562138043"/>
+ </object>
+ <object class="IBObjectRecord">
<int key="objectID">17</int>
<reference key="object" ref="396093735"/>
- <reference key="parent" ref="562138043"/>
+ <reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
- <int key="objectID">19</int>
- <reference key="object" ref="953143795"/>
+ <int key="objectID">23</int>
+ <reference key="object" ref="159603070"/>
<reference key="parent" ref="562138043"/>
</object>
</array>
@@ -212,13 +256,14 @@
<string key="13.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="17.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="19.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+ <string key="23.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="8.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
- <int key="maxID">21</int>
+ <int key="maxID">24</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -228,6 +273,7 @@
<dictionary class="NSMutableDictionary" key="outlets">
<string key="activityIndicator">UIActivityIndicatorView</string>
<string key="onOffSwitch">UISwitch</string>
+ <string key="statusLabel">UILabel</string>
<string key="transcript">UITextView</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
@@ -239,6 +285,10 @@
<string key="name">onOffSwitch</string>
<string key="candidateClassName">UISwitch</string>
</object>
+ <object class="IBToOneOutletInfo" key="statusLabel">
+ <string key="name">statusLabel</string>
+ <string key="candidateClassName">UILabel</string>
+ </object>
<object class="IBToOneOutletInfo" key="transcript">
<string key="name">transcript</string>
<string key="candidateClassName">UITextView</string>
@@ -255,6 +305,6 @@
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
- <string key="IBCocoaTouchPluginVersion">931</string>
+ <string key="IBCocoaTouchPluginVersion">933</string>
</data>
</archive>
View
1  App/Info.plist
@@ -33,6 +33,7 @@
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
View
3  App/TestListController.h
@@ -7,9 +7,12 @@
//
#import <UIKit/UIKit.h>
+@class BeeTest;
@interface TestListController : UITableViewController
@property (readonly) NSArray* testList;
+- (BeeTest*) testForClass: (Class)testClass;
+- (BeeTest*) makeTestForClass: (Class)testClass;
@end
View
54 App/TestListController.m
@@ -105,10 +105,10 @@ - (void)viewDidDisappear:(BOOL)animated
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
- // Return YES for supported orientations
- return (interfaceOrientation == UIInterfaceOrientationPortrait);
+ return YES;
}
+
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
@@ -128,12 +128,20 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+ reuseIdentifier:CellIdentifier] autorelease];
+ UISwitch* sw = [[UISwitch alloc] init];
+ cell.accessoryView = sw;
+ [sw addTarget: self action: @selector(startStopTest:) forControlEvents: UIControlEventValueChanged];
+ [sw release];
}
Class testClass = [_testList objectAtIndex: indexPath.row];
+ BeeTest* existingTest = [self testForClass: testClass];
cell.textLabel.text = [testClass displayName];
-
+ UISwitch* sw = (UISwitch*)cell.accessoryView;
+ sw.on = existingTest.running;
+ sw.tag = indexPath.row;
return cell;
}
@@ -141,12 +149,17 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
#pragma mark - Table view delegate
- (BeeTest*) testForClass: (Class)testClass {
- NSString* key = NSStringFromClass(testClass);
- BeeTest* test = [_activeTestByClass objectForKey: key];
+ return [_activeTestByClass objectForKey: NSStringFromClass(testClass)];
+}
+
+- (BeeTest*) makeTestForClass: (Class)testClass {
+ BeeTest* test = [self testForClass: testClass];
if (!test) {
test = [[testClass alloc] init];
- if (test)
- [_activeTestByClass setObject: test forKey: key];
+ if (test) {
+ [_activeTestByClass setObject: test forKey: NSStringFromClass(testClass)];
+ [test addObserver: self forKeyPath: @"running" options: 0 context: NULL];
+ }
}
return test;
}
@@ -154,7 +167,7 @@ - (BeeTest*) testForClass: (Class)testClass {
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Class testClass = [_testList objectAtIndex: indexPath.row];
- BeeTest* test = [self testForClass: testClass];
+ BeeTest* test = [self makeTestForClass: testClass];
if (!test)
return; // TODO: Show an alert
BeeTestController *testController = [[BeeTestController alloc] initWithTest: test];
@@ -162,4 +175,27 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
[testController release];
}
+- (void) observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if ([object isKindOfClass: [BeeTest class]]) {
+ Class testClass = [object class];
+ NSUInteger index = [_testList indexOfObjectIdenticalTo: testClass];
+ NSAssert(index != NSNotFound, @"Can't find %@", object);
+ NSIndexPath* path = [NSIndexPath indexPathForRow: index inSection: 0];
+ UITableViewCell* cell = [self.tableView cellForRowAtIndexPath: path];
+ UISwitch* sw = (UISwitch*)cell.accessoryView;
+ [sw setOn: [object running] animated: YES];
+ }
+}
+
+- (IBAction) startStopTest:(id)sender {
+ Class testClass = [_testList objectAtIndex: [sender tag]];
+ BeeTest* test = [self makeTestForClass: testClass];
+ NSLog(@"Setting %@ running=%i", test, [sender isOn]);
+ test.running = [sender isOn];
+}
+
@end
View
13 Tests/CircleOfLifeTest.h
@@ -0,0 +1,13 @@
+//
+// CircleOfLifeTest.h
+// Worker Bee
+//
+// Created by Jens Alfke on 10/5/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "BeeCouchTest.h"
+
+@interface CircleOfLifeTest : BeeCouchTest
+
+@end
View
54 Tests/CircleOfLifeTest.m
@@ -0,0 +1,54 @@
+//
+// CircleOfLifeTest.m
+// Worker Bee
+//
+// Created by Jens Alfke on 10/5/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "CircleOfLifeTest.h"
+
+@implementation CircleOfLifeTest
+{
+ int _sequence;
+}
+
+
+- (void) doSomethingSoon {
+ if (self.running)
+ [self performSelector: @selector(doSomething) withObject: nil afterDelay: 1.0];
+}
+
+- (void) doSomething {
+ if (!self.suspended) {
+ ++_sequence;
+ NSString* dateStr = [RESTBody JSONObjectWithDate: [NSDate date]];
+ NSDictionary* props = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt: _sequence], @"sequence",
+ dateStr, @"date", nil];
+ [self logFormat: @"Adding doc: %@", props];
+ CouchDocument* doc = [self.database untitledDocument];
+ RESTOperation* op = [doc putProperties: props];
+ [op onCompletion: ^{
+ if (op.error)
+ self.error = op.error;
+ else
+ [self logFormat: @"..._id = %@", doc.documentID];
+ }];
+ }
+ [self doSomethingSoon];
+}
+
+- (void) setUp {
+ [super setUp];
+ _sequence = 0;
+ [self doSomething];
+}
+
+- (void) tearDown {
+ [NSObject cancelPreviousPerformRequestsWithTarget: self];
+ [super tearDown];
+}
+
+
+@end
View
20 Tests/CountTest.h
@@ -0,0 +1,20 @@
+//
+// NullTest.h
+// Worker Bee
+//
+// Created by Jens Alfke on 10/4/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "BeeTest.h"
+
+@interface CountTest : BeeTest
+
+@property int count;
+@property int limit;
+
+@end
+
+
+@interface ShortCountTest : CountTest
+@end
View
55 Tests/CountTest.m
@@ -0,0 +1,55 @@
+//
+// NullTest.m
+// Worker Bee
+//
+// Created by Jens Alfke on 10/4/11.
+// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
+//
+
+#import "CountTest.h"
+
+@implementation CountTest
+
+@synthesize count = _count, limit = _limit;
+
+- (void) logSoon {
+ [self performSelector: @selector(logSomething) withObject: nil afterDelay: 1.0];
+}
+
+- (void) logSomething {
+ [self logFormat: @"Hi there! %i", ++_count, 45];
+ if (_count % 5 == 0)
+ self.status = [NSString stringWithFormat: @"Reached %i", _count];
+ if (_limit == 0 || _count < _limit)
+ [self logSoon];
+ else {
+ self.errorMessage = @"O noes, count overflowed!!!";
+ self.running = NO;
+ }
+}
+
+- (void) setUp {
+ [super setUp];
+ _count = 0;
+ [self logSoon];
+}
+
+- (void) tearDown {
+ [NSObject cancelPreviousPerformRequestsWithTarget: self];
+ [super tearDown];
+}
+
+@end
+
+
+@implementation ShortCountTest
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ self.limit = 10;
+ }
+ return self;
+}
+
+@end
View
24 Worker Bee.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 27A07488143CCDCD0043BEB6 /* BeeCouchTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A07487143CCDCD0043BEB6 /* BeeCouchTest.m */; };
+ 27A0748C143CDE6B0043BEB6 /* CircleOfLifeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A0748B143CDE6B0043BEB6 /* CircleOfLifeTest.m */; };
27B28FA614314FB800B86F18 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27B28FA514314FB800B86F18 /* UIKit.framework */; };
27B28FA814314FB800B86F18 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27B28FA714314FB800B86F18 /* Foundation.framework */; };
27B28FAA14314FB800B86F18 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27B28FA914314FB800B86F18 /* CoreGraphics.framework */; };
@@ -23,7 +25,7 @@
27CB6566143B997A00EEA1F2 /* TestListController.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CB6564143B997900EEA1F2 /* TestListController.m */; };
27CB6567143B997A00EEA1F2 /* TestListController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 27CB6565143B997900EEA1F2 /* TestListController.xib */; };
27CB656A143B9B7E00EEA1F2 /* BeeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CB6569143B9B7E00EEA1F2 /* BeeTest.m */; };
- 27CB656D143B9EB900EEA1F2 /* NullTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CB656C143B9EB800EEA1F2 /* NullTest.m */; };
+ 27CB656D143B9EB900EEA1F2 /* CountTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CB656C143B9EB800EEA1F2 /* CountTest.m */; };
27CB6576143BAA2F00EEA1F2 /* BeeTestController.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CB6574143BAA2E00EEA1F2 /* BeeTestController.m */; };
27CB6577143BAA2F00EEA1F2 /* BeeTestController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 27CB6575143BAA2F00EEA1F2 /* BeeTestController.xib */; };
27DB17F2143BB3A800EBA619 /* little_pluses.png in Resources */ = {isa = PBXBuildFile; fileRef = 27DB17F1143BB3A800EBA619 /* little_pluses.png */; };
@@ -31,6 +33,10 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
+ 27A07486143CCDCD0043BEB6 /* BeeCouchTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BeeCouchTest.h; sourceTree = "<group>"; };
+ 27A07487143CCDCD0043BEB6 /* BeeCouchTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BeeCouchTest.m; sourceTree = "<group>"; };
+ 27A0748A143CDE6B0043BEB6 /* CircleOfLifeTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleOfLifeTest.h; sourceTree = "<group>"; };
+ 27A0748B143CDE6B0043BEB6 /* CircleOfLifeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CircleOfLifeTest.m; sourceTree = "<group>"; };
27B28FA114314FB800B86F18 /* Worker Bee.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Worker Bee.app"; sourceTree = BUILT_PRODUCTS_DIR; };
27B28FA514314FB800B86F18 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
27B28FA714314FB800B86F18 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -55,8 +61,8 @@
27CB6565143B997900EEA1F2 /* TestListController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TestListController.xib; sourceTree = "<group>"; };
27CB6568143B9B7E00EEA1F2 /* BeeTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BeeTest.h; sourceTree = "<group>"; };
27CB6569143B9B7E00EEA1F2 /* BeeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BeeTest.m; sourceTree = "<group>"; };
- 27CB656B143B9EB800EEA1F2 /* NullTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NullTest.h; sourceTree = "<group>"; };
- 27CB656C143B9EB800EEA1F2 /* NullTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NullTest.m; sourceTree = "<group>"; };
+ 27CB656B143B9EB800EEA1F2 /* CountTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountTest.h; sourceTree = "<group>"; };
+ 27CB656C143B9EB800EEA1F2 /* CountTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountTest.m; sourceTree = "<group>"; };
27CB6573143BAA2E00EEA1F2 /* BeeTestController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BeeTestController.h; sourceTree = "<group>"; };
27CB6574143BAA2E00EEA1F2 /* BeeTestController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BeeTestController.m; sourceTree = "<group>"; };
27CB6575143BAA2F00EEA1F2 /* BeeTestController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BeeTestController.xib; sourceTree = "<group>"; };
@@ -132,6 +138,8 @@
27CB6575143BAA2F00EEA1F2 /* BeeTestController.xib */,
27CB6568143B9B7E00EEA1F2 /* BeeTest.h */,
27CB6569143B9B7E00EEA1F2 /* BeeTest.m */,
+ 27A07486143CCDCD0043BEB6 /* BeeCouchTest.h */,
+ 27A07487143CCDCD0043BEB6 /* BeeCouchTest.m */,
27DB17F4143BD36A00EBA619 /* double_lined.png */,
27DB17F1143BB3A800EBA619 /* little_pluses.png */,
27B28FAC14314FB800B86F18 /* Supporting Files */,
@@ -154,8 +162,10 @@
27CB656E143B9EC500EEA1F2 /* Tests */ = {
isa = PBXGroup;
children = (
- 27CB656B143B9EB800EEA1F2 /* NullTest.h */,
- 27CB656C143B9EB800EEA1F2 /* NullTest.m */,
+ 27CB656B143B9EB800EEA1F2 /* CountTest.h */,
+ 27CB656C143B9EB800EEA1F2 /* CountTest.m */,
+ 27A0748A143CDE6B0043BEB6 /* CircleOfLifeTest.h */,
+ 27A0748B143CDE6B0043BEB6 /* CircleOfLifeTest.m */,
);
path = Tests;
sourceTree = "<group>";
@@ -232,8 +242,10 @@
27B28FB614314FB800B86F18 /* AppDelegate.m in Sources */,
27CB6566143B997A00EEA1F2 /* TestListController.m in Sources */,
27CB656A143B9B7E00EEA1F2 /* BeeTest.m in Sources */,
- 27CB656D143B9EB900EEA1F2 /* NullTest.m in Sources */,
+ 27CB656D143B9EB900EEA1F2 /* CountTest.m in Sources */,
27CB6576143BAA2F00EEA1F2 /* BeeTestController.m in Sources */,
+ 27A07488143CCDCD0043BEB6 /* BeeCouchTest.m in Sources */,
+ 27A0748C143CDE6B0043BEB6 /* CircleOfLifeTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Please sign in to comment.
Something went wrong with that request. Please try again.