require 'osx/cocoa'
include OSX

require 'test/unit'
require 'shoulda'
require 'flexmock/test_unit'

# This file contains methods I find useful when using mocks. 

# These methods are used to change the flow of control so that the 
# test can state what is to happen before stating what the mock should
# receive.
module Test::Unit::MockTalk
  def because(&block)
    @because = block
    self
  end
  alias_method :during, :because
  alias_method :whenever, :because
  
  def behold!
    yield
    @because.call
  end
end


# Use this class when creating a mock that's to receive a notification.
# I don't know why you can't use NSObject, but you can't -- the notification
# will not be received.
class SomeRandomWatcher < OSX::NSObject
end

# Use this in a _with_ expectation.
def this_notification(name, object = nil, userInfo = nil)
  on { | notification |
    notification.name == name && 
    notification.object == object && 
    notification.userInfo == userInfo.to_ns
  }
end


class RawNotificationTests < Test::Unit::TestCase
  include Test::Unit::MockTalk

  def setup
    @observed = NSObject.alloc.init
    @watcher = flexmock(SomeRandomWatcher.alloc.init)
  end

  should "observe a name/object combination - both must be present" do
    center = NSNotificationCenter.defaultCenter
    center.addObserver_selector_name_object(@watcher,
                 :posted, "name", @observed)
    during {
      center.postNotificationName_object_userInfo(
                   "name", @observed, :key => :value)
      center.postNotificationName_object_userInfo(
                   "name", :a_random_object, :key => :value)
      center.postNotificationName_object_userInfo(
                   "WRONG_NAME", @observed, :key => :value)
    }.behold! {
      @watcher.should_receive(:posted).
               once.with(this_notification("name", @observed, :key => :value))
    }
  end
end

