// Protocol Buffers - Google's data interchange format
// Copyright 2013 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#import "GPBTestUtilities.h"
#import "objectivec/Tests/Unittest.pbobjc.h"
#import "objectivec/Tests/UnittestImport.pbobjc.h"
#import "objectivec/Tests/UnittestObjc.pbobjc.h"

//
// This file really just uses the unittests framework as a testbed to
// run some simple performance tests. The data can then be used to help
// evaluate changes to the runtime.
//

static const uint32_t kRepeatedCount = 100;

@interface PerfTests : GPBTestCase
@end

@implementation PerfTests

- (void)setUp {
  // A convenient place to put a break point if you want to connect instruments.
  [super setUp];
}

- (void)testMessagePerformance {
  [self measureBlock:^{
    for (int i = 0; i < 200; ++i) {
      TestAllTypes* message = [[TestAllTypes alloc] init];
      [self setAllFields:message repeatedCount:kRepeatedCount];
      NSData* rawBytes = [message data];
      [message release];
      message = [[TestAllTypes alloc] initWithData:rawBytes error:NULL];
      [message release];
    }
  }];
}

- (void)testMessageSerialParsingPerformance {
  // This and the next test are meant to monitor that the parsing functionality of protos does not
  // lock across threads when parsing different instances. The Serial version of the test should run
  // around ~2 times slower than the Parallel version since it's parsing the protos in the same
  // thread.
  TestAllTypes* allTypesMessage = [TestAllTypes message];
  [self setAllFields:allTypesMessage repeatedCount:2];
  NSData* allTypesData = allTypesMessage.data;

  [self measureBlock:^{
    for (int i = 0; i < 500; ++i) {
      [TestAllTypes parseFromData:allTypesData error:NULL];
      [TestAllTypes parseFromData:allTypesData error:NULL];
    }
  }];
}

- (void)testMessageParallelParsingPerformance {
  // This and the previous test are meant to monitor that the parsing functionality of protos does
  // not lock across threads when parsing different instances. The Serial version of the test should
  // run around ~2 times slower than the Parallel version since it's parsing the protos in the same
  // thread.
  TestAllTypes* allTypesMessage = [TestAllTypes message];
  [self setAllFields:allTypesMessage repeatedCount:2];
  NSData* allTypesData = allTypesMessage.data;

  dispatch_queue_t concurrentQueue = dispatch_queue_create("perfQueue", DISPATCH_QUEUE_CONCURRENT);

  [self measureBlock:^{
    for (int i = 0; i < 500; ++i) {
      dispatch_group_t group = dispatch_group_create();

      dispatch_group_async(group, concurrentQueue, ^{
        [TestAllTypes parseFromData:allTypesData error:NULL];
      });

      dispatch_group_async(group, concurrentQueue, ^{
        [TestAllTypes parseFromData:allTypesData error:NULL];
      });

      dispatch_group_notify(group, concurrentQueue,
                            ^{
                            });

      dispatch_release(group);
    }
  }];

  dispatch_release(concurrentQueue);
}

- (void)testMessageSerialExtensionsParsingPerformance {
  // This and the next test are meant to monitor that the parsing functionality of protos does not
  // lock across threads when parsing different instances when using extensions. The Serial version
  // of the test should run around ~2 times slower than the Parallel version since it's parsing the
  // protos in the same thread.
  TestAllExtensions* allExtensionsMessage = [TestAllExtensions message];
  [self setAllExtensions:allExtensionsMessage repeatedCount:2];
  NSData* allExtensionsData = allExtensionsMessage.data;

  [self measureBlock:^{
    for (int i = 0; i < 500; ++i) {
      [TestAllExtensions parseFromData:allExtensionsData
                     extensionRegistry:[self extensionRegistry]
                                 error:NULL];
      [TestAllExtensions parseFromData:allExtensionsData
                     extensionRegistry:[self extensionRegistry]
                                 error:NULL];
    }
  }];
}

- (void)testMessageParallelExtensionsParsingPerformance {
  // This and the previous test are meant to monitor that the parsing functionality of protos does
  // not lock across threads when parsing different instances when using extensions. The Serial
  // version of the test should run around ~2 times slower than the Parallel version since it's
  // parsing the protos in the same thread.
  TestAllExtensions* allExtensionsMessage = [TestAllExtensions message];
  [self setAllExtensions:allExtensionsMessage repeatedCount:2];
  NSData* allExtensionsData = allExtensionsMessage.data;

  dispatch_queue_t concurrentQueue = dispatch_queue_create("perfQueue", DISPATCH_QUEUE_CONCURRENT);

  [self measureBlock:^{
    for (int i = 0; i < 500; ++i) {
      dispatch_group_t group = dispatch_group_create();

      dispatch_group_async(group, concurrentQueue, ^{
        [TestAllExtensions parseFromData:allExtensionsData
                       extensionRegistry:[UnittestRoot extensionRegistry]
                                   error:NULL];
      });

      dispatch_group_async(group, concurrentQueue, ^{
        [TestAllExtensions parseFromData:allExtensionsData
                       extensionRegistry:[UnittestRoot extensionRegistry]
                                   error:NULL];
      });

      dispatch_group_notify(group, concurrentQueue,
                            ^{
                            });

      dispatch_release(group);
    }
  }];

  dispatch_release(concurrentQueue);
}

- (void)testExtensionsPerformance {
  [self measureBlock:^{
    for (int i = 0; i < 200; ++i) {
      TestAllExtensions* message = [[TestAllExtensions alloc] init];
      [self setAllExtensions:message repeatedCount:kRepeatedCount];
      NSData* rawBytes = [message data];
      [message release];
      TestAllExtensions* message2 = [[TestAllExtensions alloc] initWithData:rawBytes error:NULL];
      [message2 release];
    }
  }];
}

- (void)testPackedTypesPerformance {
  [self measureBlock:^{
    for (int i = 0; i < 1000; ++i) {
      TestPackedTypes* message = [[TestPackedTypes alloc] init];
      [self setPackedFields:message repeatedCount:kRepeatedCount];
      NSData* rawBytes = [message data];
      [message release];
      message = [[TestPackedTypes alloc] initWithData:rawBytes error:NULL];
      [message release];
    }
  }];
}

- (void)testPackedExtensionsPerformance {
  [self measureBlock:^{
    for (int i = 0; i < 1000; ++i) {
      TestPackedExtensions* message = [[TestPackedExtensions alloc] init];
      [self setPackedExtensions:message repeatedCount:kRepeatedCount];
      NSData* rawBytes = [message data];
      [message release];
      TestPackedExtensions* message2 = [[TestPackedExtensions alloc] initWithData:rawBytes
                                                                            error:NULL];
      [message2 release];
    }
  }];
}

- (void)testHas {
  TestAllTypes* message = [self allSetRepeatedCount:1];
  [self measureBlock:^{
    for (int i = 0; i < 10000; ++i) {
      [message hasOptionalInt32];
      message.hasOptionalInt32 = NO;
      [message hasOptionalInt32];

      [message hasOptionalInt64];
      message.hasOptionalInt64 = NO;
      [message hasOptionalInt64];

      [message hasOptionalUint32];
      message.hasOptionalUint32 = NO;
      [message hasOptionalUint32];

      [message hasOptionalUint64];
      message.hasOptionalUint64 = NO;
      [message hasOptionalUint64];

      [message hasOptionalSint32];
      message.hasOptionalSint32 = NO;
      [message hasOptionalSint32];

      [message hasOptionalSint64];
      message.hasOptionalSint64 = NO;
      [message hasOptionalSint64];

      [message hasOptionalFixed32];
      message.hasOptionalFixed32 = NO;
      [message hasOptionalFixed32];

      [message hasOptionalFixed64];
      message.hasOptionalFixed64 = NO;
      [message hasOptionalFixed64];

      [message hasOptionalSfixed32];
      message.hasOptionalSfixed32 = NO;
      [message hasOptionalSfixed32];

      [message hasOptionalSfixed64];
      message.hasOptionalSfixed64 = NO;
      [message hasOptionalSfixed64];

      [message hasOptionalFloat];
      message.hasOptionalFloat = NO;
      [message hasOptionalFloat];

      [message hasOptionalDouble];
      message.hasOptionalDouble = NO;
      [message hasOptionalDouble];

      [message hasOptionalBool];
      message.hasOptionalBool = NO;
      [message hasOptionalBool];

      [message hasOptionalString];
      message.hasOptionalString = NO;
      [message hasOptionalString];

      [message hasOptionalBytes];
      message.hasOptionalBytes = NO;
      [message hasOptionalBytes];

      [message hasOptionalGroup];
      message.hasOptionalGroup = NO;
      [message hasOptionalGroup];

      [message hasOptionalNestedMessage];
      message.hasOptionalNestedMessage = NO;
      [message hasOptionalNestedMessage];

      [message hasOptionalForeignMessage];
      message.hasOptionalForeignMessage = NO;
      [message hasOptionalForeignMessage];

      [message hasOptionalImportMessage];
      message.hasOptionalImportMessage = NO;
      [message hasOptionalImportMessage];

      [message.optionalGroup hasA];
      message.optionalGroup.hasA = NO;
      [message.optionalGroup hasA];

      [message.optionalNestedMessage hasBb];
      message.optionalNestedMessage.hasBb = NO;
      [message.optionalNestedMessage hasBb];

      [message.optionalForeignMessage hasC];
      message.optionalForeignMessage.hasC = NO;
      [message.optionalForeignMessage hasC];

      [message.optionalImportMessage hasD];
      message.optionalImportMessage.hasD = NO;
      [message.optionalImportMessage hasD];

      [message hasOptionalNestedEnum];
      message.hasOptionalNestedEnum = NO;
      [message hasOptionalNestedEnum];

      [message hasOptionalForeignEnum];
      message.hasOptionalForeignEnum = NO;
      [message hasOptionalForeignEnum];

      [message hasOptionalImportEnum];
      message.hasOptionalImportEnum = NO;
      [message hasOptionalImportEnum];

      [message hasOptionalStringPiece];
      message.hasOptionalStringPiece = NO;
      [message hasOptionalStringPiece];

      [message hasOptionalCord];
      message.hasOptionalCord = NO;
      [message hasOptionalCord];

      [message hasDefaultInt32];
      message.hasDefaultInt32 = NO;
      [message hasDefaultInt32];

      [message hasDefaultInt64];
      message.hasDefaultInt64 = NO;
      [message hasDefaultInt64];

      [message hasDefaultUint32];
      message.hasDefaultUint32 = NO;
      [message hasDefaultUint32];

      [message hasDefaultUint64];
      message.hasDefaultUint64 = NO;
      [message hasDefaultUint64];

      [message hasDefaultSint32];
      message.hasDefaultSint32 = NO;
      [message hasDefaultSint32];

      [message hasDefaultSint64];
      message.hasDefaultSint64 = NO;
      [message hasDefaultSint64];

      [message hasDefaultFixed32];
      message.hasDefaultFixed32 = NO;
      [message hasDefaultFixed32];

      [message hasDefaultFixed64];
      message.hasDefaultFixed64 = NO;
      [message hasDefaultFixed64];

      [message hasDefaultSfixed32];
      message.hasDefaultSfixed32 = NO;
      [message hasDefaultSfixed32];

      [message hasDefaultSfixed64];
      message.hasDefaultSfixed64 = NO;
      [message hasDefaultSfixed64];

      [message hasDefaultFloat];
      message.hasDefaultFloat = NO;
      [message hasDefaultFloat];

      [message hasDefaultDouble];
      message.hasDefaultDouble = NO;
      [message hasDefaultDouble];

      [message hasDefaultBool];
      message.hasDefaultBool = NO;
      [message hasDefaultBool];

      [message hasDefaultString];
      message.hasDefaultString = NO;
      [message hasDefaultString];

      [message hasDefaultBytes];
      message.hasDefaultBytes = NO;
      [message hasDefaultBytes];

      [message hasDefaultNestedEnum];
      message.hasDefaultNestedEnum = NO;
      [message hasDefaultNestedEnum];

      [message hasDefaultForeignEnum];
      message.hasDefaultForeignEnum = NO;
      [message hasDefaultForeignEnum];

      [message hasDefaultImportEnum];
      message.hasDefaultImportEnum = NO;
      [message hasDefaultImportEnum];

      [message hasDefaultStringPiece];
      message.hasDefaultStringPiece = NO;
      [message hasDefaultStringPiece];

      [message hasDefaultCord];
      message.hasDefaultCord = NO;
      [message hasDefaultCord];
    }
  }];
}

@end
