Skip to content

Commit

Permalink
Issue #67: Added tests and fixed MIKMIDISequence's KVO compliance for…
Browse files Browse the repository at this point in the history
… -tracks.
  • Loading branch information
Andrew Madsen committed Mar 17, 2015
1 parent b34e1e6 commit 4febc1b
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 28 deletions.
42 changes: 40 additions & 2 deletions Framework/MIKMIDI Tests/MIKMIDISequenceTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,26 @@

@interface MIKMIDISequenceTests : XCTestCase

@property (nonatomic, strong) MIKMIDISequence *sequence;
@property (nonatomic, strong) NSMutableSet *receivedNotificationKeyPaths;

@end

@implementation MIKMIDISequenceTests

- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.

self.receivedNotificationKeyPaths = [NSMutableSet set];
self.sequence = [MIKMIDISequence sequence];
[self.sequence addObserver:self forKeyPath:@"tracks" options:0 context:NULL];
}

- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[self.sequence removeObserver:self forKeyPath:@"tracks"];

[super tearDown];
}

Expand Down Expand Up @@ -54,4 +61,35 @@ - (void)testMIDIFileReadPerformance
}];
}

- (void)testKVOForAddingATrack
{
XCTAssertNotNil(self.sequence);

MIKMIDITrack *firstTrack = [self.sequence addTrack];
XCTAssertNotNil(firstTrack, @"Creating an MIKMIDITrack failed.");
XCTAssertTrue([self.receivedNotificationKeyPaths containsObject:@"tracks"], @"KVO notification when adding a track not received.");
}

- (void)testKVOForRemovingATrack
{
MIKMIDITrack *firstTrack = [self.sequence addTrack];
XCTAssertNotNil(firstTrack, @"Creating an MIKMIDITrack failed.");
MIKMIDITrack *secondTrack = [self.sequence addTrack];
XCTAssertNotNil(secondTrack, @"Creating an MIKMIDITrack failed.");

[self.receivedNotificationKeyPaths removeAllObjects];
[self.sequence removeTrack:firstTrack];
XCTAssertTrue([self.receivedNotificationKeyPaths containsObject:@"tracks"], @"KVO notification when removing a track not received.");
XCTAssertEqualObjects(self.sequence.tracks, @[secondTrack], @"Removing a track failed.");
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.sequence) {
[self.receivedNotificationKeyPaths addObject:keyPath];
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>1.38</real>
<real>0.14</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
<key>maxPercentRelativeStandardDeviation</key>
Expand Down
32 changes: 19 additions & 13 deletions Source/MIKMIDISequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ NS_INLINE MIKMIDITimeSignature MIKMIDITimeSignatureMake(UInt8 numerator, UInt8 d
*/
- (BOOL)writeToURL:(NSURL *)fileURL error:(NSError **)error;

#pragma mark - Track Management

/**
* Creates and adds a new MIDI track to the sequence.
*/
Expand All @@ -167,18 +169,7 @@ NS_INLINE MIKMIDITimeSignature MIKMIDITimeSignatureMake(UInt8 numerator, UInt8 d
*/
- (BOOL)removeTrack:(MIKMIDITrack *)track;

/**
* A MusicTimeStamp that is less than the sequence's length, but is at an equivalent position in the looped sequence as loopedTimeStamp
*
* When the music sequence is being looped by an MIKMIDIPlayer, the time stamp of the player continuosly increases. This method can be
* used to find where in the MIDI sequence the looped playback is at. For example, in a sequence with a length of 16,
* calling this method with a loopedTimeStamp of 17 would return 1.
*
* @param loopedTimeStamp The time stamp that you would like an equivalent time stamp for.
*
* @return The MusicTimeStamp of the sequence that is in an equivalent position in the sequence as loopedTimeStamp.
*/
- (MusicTimeStamp)equivalentTimeStampForLoopedTimeStamp:(MusicTimeStamp)loopedTimeStamp;
#pragma mark - Tempo & Time Signature

/**
* Returns an array of MIKMIDIEvent from the tempo track.
Expand Down Expand Up @@ -274,7 +265,22 @@ NS_INLINE MIKMIDITimeSignature MIKMIDITimeSignatureMake(UInt8 numerator, UInt8 d
*/
- (MIKMIDITimeSignature)timeSignatureAtTimeStamp:(MusicTimeStamp)timeStamp;

// Properties
#pragma mark - Timing

/**
* A MusicTimeStamp that is less than the sequence's length, but is at an equivalent position in the looped sequence as loopedTimeStamp
*
* When the music sequence is being looped by an MIKMIDIPlayer, the time stamp of the player continuosly increases. This method can be
* used to find where in the MIDI sequence the looped playback is at. For example, in a sequence with a length of 16,
* calling this method with a loopedTimeStamp of 17 would return 1.
*
* @param loopedTimeStamp The time stamp that you would like an equivalent time stamp for.
*
* @return The MusicTimeStamp of the sequence that is in an equivalent position in the sequence as loopedTimeStamp.
*/
- (MusicTimeStamp)equivalentTimeStampForLoopedTimeStamp:(MusicTimeStamp)loopedTimeStamp;

#pragma mark - Properties

/**
* The tempo track for the sequence. Even in a new, empty sequence,
Expand Down
55 changes: 43 additions & 12 deletions Source/MIKMIDISequence.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
@interface MIKMIDISequence ()

@property (nonatomic) MusicSequence musicSequence;
@property (strong, nonatomic) MIKMIDITrack *tempoTrack;
@property (strong, nonatomic) NSArray *tracks;
@property (nonatomic, strong) MIKMIDITrack *tempoTrack;
@property (nonatomic, strong) NSMutableArray *internalTracks;

@end

Expand Down Expand Up @@ -155,7 +155,7 @@ - (instancetype)initWithMusicSequence:(MusicSequence)musicSequence error:(NSErro
}
[tracks addObject:[MIKMIDITrack trackWithSequence:self musicTrack:musicTrack]];
}
self.tracks = tracks;
self.internalTracks = tracks;
self.length = MIKMIDISequenceLongestTrackLength;
}

Expand All @@ -181,27 +181,22 @@ - (MIKMIDITrack *)addTrack
}

MIKMIDITrack *track = [MIKMIDITrack trackWithSequence:self musicTrack:musicTrack];

if (track) {
NSMutableArray *tracks = [self.tracks mutableCopy];
[tracks addObject:track];
self.tracks = tracks;
}
[self addTracksObject:track];

return track;
}

- (BOOL)removeTrack:(MIKMIDITrack *)track
{
if (!track) return NO;

OSStatus err = MusicSequenceDisposeTrack(self.musicSequence, track.musicTrack);
if (err) {
NSLog(@"MusicSequenceDisposeTrack() failed with error %d in %s.", err, __PRETTY_FUNCTION__);
return NO;
}

NSMutableArray *tracks = [self.tracks mutableCopy];
[tracks removeObject:track];
self.tracks = tracks;
[self removeTracksObject:track];

return YES;
}
Expand Down Expand Up @@ -324,6 +319,42 @@ - (NSString *)description

#pragma mark - Properties

+ (NSSet *)keyPathsForValuesAffectingTracks
{
return [NSSet setWithObjects:@"internalTracks", nil];
}

- (NSArray *)tracks
{
return [self.internalTracks copy];
}

- (void)addTracksObject:(MIKMIDITrack *)track
{
if (!track) return;
[self insertObject:track inTracksAtIndex:[self.internalTracks count]];
}

- (void)insertObject:(MIKMIDITrack *)track inTracksAtIndex:(NSUInteger)index
{
if (!track) return;
[self.internalTracks insertObject:track atIndex:index];
}

- (void)removeTracksObject:(MIKMIDITrack *)track
{
if (!track) return;
NSInteger index = [self.internalTracks indexOfObject:track];
if (index == NSNotFound) return;
[self removeObjectFromInternalTracksAtIndex:index];
}

- (void)removeObjectFromInternalTracksAtIndex:(NSUInteger)index
{
if (index >= [self.internalTracks count]) return;
[self.internalTracks removeObjectAtIndex:index];
}

- (MusicTimeStamp)length
{
if (_length != MIKMIDISequenceLongestTrackLength) return _length;
Expand Down

0 comments on commit 4febc1b

Please sign in to comment.