// 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.

#import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h"

#import <Cocoa/Cocoa.h>

#include "chrome/test/testing_profile.h"
#include "content/browser/renderer_host/mock_render_process_host.h"
#include "content/browser/renderer_host/render_widget_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"

class RWHVMEditCommandHelperTest : public PlatformTest {
};

// Bare bones obj-c class for testing purposes.
@interface RWHVMEditCommandHelperTestClass : NSObject
@end

@implementation RWHVMEditCommandHelperTestClass
@end

// Class that owns a RenderWidgetHostViewMac.
@interface RenderWidgetHostViewMacOwner :
    NSObject<RenderWidgetHostViewMacOwner> {
  RenderWidgetHostViewMac* rwhvm_;
}

- (id) initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)rwhvm;
@end

@implementation RenderWidgetHostViewMacOwner

- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)rwhvm {
  if ((self = [super init])) {
    rwhvm_ = rwhvm;
  }
  return self;
}

- (RenderWidgetHostViewMac*)renderWidgetHostViewMac {
  return rwhvm_;
}

@end


namespace {
  // Returns true if all the edit command names in the array are present
  // in test_obj.
  // edit_commands is a list of NSStrings, selector names are formed by
  // appending a trailing ':' to the string.
  bool CheckObjectRespondsToEditCommands(NSArray* edit_commands, id test_obj) {
    for (NSString* edit_command_name in edit_commands) {
      NSString* sel_str = [edit_command_name stringByAppendingString:@":"];
      if (![test_obj respondsToSelector:NSSelectorFromString(sel_str)]) {
        return false;
      }
    }

    return true;
  }
}  // namespace

// Create a Mock RenderWidget
class MockRenderWidgetHostEditCommandCounter : public RenderWidgetHost {
 public:
  MockRenderWidgetHostEditCommandCounter(RenderProcessHost* process,
                                         int routing_id) :
    RenderWidgetHost(process, routing_id) {}

  MOCK_METHOD2(ForwardEditCommand, void(const std::string&,
      const std::string&));
};


// Tests that editing commands make it through the pipeline all the way to
// RenderWidgetHost.
TEST_F(RWHVMEditCommandHelperTest, TestEditingCommandDelivery) {
  RWHVMEditCommandHelper helper;
  NSArray* edit_command_strings = helper.GetEditSelectorNames();

  // Set up a mock render widget and set expectations.
  MessageLoopForUI message_loop;
  TestingProfile profile;
  MockRenderProcessHost mock_process(&profile);
  MockRenderWidgetHostEditCommandCounter mock_render_widget(&mock_process, 0);

  size_t num_edit_commands = [edit_command_strings count];
  EXPECT_CALL(mock_render_widget,
        ForwardEditCommand(testing::_, testing::_)).Times(num_edit_commands);

// TODO(jeremy): Figure this out and reenable this test.
// For some bizarre reason this code doesn't work, running the code in the
// debugger confirms that the function is called with the correct parameters
// however gmock appears not to be able to match up parameters correctly.
// Disable for now till we can figure this out.
#if 0
  // Tell Mock object that we expect to recieve each edit command once.
  std::string empty_str;
  for (NSString* edit_command_name in edit_command_strings) {
    std::string command([edit_command_name UTF8String]);
    EXPECT_CALL(mock_render_widget,
        ForwardEditCommand(command, empty_str)).Times(1);
  }
#endif  // 0

  // RenderWidgetHostViewMac self destructs (RenderWidgetHostViewMacCocoa
  // takes ownership) so no need to delete it ourselves.
  RenderWidgetHostViewMac* rwhvm = new RenderWidgetHostViewMac(
      &mock_render_widget);

  RenderWidgetHostViewMacOwner* rwhwvm_owner =
      [[[RenderWidgetHostViewMacOwner alloc]
          initWithRenderWidgetHostViewMac:rwhvm] autorelease];

  helper.AddEditingSelectorsToClass([rwhwvm_owner class]);

  for (NSString* edit_command_name in edit_command_strings) {
    NSString* sel_str = [edit_command_name stringByAppendingString:@":"];
    [rwhwvm_owner performSelector:NSSelectorFromString(sel_str) withObject:nil];
  }
}

// Test RWHVMEditCommandHelper::AddEditingSelectorsToClass
TEST_F(RWHVMEditCommandHelperTest, TestAddEditingSelectorsToClass) {
  RWHVMEditCommandHelper helper;
  NSArray* edit_command_strings = helper.GetEditSelectorNames();
  ASSERT_GT([edit_command_strings count], 0U);

  // Create a class instance and add methods to the class.
  RWHVMEditCommandHelperTestClass* test_obj =
      [[[RWHVMEditCommandHelperTestClass alloc] init] autorelease];

  // Check that edit commands aren't already attached to the object.
  ASSERT_FALSE(CheckObjectRespondsToEditCommands(edit_command_strings,
      test_obj));

  helper.AddEditingSelectorsToClass([test_obj class]);

  // Check that all edit commands where added.
  ASSERT_TRUE(CheckObjectRespondsToEditCommands(edit_command_strings,
      test_obj));

  // AddEditingSelectorsToClass() should be idempotent.
  helper.AddEditingSelectorsToClass([test_obj class]);

  // Check that all edit commands are still there.
  ASSERT_TRUE(CheckObjectRespondsToEditCommands(edit_command_strings,
      test_obj));
}

// Test RWHVMEditCommandHelper::IsMenuItemEnabled.
TEST_F(RWHVMEditCommandHelperTest, TestMenuItemEnabling) {
  RWHVMEditCommandHelper helper;
  RenderWidgetHostViewMacOwner* rwhvm_owner =
      [[[RenderWidgetHostViewMacOwner alloc] init] autorelease];

  // The select all menu should always be enabled.
  SEL select_all = NSSelectorFromString(@"selectAll:");
  ASSERT_TRUE(helper.IsMenuItemEnabled(select_all, rwhvm_owner));

  // Random selectors should be enabled by the function.
  SEL garbage_selector = NSSelectorFromString(@"randomGarbageSelector:");
  ASSERT_FALSE(helper.IsMenuItemEnabled(garbage_selector, rwhvm_owner));

  // TODO(jeremy): Currently IsMenuItemEnabled just returns true for all edit
  // selectors.  Once we go past that we should do more extensive testing here.
}