0

I have written a small terminal application with objective-c on Mac that uses getoptlong to parse the command line arguments. The code that parses the command line is the following:

+ (void) parseCommandLine:(int) argc
                     args:(char **) argv
            outSocketPath:(NSURL **) socketPath
          outSocketHandle:(int *) socketHandle
          outTestModePath:(NSURL **) testModePath {

    *socketPath = nil;
    *testModePath = nil; 
    socketHandle = 0;

    // are we running with launchd, the default is true
    BOOL noLaunchdFlag = NO;
    struct sockaddr_un un;

    // description of the available options of the daemon
    static struct option longopts[] = {
        { "no-launchd", no_argument, NULL, 'l' },
        { "socket", required_argument, NULL, 's' },
        { "testing", required_argument, NULL, 't'},
        { "help", no_argument, NULL, 'h'}
    };

    // parse the options and decide how to move
    int ch;
    while ((ch = getopt_long(argc, argv, "lst:h", longopts, NULL)) != -1){
        switch (ch) {
            case 'l':
                noLaunchdFlag = YES;
                break;
            case 's':
                *socketPath = [NSURL fileURLWithPath: [NSString 
                    stringWithCString:optarg encoding:NSUTF8StringEncoding]];
                break;
            case 't':
                *testModePath = [NSURL fileURLWithPath: [NSString 
                    stringWithCString:optarg encoding:NSUTF8StringEncoding]];
            case 'h':
            default:
                return [FSEventsDaemon usage];
        } // switch
    } // while looping through args
    // assert the parameters
    if(noLaunchdFlag) {
        if(*socketPath == nil){
            // we always need to have a socket path
            [NSException raise:@"Invalid Parameter" format:@""];
        }
        if([[*socketPath absoluteString] length] > sizeof(un.sun_path) - 1) {
            // socket path is too long    
            [NSException raise:@"Invalid Parameter" format:@""];
        }
        if(*testModePath == nil && geteuid() != 0){
            // if we are not running in test mode we must be root
            [NSException raise:@"" format:@""];
        }
        // all args are ok
        return;
    }
    else {
        if(testModePath != nil) {
            [NSException raise:@"Invalid Paramter" 
                format:@"You cannot use -t because testing is not supported via launchd"];
        }
        // get the handle
        *socketHandle = [FSEventsDaemon getLauchdSocketHandle];
        // all args are ok
        return;
    }
}

Having done that I'd like to use unit tests to assert that the operation is correctly done. I have for example written the following test:

- (void) testParseArgsNotTestNotRoot {
    char * argv[] = {"./FsEvents", "--socket=", [self getPath], "-l"};
    int argc = 4;
    NSURL *sockPath = nil;
    int handle;
    NSURL *testPath = nil;
    // should raise an exception because we are not root
    STAssertThrows([FSEventsDaemon parseCommandLine:argc args:argv 
        outSocketPath:&sockPath outSocketHandle:&handle 
        outTestModePath:&testPath], nil);
}

When running the test via the debugger the getoptlong args parsing never gets the path. Is this the correct way to test the usage of getoptlong? Is there a better pattern used for testing the parsing of the command line arguments?

mandel
  • 2,921
  • 3
  • 23
  • 27
  • 1
    Ditch getopt() and use DDCLI.... seriously. – bbum Aug 13 '12 at 16:40
  • I have been considering a lot recently. Is there a better way to add it to a project that is not copying the source code? – mandel Aug 13 '12 at 17:01
  • Just copy the source and stick it in a folder. There isn't that much there (though it is very useful) and Dave isn't modifying it actively (because it is mostly complete as is). I've been using it extensively for years and have never had a problem. The one project that was old enough that I needed to update, I just copied the source in... – bbum Aug 13 '12 at 17:58

0 Answers0