diff --git a/Travis CI.xcodeproj/project.pbxproj b/Travis CI.xcodeproj/project.pbxproj index 795f09a..0260cbc 100755 --- a/Travis CI.xcodeproj/project.pbxproj +++ b/Travis CI.xcodeproj/project.pbxproj @@ -22,7 +22,10 @@ 2E12F3951652048C000048F5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2E12F3931652046A000048F5 /* Sparkle.framework */; }; 2E12F39716520C2A000048F5 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 2E12F39616520C2A000048F5 /* dsa_pub.pem */; }; 2E72AF3B1669DA6000F2631C /* BuildEventStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E72AF3A1669DA6000F2631C /* BuildEventStream.m */; }; + 2E72AF3E166A7BE100F2631C /* EventFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E72AF3D166A7BE000F2631C /* EventFilter.m */; }; + 2E72AF41166A7F4600F2631C /* EventFilterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E72AF40166A7F4600F2631C /* EventFilterTests.m */; }; 2E72AF42166A7FEA00F2631C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2ECFA87545264EC28B9D5614 /* libPods.a */; }; + 2E72AF43166A800400F2631C /* EventFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E72AF3D166A7BE000F2631C /* EventFilter.m */; }; 2E72AF44166A812200F2631C /* BuildEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA045C156450750043A3A6 /* BuildEvent.m */; }; 2E75988B164DF68100B87810 /* Notification.m in Sources */ = {isa = PBXBuildFile; fileRef = 2E75988A164DF68100B87810 /* Notification.m */; }; 2E90F156164E389F009CAA25 /* Travis CI.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2E90F155164E389F009CAA25 /* Travis CI.icns */; }; @@ -124,6 +127,9 @@ 2E12F39616520C2A000048F5 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = ""; }; 2E72AF391669DA5F00F2631C /* BuildEventStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BuildEventStream.h; sourceTree = ""; }; 2E72AF3A1669DA6000F2631C /* BuildEventStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BuildEventStream.m; sourceTree = ""; }; + 2E72AF3C166A7BE000F2631C /* EventFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventFilter.h; sourceTree = ""; }; + 2E72AF3D166A7BE000F2631C /* EventFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventFilter.m; sourceTree = ""; }; + 2E72AF40166A7F4600F2631C /* EventFilterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EventFilterTests.m; sourceTree = ""; }; 2E759889164DF68100B87810 /* Notification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Notification.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 2E75988A164DF68100B87810 /* Notification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = Notification.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 2E90F155164E389F009CAA25 /* Travis CI.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Travis CI.icns"; sourceTree = ""; }; @@ -288,6 +294,7 @@ 2E00CCC4164F974900A30635 /* TravisToolbarTests */ = { isa = PBXGroup; children = ( + 2E72AF40166A7F4600F2631C /* EventFilterTests.m */, 2E00CCD8164F9A9B00A30635 /* PreferencesControllerTests.m */, 2EBA8CF5164FAD4A005C033E /* FilterPreferencesTests.m */, 2E00CCC5164F974900A30635 /* Supporting Files */, @@ -341,6 +348,8 @@ 2EDA045C156450750043A3A6 /* BuildEvent.m */, 2E72AF391669DA5F00F2631C /* BuildEventStream.h */, 2E72AF3A1669DA6000F2631C /* BuildEventStream.m */, + 2E72AF3C166A7BE000F2631C /* EventFilter.h */, + 2E72AF3D166A7BE000F2631C /* EventFilter.m */, 2EBA8CF8164FAD91005C033E /* FilterPreferences.h */, 2EBA8CF9164FAD91005C033E /* FilterPreferences.m */, 2E759889164DF68100B87810 /* Notification.h */, @@ -502,11 +511,13 @@ buildActionMask = 2147483647; files = ( 2E72AF44166A812200F2631C /* BuildEvent.m in Sources */, + 2E72AF43166A800400F2631C /* EventFilter.m in Sources */, 2E00CCD9164F9A9B00A30635 /* PreferencesControllerTests.m in Sources */, 2EBA8CF6164FAD4A005C033E /* FilterPreferencesTests.m in Sources */, 2EBA8CE9164FA582005C033E /* Preferences.m in Sources */, 2EBA8CEA164FA582005C033E /* PreferencesController.m in Sources */, 2EBA8CFB164FAD9F005C033E /* FilterPreferences.m in Sources */, + 2E72AF41166A7F4600F2631C /* EventFilterTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -526,6 +537,7 @@ 2E00CCB7164EE18000A30635 /* TravisHTTPClient.m in Sources */, 2EBA8CFA164FAD91005C033E /* FilterPreferences.m in Sources */, 2E72AF3B1669DA6000F2631C /* BuildEventStream.m in Sources */, + 2E72AF3E166A7BE100F2631C /* EventFilter.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/AppDelegate.m b/src/AppDelegate.m index dc850bf..1470ac8 100644 --- a/src/AppDelegate.m +++ b/src/AppDelegate.m @@ -8,7 +8,6 @@ #import "AppDelegate.h" -#import "TravisEventFetcher.h" #import "BuildEvent.h" #import "Preferences.h" #import "Notification.h" @@ -17,10 +16,12 @@ #import "FilterPreferences.h" #import #import "BuildEventStream.h" +#import "EventFilter.h" @interface AppDelegate () @property (strong) NSStatusItem *statusItem; @property (strong) BuildEventStream *buildEventStream; +@property (strong) EventFilter *eventFilter; @end @implementation AppDelegate @@ -32,10 +33,9 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification { [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; [self setBuildEventStream:[BuildEventStream buildEventStream]]; + [self setEventFilter:[EventFilter eventFilterWithInputStream:[[self buildEventStream] eventStream] filterPreferences:[FilterPreferences filterWithPreferences:[Preferences sharedPreferences]]]]; - [[[[self buildEventStream] eventStream] filter:^(BuildEvent *event) { - return [self shouldShowNotificationFor:event]; - }] subscribeNext:^(BuildEvent *event) { + [[[self eventFilter] outputStream] subscribeNext:^(BuildEvent *event) { [[[TravisAPI standardAPI] fetchBuildWithID:[event buildID] forRepository:[event name]] subscribeNext:^(NSDictionary *build) { [event updateBuildInfo:build]; Notification *notification = [Notification notificationWithEventData:event]; @@ -77,12 +77,6 @@ - (IBAction)showPreferences:(id)sender { [[self preferencesPanel] makeKeyAndOrderFront:self]; } -- (BOOL)shouldShowNotificationFor:(BuildEvent *)eventData { - FilterPreferences *filter = [FilterPreferences filterPreferencesWithPreferences:[Preferences sharedPreferences]]; - - return [filter matchesSlug:[eventData name]]; -} - #pragma mark - NSUserNotificationCenterDelegate - (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { diff --git a/src/EventFilter.h b/src/EventFilter.h new file mode 100644 index 0000000..54e1412 --- /dev/null +++ b/src/EventFilter.h @@ -0,0 +1,25 @@ +// +// EventFilter.h +// Travis CI +// +// Created by Henrik Hodne on 12/1/12. +// Copyright (c) 2012 Travis CI GmbH. All rights reserved. +// + +#import + +@class RACSignal; +@class FilterPreferences; + +@interface EventFilter : NSObject + +// A stream of filtered `TravisEvent`. +@property (nonatomic, strong, readonly) RACSignal *outputStream; + +// Creates a new event filter. +// +// inputStream - A stream of `TravisEvent` objects to filter. +// filterPreferences - A `FilterPreferences` object describing how to filter the incoming `TravisEvent` objects. ++ (EventFilter *)eventFilterWithInputStream:(RACSignal *)inputStream filterPreferences:(FilterPreferences *)filterPreferences; + +@end diff --git a/src/EventFilter.m b/src/EventFilter.m new file mode 100644 index 0000000..f4f89b8 --- /dev/null +++ b/src/EventFilter.m @@ -0,0 +1,46 @@ +// +// EventFilter.m +// Travis CI +// +// Created by Henrik Hodne on 12/1/12. +// Copyright (c) 2012 Travis CI GmbH. All rights reserved. +// + +#import "EventFilter.h" +#import +#import +#import "BuildEvent.h" +#import "FilterPreferences.h" + +@interface EventFilter () +@property (nonatomic, strong, readonly) RACSignal *inputStream; +@property (nonatomic, strong, readonly) FilterPreferences *filterPreferences; +@end + +@implementation EventFilter + +- (RACSignal *)outputStream { + @weakify(self); + return [[self inputStream] filter:^(BuildEvent *event) { + @strongify(self); + return [[self filterPreferences] matchesSlug:[event name]]; + }]; +} + +#pragma mark - Lifecycle + ++ (EventFilter *)eventFilterWithInputStream:(RACSignal *)inputStream filterPreferences:(FilterPreferences *)filterPreferences { + return [[self alloc] initWithInputStream:inputStream filterPreferences:filterPreferences]; +} + +- (id)initWithInputStream:(RACSignal *)inputStream filterPreferences:(FilterPreferences *)filterPreferences { + self = [super init]; + if (self == nil) return nil; + + _inputStream = inputStream; + _filterPreferences = filterPreferences; + + return self; +} + +@end diff --git a/test/EventFilterTests.m b/test/EventFilterTests.m new file mode 100644 index 0000000..ce5b9ae --- /dev/null +++ b/test/EventFilterTests.m @@ -0,0 +1,60 @@ +// +// EventFilterTests.m +// Travis CI +// +// Created by Henrik Hodne on 12/1/12. +// Copyright (c) 2012 Travis CI GmbH. All rights reserved. +// + +#import + +#define HC_SHORTHAND +#import + +#define MOCKITO_SHORTHAND +#import + +#import "EventFilter.h" +#import "BuildEvent.h" +#import "FilterPreferences.h" +#import + +@interface EventFilterTests : SenTestCase +@end + +@implementation EventFilterTests { + EventFilter *_eventFilter; + RACSubject *_inputStream; + FilterPreferences *_filterPreferences; + id _outputStream; +} + +- (void)setUp { + _inputStream = [RACSubject subject]; + _outputStream = mockProtocol(@protocol(RACSubscriber)); + _filterPreferences = mock([FilterPreferences class]); + _eventFilter = [EventFilter eventFilterWithInputStream:_inputStream filterPreferences:_filterPreferences]; + [[_eventFilter outputStream] subscribe:_outputStream]; +} + +#pragma mark - Tests + +- (void)testDoesNotRemoveMatchingSlugs { + [given([_filterPreferences matchesSlug:@"travis-ci/travis-ci"]) willReturnBool:YES]; + + BuildEvent *event = [[BuildEvent alloc] initWithEventData:@{ @"repository": @{ @"slug": @"travis-ci/travis-ci" } }]; + [_inputStream sendNext:event]; + + [verify(_outputStream) sendNext:event]; +} + +- (void)testRemovesNonMatchingSlugs { + [given([_filterPreferences matchesSlug:@"travis-ci/travis-ci"]) willReturnBool:NO]; + + BuildEvent *event = [[BuildEvent alloc] initWithEventData:@{ @"repository": @{ @"slug": @"travis-ci/travis-ci" } }]; + [_inputStream sendNext:event]; + + [verifyCount(_outputStream, never()) sendNext:event]; +} + +@end