// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/child_process_logging.h"

#import <Foundation/Foundation.h>

#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"

typedef PlatformTest ChildProcessLoggingTest;

namespace {

// Class to mock breakpad's setkeyvalue/clearkeyvalue functions needed for
// SetActiveRendererURLImpl.
// The Keys are stored in a static dictionary and methods are provided to
// verify correctness.
class MockBreakpadKeyValueStore {
 public:
  MockBreakpadKeyValueStore() {
    // Only one of these objects can be active at once.
    DCHECK(dict == NULL);
    dict = [[NSMutableDictionary alloc] init];
  }

  ~MockBreakpadKeyValueStore() {
    // Only one of these objects can be active at once.
    DCHECK(dict != NULL);
    [dict release];
    dict = NULL;
  }

  static void SetKeyValue(NSString* key, NSString* value) {
    DCHECK(dict != NULL);
    [dict setObject:value forKey:key];
  }

  static void ClearKeyValue(NSString *key) {
    DCHECK(dict != NULL);
    [dict removeObjectForKey:key];
  }

  int CountDictionaryEntries() {
    return [dict count];
  }

  bool VerifyDictionaryContents(const std::string &url) {
    using child_process_logging::kMaxNumCrashURLChunks;
    using child_process_logging::kMaxNumURLChunkValueLength;
    using child_process_logging::kUrlChunkFormatStr;

    int num_url_chunks = CountDictionaryEntries();
    EXPECT_TRUE(num_url_chunks <= kMaxNumCrashURLChunks);

    NSString *kUrlChunkFormatStr_utf8 = [NSString
        stringWithUTF8String:kUrlChunkFormatStr];

    NSString *accumulated_url = @"";
    for (int i = 0; i < num_url_chunks; ++i) {
      // URL chunk names are 1-based.
      NSString *key = [NSString stringWithFormat:kUrlChunkFormatStr_utf8, i+1];
      EXPECT_TRUE(key != NULL);
      NSString *value = [dict objectForKey:key];
      EXPECT_TRUE([value length] > 0);
      EXPECT_TRUE([value length] <= (unsigned)kMaxNumURLChunkValueLength);
      accumulated_url = [accumulated_url stringByAppendingString:value];
    }

    NSString *expected_url = [NSString stringWithUTF8String:url.c_str()];
    return([accumulated_url isEqualToString:expected_url]);
  }

 private:
  static NSMutableDictionary* dict;
  DISALLOW_COPY_AND_ASSIGN(MockBreakpadKeyValueStore);
};

// static
NSMutableDictionary* MockBreakpadKeyValueStore::dict;

}  // namespace

// Call through to SetActiveURLImpl using the functions from
// MockBreakpadKeyValueStore.
void SetActiveURLWithMock(const GURL& url) {
  using child_process_logging::SetActiveURLImpl;

  SetCrashKeyValueFuncPtr setFunc = MockBreakpadKeyValueStore::SetKeyValue;
  ClearCrashKeyValueFuncPtr clearFunc =
      MockBreakpadKeyValueStore::ClearKeyValue;

  SetActiveURLImpl(url, setFunc, clearFunc);
}

TEST_F(ChildProcessLoggingTest, TestUrlSplitting) {
  using child_process_logging::kMaxNumCrashURLChunks;
  using child_process_logging::kMaxNumURLChunkValueLength;

  const std::string short_url("http://abc/");
  std::string long_url("http://");
  std::string overflow_url("http://");

  long_url += std::string(kMaxNumURLChunkValueLength * 2, 'a');
  long_url += "/";

  int max_num_chars_stored_in_dump = kMaxNumURLChunkValueLength *
      kMaxNumCrashURLChunks;
  overflow_url += std::string(max_num_chars_stored_in_dump + 1, 'a');
  overflow_url += "/";

  // Check that Clearing NULL URL works.
  MockBreakpadKeyValueStore mock;
  SetActiveURLWithMock(GURL());
  EXPECT_EQ(mock.CountDictionaryEntries(), 0);

  // Check that we can set a URL.
  SetActiveURLWithMock(GURL(short_url.c_str()));
  EXPECT_TRUE(mock.VerifyDictionaryContents(short_url));
  EXPECT_EQ(mock.CountDictionaryEntries(), 1);
  SetActiveURLWithMock(GURL());
  EXPECT_EQ(mock.CountDictionaryEntries(), 0);

  // Check that we can replace a long url with a short url.
  SetActiveURLWithMock(GURL(long_url.c_str()));
  EXPECT_TRUE(mock.VerifyDictionaryContents(long_url));
  SetActiveURLWithMock(GURL(short_url.c_str()));
  EXPECT_TRUE(mock.VerifyDictionaryContents(short_url));
  SetActiveURLWithMock(GURL());
  EXPECT_EQ(mock.CountDictionaryEntries(), 0);


  // Check that overflow works correctly.
  SetActiveURLWithMock(GURL(overflow_url.c_str()));
  EXPECT_TRUE(mock.VerifyDictionaryContents(
      overflow_url.substr(0, max_num_chars_stored_in_dump)));
  SetActiveURLWithMock(GURL());
  EXPECT_EQ(mock.CountDictionaryEntries(), 0);
}