1# Copyright (c) 2007, The RubyCocoa Project.
2# All Rights Reserved.
3#
4# RubyCocoa is free software, covered under either the Ruby's license or the 
5# LGPL. See the COPYRIGHT file for more information.
6
7# A BridgeSupport files verifier, based on the generated Ruby files from 
8# gen_bridge_doc.
9#
10# To run it from the source root:
11#    ruby framework/tool/verify_bridge_metadata.rb framework/bridge-support 
12#       framework/bridge-doc
13#
14# (Make sure to have run install.rb config and doc tasks before.)
15
16begin require 'rubygems'; rescue LoadError; end
17require 'xml/libxml'
18
19def die(s)
20  $stderr.puts s
21  exit 1
22end
23
24def log(s)
25  $stderr.puts s if $VERBOSE
26end
27
28def lputs(p, s)
29  puts p.ljust(50) + s
30end
31
32# Check arguments.
33if ARGV.size != 2
34  die "Usage: #{__FILE__} <bridge-support-dir> <bridge-doc-dir>"
35end
36bs_dir, doc_dir = ARGV
37$doc_ruby_dir = doc_ruby_dir = File.join(doc_dir, 'ruby')
38
39# Setup some autoload magic first.
40module OSX
41  def self.const_missing(sym)
42    return Object if sym == :ObjcID
43    path = "#{$doc_ruby_dir}/#{sym}.rb"
44    if File.exist?(path)
45      require(path)
46      if const_defined?(sym)
47        const_get(sym)
48      end
49    end
50  end
51end
52
53# Load bridge support and Ruby files.
54class BSDocs
55  def initialize(bs_dir)
56    @bs_docs = Dir.glob(bs_dir + '/*.bridgesupport').map { |f| XML::Document.file(f) } 
57  end
58  def any?(xpath)
59    xpath = '/signatures/' + xpath
60    @bs_docs.each do |doc|
61      set = doc.find(xpath)
62      return set[0] unless set.empty? 
63    end
64    return nil
65  end
66end
67bs_docs = BSDocs.new(bs_dir) 
68Dir.glob(doc_ruby_dir + '/*.rb').map { |x| require(x) }
69
70class BSTest
71  attr_accessor :name, :failures, :count
72  def self.go(name)
73    test = self.new 
74    test.name = name
75    test.failures = []
76    test.count = 0
77    @tests ||= []
78    @tests << test
79    yield(test)
80  end
81  def self.all_tests
82    @tests
83  end
84  def success(msg)
85    log(msg)
86    @count += 1
87  end
88  def failure(msg)
89    log(msg)
90    @failures << msg
91    @count += 1
92  end
93end
94
95log 'Verifying functions...'
96BSTest.go('Functions') do |test|
97  OSX.methods(false).each do |func_name|
98    if elem = bs_docs.any?("function[@name=\"#{func_name}\"]")
99      argc = elem.find('arg').length
100      e_argc = OSX.method(func_name).arity
101      if e_argc < 0 and elem['variadic'] == 'true'
102        e_argc = e_argc.abs - 1
103      end     
104      if e_argc == argc
105        test.success func_name
106      else
107        test.failure "#{func_name} has wrong arity (expected #{e_argc}, got #{argc})"
108      end
109    else
110      test.failure "#{func_name} is missing"
111    end  
112  end
113end
114
115log 'Verifying constants...'
116BSTest.go('Constants') do |test|
117  OSX.constants.each do |const_name|
118    o = OSX.const_get(const_name)
119    next unless o.nil? # Constants are nil
120    low_const_name = const_name.sub(/^[A-Z]/) { |s| s.downcase }
121    if bs_docs.any?("constant[@name=\"#{const_name}\" or @name=\"#{low_const_name}\"]") \
122       or bs_docs.any?("enum[@name=\"#{const_name}\" or @name=\"#{low_const_name}\"]")
123      
124      test.success const_name
125    else
126      test.failure "#{const_name} is missing"
127    end 
128  end
129end
130
131# Print test results
132BSTest.all_tests.each do |test|
133  puts "#{test.name}: #{test.count} test(s), #{test.failures.length} failure(s)"
134  test.failures.each { |s| puts "-> " + s }
135end
136