Implement negative support, tests.
This commit is contained in:
parent
82098b044c
commit
a280f3f69c
3 changed files with 202 additions and 30 deletions
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1240"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "3D42D2732642BEA0002A170C"
|
||||||
|
BuildableName = "SecondsFormatterTests.xctest"
|
||||||
|
BlueprintName = "SecondsFormatterTests"
|
||||||
|
ReferencedContainer = "container:Cog.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -34,64 +34,129 @@
|
||||||
if (isnan(floatValue)) { return @"NaN"; }
|
if (isnan(floatValue)) { return @"NaN"; }
|
||||||
if (isinf(floatValue)) { return @"Inf"; }
|
if (isinf(floatValue)) { return @"Inf"; }
|
||||||
|
|
||||||
int totalSeconds = (int)floatValue;
|
BOOL isNegative = floatValue < 0;
|
||||||
|
|
||||||
|
int totalSeconds = (int)(isNegative ? -floatValue : floatValue);
|
||||||
|
|
||||||
int seconds = totalSeconds % 60;
|
int seconds = totalSeconds % 60;
|
||||||
int minutes = totalSeconds / 60;
|
int minutes = totalSeconds / 60;
|
||||||
int hours = 0;
|
int hours = 0;
|
||||||
int days = 0;
|
int days = 0;
|
||||||
|
|
||||||
while(60 <= minutes) {
|
while (60 <= minutes) {
|
||||||
minutes -= 60;
|
minutes -= 60;
|
||||||
++hours;
|
++hours;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(24 <= hours) {
|
while (24 <= hours) {
|
||||||
hours -= 24;
|
hours -= 24;
|
||||||
++days;
|
++days;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *result = nil;
|
NSString *result = nil;
|
||||||
|
|
||||||
if(0 < days) {
|
const char *signPrefix = isNegative ? "-" : "";
|
||||||
result = [NSString stringWithFormat:@"%i:%.2i:%.2i:%.2i", days, hours, minutes, seconds];
|
|
||||||
|
if (0 < days) {
|
||||||
|
result = [NSString stringWithFormat:@"%s%i:%.2i:%.2i:%.2i", signPrefix, days, hours, minutes, seconds];
|
||||||
}
|
}
|
||||||
else if(0 < hours) {
|
else if (0 < hours) {
|
||||||
result = [NSString stringWithFormat:@"%i:%.2i:%.2i", hours, minutes, seconds];
|
result = [NSString stringWithFormat:@"%s%i:%.2i:%.2i", signPrefix, hours, minutes, seconds];
|
||||||
}
|
}
|
||||||
else if(0 < minutes) {
|
else if (0 < minutes) {
|
||||||
result = [NSString stringWithFormat:@"%i:%.2i", minutes, seconds];
|
result = [NSString stringWithFormat:@"%s%i:%.2i", signPrefix, minutes, seconds];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = [NSString stringWithFormat:@"0:%.2i", seconds];
|
result = [NSString stringWithFormat:@"%s0:%.2i", signPrefix, seconds];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)getObjectValue:(out id _Nullable __autoreleasing *)object forString:(NSString *)string errorDescription:(out NSString * _Nullable __autoreleasing *)error
|
- (BOOL)getObjectValue:(out id _Nullable __autoreleasing *)object
|
||||||
|
forString:(NSString *)string
|
||||||
|
errorDescription:(out NSString * _Nullable __autoreleasing *)error
|
||||||
{
|
{
|
||||||
NSScanner *scanner = nil;
|
NSScanner *scanner = [NSScanner scannerWithString:string];
|
||||||
BOOL result = NO;
|
|
||||||
int value = 0;
|
|
||||||
int seconds = 0;
|
|
||||||
|
|
||||||
scanner = [NSScanner scannerWithString:string];
|
BOOL result = NO;
|
||||||
|
|
||||||
while(NO == [scanner isAtEnd]) {
|
const int segmentCount = 4;
|
||||||
|
const int lastSegment = segmentCount - 1;
|
||||||
|
int segments[segmentCount] = {-1, -1, -1, -1};
|
||||||
|
int lastScannedSegment = -1;
|
||||||
|
|
||||||
|
BOOL isNegative = NO;
|
||||||
|
|
||||||
|
if ([scanner isAtEnd] == NO) {
|
||||||
|
isNegative = [scanner scanString:@"-" intoString:NULL];
|
||||||
|
|
||||||
// Grab a value
|
int segmentIndex = 0;
|
||||||
if([scanner scanInt:&value]) {
|
|
||||||
seconds *= 60;
|
while (NO == [scanner isAtEnd]) {
|
||||||
seconds += value;
|
// Grab a value
|
||||||
result = YES;
|
if ([scanner scanInt:&(segments[segmentIndex])] == NO) {
|
||||||
|
segments[segmentIndex] = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segmentIndex == lastSegment) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the separator, if present
|
||||||
|
if ([scanner scanString:@":" intoString:NULL] == NO) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentIndex += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the separator, if present
|
lastScannedSegment = segmentIndex;
|
||||||
[scanner scanString:@":" intoString:NULL];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result && NULL != object) {
|
int seconds = 0;
|
||||||
|
|
||||||
|
const BOOL hasDaysSegment = (lastScannedSegment == 3);
|
||||||
|
const BOOL hasHoursSegment = (lastScannedSegment >= 2);
|
||||||
|
|
||||||
|
for (int i = 0; i <= lastScannedSegment; i += 1) {
|
||||||
|
if (segments[i] < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDaysSegment &&
|
||||||
|
(i == 1)) {
|
||||||
|
// Special case for days.
|
||||||
|
seconds *= 24;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
seconds *= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BOOL isDaysSegment = (hasDaysSegment && (i == 0));
|
||||||
|
const BOOL isHoursSegment = (hasHoursSegment && (((lastScannedSegment == 3) && (i == 1)) || ((lastScannedSegment == 2) && (i == 0))));
|
||||||
|
|
||||||
|
if (isDaysSegment ||
|
||||||
|
((isDaysSegment == NO) &&
|
||||||
|
((isHoursSegment && (segments[i] < 24)) ||
|
||||||
|
((isHoursSegment == NO) &&
|
||||||
|
(segments[i] < 60))))) {
|
||||||
|
seconds += segments[i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = NO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
result = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNegative) { seconds *= -1; }
|
||||||
|
|
||||||
|
if (result && NULL != object) {
|
||||||
*object = [NSNumber numberWithInt:seconds];
|
*object = [NSNumber numberWithInt:seconds];
|
||||||
}
|
}
|
||||||
else if(NULL != error) {
|
else if(NULL != error) {
|
||||||
|
|
|
@ -27,11 +27,6 @@
|
||||||
[super tearDown];
|
[super tearDown];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testExample {
|
|
||||||
// This is an example of a functional test case.
|
|
||||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testPositive
|
- (void)testPositive
|
||||||
{
|
{
|
||||||
NSDictionary *testsDict =
|
NSDictionary *testsDict =
|
||||||
|
@ -62,6 +57,66 @@
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testNegative
|
||||||
|
{
|
||||||
|
NSDictionary *testsDict =
|
||||||
|
@{
|
||||||
|
// key: test name, value: test string
|
||||||
|
@"Negative One Second": @"-0:01",
|
||||||
|
@"Negative One Minute": @"-1:00",
|
||||||
|
@"Negative One Hour": @"-1:00:00",
|
||||||
|
@"Negative One Day": @"-1:00:00:00",
|
||||||
|
@"Negative One of Each": @"-1:01:01:01",
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TEST_INFO @"Test name: %@, Source string: %@", testName, string
|
||||||
|
|
||||||
|
NSFormatter *secondsFormatter = [[SecondsFormatter alloc] init];
|
||||||
|
|
||||||
|
[testsDict enumerateKeysAndObjectsUsingBlock:
|
||||||
|
^(NSString *testName, NSString *string, BOOL * _Nonnull stop) {
|
||||||
|
NSNumber *value;
|
||||||
|
BOOL result =
|
||||||
|
[secondsFormatter getObjectValue:&value
|
||||||
|
forString:string
|
||||||
|
errorDescription:NULL];
|
||||||
|
XCTAssertTrue(result, TEST_INFO);
|
||||||
|
NSString *timeString = [secondsFormatter stringForObjectValue:value];
|
||||||
|
XCTAssertEqualObjects(string, timeString, TEST_INFO);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testMalformed
|
||||||
|
{
|
||||||
|
NSDictionary *testsDict =
|
||||||
|
@{
|
||||||
|
// key: test name, value: test string
|
||||||
|
@"Empty String": @"",
|
||||||
|
@"Random String": @"abc",
|
||||||
|
@"Solitary Minus": @"-",
|
||||||
|
@"Malformed Seconds": @"0:60",
|
||||||
|
@"Malformed Minutes": @"60:00",
|
||||||
|
@"Malformed Hours": @"24:00:00",
|
||||||
|
@"Illegal #1": @":00",
|
||||||
|
@"Illegal #2": @"-:00",
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TEST_INFO @"Test name: %@, Source string: %@", testName, string
|
||||||
|
|
||||||
|
NSFormatter *secondsFormatter = [[SecondsFormatter alloc] init];
|
||||||
|
|
||||||
|
[testsDict enumerateKeysAndObjectsUsingBlock:
|
||||||
|
^(NSString *testName, NSString *string, BOOL * _Nonnull stop) {
|
||||||
|
NSNumber *value;
|
||||||
|
BOOL result =
|
||||||
|
[secondsFormatter getObjectValue:&value
|
||||||
|
forString:string
|
||||||
|
errorDescription:NULL];
|
||||||
|
XCTAssertFalse(result, TEST_INFO);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
- (void)testPerformanceExample
|
- (void)testPerformanceExample
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue