
#import "ObjectTesting.h"
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSRegularExpression.h>
#import <Foundation/NSTextCheckingResult.h>
#import <Foundation/NSDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSRunLoop.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSError.h>
#import <Foundation/FoundationErrors.h>

@interface DegeneratePatternTest : NSObject
{
  NSRegularExpression *expression;
  NSString* input;
}
@end

@implementation DegeneratePatternTest

- (instancetype) init
{
  if (nil == (self = [super init]))
    {
      return nil;
    }
  expression =
    [[NSRegularExpression alloc] initWithPattern: @"^(([a-z])+.)+[A-Z]([a-z])+$"
                                         options: 0
                                          error: NULL];
  ASSIGN(input, @"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!");
  return self;
}

- (void) runTest: (id)obj
{
  NSAutoreleasePool *pool = [NSAutoreleasePool new];
  [expression matchesInString: input
                      options: 0
                        range: NSMakeRange(0, [input length])];
  DESTROY(pool);
}

- (void) dealloc
{
  DESTROY(expression);
  DESTROY(input);
  [super dealloc];
}
@end


int main()
{
  NSAutoreleasePool   *arp = [NSAutoreleasePool new];
  START_SET("NSRegularExpression")

#if !(__APPLE__ || GS_USE_ICU)
  SKIP("NSRegularExpression not built\nThe ICU library was not available when GNUstep-base was built")
#else
# ifdef GNUSTEP
    // Ensure that a deterministic limit is set up for this process
    NSUserDefaults *dflts = [NSUserDefaults standardUserDefaults];
    NSDictionary *domain = [NSDictionary dictionaryWithObjectsAndKeys:
     [NSNumber numberWithInt: 1500], @"GSRegularExpressionWorkLimit", nil];
    [dflts setVolatileDomain: domain
                     forName: @"GSTestDomain"];
# endif
  id testObj = AUTORELEASE([[NSRegularExpression alloc]
    initWithPattern: @"^a" options: 0 error: NULL]);

  test_NSObject(@"NSRegularExpression",
    [NSArray arrayWithObject: AUTORELEASE([[NSRegularExpression
      alloc] initWithPattern: @"^a" options: 0 error: NULL])]);
  test_NSCopying(@"NSRegularExpression",@"NSRegularExpression",
                 [NSArray arrayWithObject:testObj],NO,NO);

  NSAutoreleasePool *iPool = [NSAutoreleasePool new];
    {
      PASS_EQUAL([testObj pattern], @"^a", "Correct pattern returned");
    }
  DESTROY(iPool);
  /* Regression test: We had a double-free bug on retrieving pattern,
   * which is also the reason for wrapping the previous one in an ARP */
  PASS_RUNS([testObj pattern], "Calling -pattern twice runs");

  // The pattern does not include a capture group $1, so this should return 
  // nil;
  NSString *replacement = @"should be unset on return";
  replacement =  [testObj stringByReplacingMatchesInString: @"ab"
                                                   options: 0
                                                     range: NSMakeRange(0,2)
                                              withTemplate: @"$1c"];
  PASS(replacement == nil, "Replacement: returns nil on capture group error");
  replacement = [testObj stringByReplacingMatchesInString: @"ab"
                                                  options: 0
                                                    range: NSMakeRange(0, 2)
                                             withTemplate: @"c"];
  PASS_EQUAL(replacement, @"cb", "Replacement correct");

  NSMutableString *replMut = [NSMutableString stringWithString: @"ab"];
  [testObj replaceMatchesInString: replMut
                          options: 0
                            range: NSMakeRange(0,2)
                     withTemplate: @"$1c"];
  PASS_EQUAL(replMut, @"ab", 
             "Mutable replacement: Incorrect template does not change string");
  replMut = [NSMutableString stringWithString: @"ab"];
  [testObj replaceMatchesInString: replMut
                          options: 0
                            range: NSMakeRange(0,2)
                     withTemplate: @"c"];
  PASS_EQUAL(replMut, @"cb",
             "Mutable replacement: Correct replacement for template");

  NSTextCheckingResult *r = [testObj firstMatchInString: @"ab"
                                                options: 0
                                                  range: NSMakeRange(0,2)];
  PASS(r != nil, "Found NSTextCheckingResult");
  replacement = @"should be unset on return";
  replacement = [testObj replacementStringForResult: r
                                           inString: @"ab"
                                             offset: 0
                                           template: @"$1c"];
  PASS(replacement == nil,
       "Custom replacement: returns nil on capture group error");
  replacement = nil;
  replacement = [testObj replacementStringForResult: r
                                           inString: @"ab"
                                             offset: 0
                                           template: @"c"];
  PASS_EQUAL(replacement, @"c",
             "Custom replacement: Returns correct replacement");

  NSRegularExpression *testObj2 = AUTORELEASE([[NSRegularExpression alloc]
    initWithPattern: @"bc" options: 0 error: NULL]);
  r = [testObj2 firstMatchInString: @"abcdeabcde"
			   options: 0
			     range: NSMakeRange(5, 5)];
  PASS(r != nil && NSEqualRanges([r range], NSMakeRange(6, 2)),
       "Restricting the range for firstMatchInString: works");

  /* To test whether we correctly bail out of processing degenerate patterns,
   * we spin up a new thread and evaluate an expression there. The expectation
   * is that the thread should terminate within a few seconds.
   *
   * NOTE: Since we cannot terminate the thread in case of a failure, this
   * test should be run last.
   */
  DegeneratePatternTest *test = [DegeneratePatternTest new];
  NSThread *thread = [[NSThread alloc] initWithTarget: test
                                              selector: @selector(runTest:)
                                                object: nil];
  [thread start];
  [thread setName: @"PatternTestRunner"];
  NSDate *started = [NSDate date];
  NSRunLoop *rl = [NSRunLoop currentRunLoop];
  /* We spin the runloop for a bit while we wait for the other thread to bail
   * out */
  while ([thread isExecuting] && abs([started timeIntervalSinceNow] < 10.0f))
    {
      [rl runMode: NSDefaultRunLoopMode
       beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
    }
  PASS(NO == [thread isExecuting], "Faulty regular expression terminated");

  /* Testing the error handling when an invalid pattern is passed
   * The error should look like this:
   *
   * Error Domain=NSCocoaErrorDomain Code=2048 "The value “(abc” is
   * invalid." UserInfo={NSInvalidValue=(abc}
   */

  NSError *error = nil;
  NSRegularExpression *testObj3 =
    [[NSRegularExpression alloc] initWithPattern: @"(abc"
           options: 0
             error: &error];
  PASS(testObj3 == nil, "Invalid pattern: returns nil");
  PASS(error != nil, "Invalid pattern: error is set");
  PASS_EQUAL([error domain], NSCocoaErrorDomain,
             "Invalid pattern: error domain is NSCocoaErrorDomain");
  PASS_EQUAL([error code], NSFormattingError, 
             "Invalid pattern: error code is NSFormattingError");
  PASS_EQUAL([[error userInfo] objectForKey: @"NSInvalidValue"],
             @"(abc", "Invalid pattern: error message is correct");
  PASS_EQUAL([[error userInfo] objectForKey: NSLocalizedDescriptionKey],
             @"The value “(abc” is invalid.",
             "Invalid pattern: localized description is correct");

  /* Testing exception handling when nil is passed as pattern */
  PASS_EXCEPTION([NSRegularExpression regularExpressionWithPattern: nil
                                                           options: 0
                                                             error: NULL],
                 NSInvalidArgumentException,
                 "nil pattern: throws NSInvalidArgumentException");

#endif

  END_SET("NSRegularExpression")
  [arp release]; arp = nil;
  return 0;
}
