Class: Bcpm::Tests::Environment

Inherits:
Object
  • Object
show all
Defined in:
lib/bcpm/tests/environment.rb

Overview

A match run for simulation purposes.

Each test case is its own anonymous class.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(prebuilt_name = nil) ⇒ Environment

Creates a new environment blueprint.

Args:

prebuilt_name:: if given, the created blueprint points to an already-built environment


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/bcpm/tests/environment.rb', line 25

def initialize(prebuilt_name = nil)
  @file_ops = [] 
  @patch_ops = []
  @build_log = nil

  if prebuilt_name
    @player_name = prebuilt_name
    @available = true
  else
    @player_name = self.class.new_player_name
    @available = false
  end
end

Instance Attribute Details

#build_logObject (readonly)

The build log, if the build happened.



19
20
21
# File 'lib/bcpm/tests/environment.rb', line 19

def build_log
  @build_log
end

#player_nameObject (readonly)

Name of the player container for the enviornment.



16
17
18
# File 'lib/bcpm/tests/environment.rb', line 16

def player_name
  @player_name
end

Class Method Details

.new_player_nameObject

A player name guaranteed to be unique across the systme.



243
244
245
246
247
248
249
250
251
# File 'lib/bcpm/tests/environment.rb', line 243

def self.new_player_name
  # NOTE: Java doesn't like .s in it package names :)
  host = Socket.hostname.gsub(/[^A-Za-z_]/, '_')
  @prefix ||=
      "bcpmtest_#{host}_#{(Time.now.to_f * 1000).to_i}_#{$PID}"
  @counter ||= 0
  @counter += 1
  "#{@prefix}_#{@counter}"
end

Instance Method Details

#available?Boolean

True if the environment has been setup and can be used to run tests.

Returns:

  • (Boolean)


67
68
69
# File 'lib/bcpm/tests/environment.rb', line 67

def available? 
  @available
end

#buildObject

Builds the binaries for the player in this environment.

Called by setup, uses its environment.

Returns true for success, false for failure.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/bcpm/tests/environment.rb', line 202

def build
  uid = "bcpmbuild_#{Socket.hostname}_#{(Time.now.to_f * 1000).to_i}_#{$PID}"
  tempdir = File.expand_path File.join(Dir.tmpdir, 'bcpm', uid)
  FileUtils.mkdir_p tempdir
  build_log = File.join tempdir, 'build.log'
  build_file = File.join tempdir, 'build.xml'
  Bcpm::Match.write_build build_file, 'bc.conf'
    
  Bcpm::Match.run_build_script tempdir, build_file, build_log, 'build'
  @build_log = File.exist?(build_log) ? File.open(build_log, 'rb') { |f| f.read } : ''
  FileUtils.rm_rf tempdir
  
  @build_log.index("\nBUILD SUCCESSFUL\n") ? true : false
end

#file_op(op) ⇒ Object

Queue an operation that adds a file.



84
85
86
# File 'lib/bcpm/tests/environment.rb', line 84

def file_op(op)
  @file_ops << op
end

#file_opsObject

Copies files from the test suite to the environment.

Called by setup, uses its environment.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/bcpm/tests/environment.rb', line 96

def file_ops
  @file_ops.each do |op|
    op_type, target, source = *op
    
    if op_type == :fragment
      target, target_fragment = *target
      source, source_fragment = *source
    end
    
    target = "#{@player_name}.#{target}"
    source = "#{@player_name}.#{source}"
    file_path = java_path @player_src, source
    
    next unless File.exist? file_path
    source_contents = File.read file_path

    case op_type        
    when :file
      contents = source_contents
    when :fragment
      next unless fragment_match = fragment_regexp(source_fragment).match(source_contents)
      contents = fragment_match[0]
    end

    source_pkg = java_package source
    target_pkg = java_package target
    unless source_pkg == target_pkg
      contents.gsub! /(^|[^A-Za-z0-9_.])#{source_pkg}([^A-Za-z0-9_]|$)/, "\\1#{target_pkg}\\2"
    end

    source_class = java_class source
    target_class = java_class target
    unless source_class == target_class
      contents.gsub! /(^|[^A-Za-z0-9_])#{source_class}([^A-Za-z0-9_]|$)/, "\\1#{target_class}\\2"
    end
      
    file_path = java_path @player_src, target
    
    case op_type
    when :file
      next unless File.exist?(File.dirname(file_path))
    when :fragment
      next unless File.exist?(file_path)
      source_contents = File.read file_path
      # Not using a string because source code might contain \1 which would confuse gsub.
      source_contents.gsub! fragment_regexp(target_fragment) do |match|
        "#{$1}\n#{contents}\n#{$3}"
      end
      contents = source_contents
    end
    
    File.open(file_path, 'wb') { |f| f.write contents }  
  end
end

#fragment_regexp(label) ⇒ Object

Regular expression matching a code fragment.

The expression captures three groups: the fragment start marker, the fragment, and the fragment end marker.



221
222
223
# File 'lib/bcpm/tests/environment.rb', line 221

def fragment_regexp(label)
  /^([ \t]*\/\/\$[ \t]*\+mark\:[ \t]*#{label}\s)(.*)(\n[ \t]*\/\/\$[ \t]*\-mark\:[ \t]*#{label}\s)/m
end

#java_class(class_name) ⇒ Object

Short class name for a Java class given its fully qualified name.



237
238
239
240
# File 'lib/bcpm/tests/environment.rb', line 237

def java_class(class_name)
  index = class_name.rindex '.'
  index ? class_name[index + 1, class_name.length - index - 1] : class_name
end

#java_package(class_name) ⇒ Object

Package for a Java class given its fully qualified name.



231
232
233
234
# File 'lib/bcpm/tests/environment.rb', line 231

def java_package(class_name)
  index = class_name.rindex '.'
  index ? class_name[0, index] : ''
end

#java_path(package_path, class_name) ⇒ Object

Path to .java source for a class.



226
227
228
# File 'lib/bcpm/tests/environment.rb', line 226

def java_path(package_path, class_name)
  File.join(File.dirname(package_path), class_name.gsub('.', '/') + '.java')
end

#patch_op(op) ⇒ Object

Queue an operation that patches all source files.



89
90
91
# File 'lib/bcpm/tests/environment.rb', line 89

def patch_op(op)
  @patch_ops << op
end

#patch_opsObject

Applies the patch operations to the source code in the environment.

Called by setup, uses its environment.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/bcpm/tests/environment.rb', line 154

def patch_ops
  return if @patch_ops.empty?
  
  Dir.glob(File.join(@player_src, '**', '*.java')).each do |file|
    old_contents = File.read file
    lines = old_contents.split("\n")

    stubs_enabled = true

    0.upto(lines.count - 1) do |i|
      line = lines[i]
      if directive_match = /^\s*\/\/\$(.*)$/.match(line)
        directive = directive_match[1]
        case directive.strip.downcase
        when '+stubs', '-stubs'
          stubs_enabled = directive[0] == ?+
        end
      else
        @patch_ops.each do |op|
          op_type, target, source = *op
    
          case op_type
          when :stub_member
            if stubs_enabled
              line.gsub!(/(^|[^A-Za-z0-9_.])([A-Za-z0-9_.]*\.)?#{source}\(/) do |match|
                arg = ($2.nil? || $2.empty?) ? 'this' : $2[0..-2]
                "#{$1}#{player_name}.#{target}(#{arg}, "
              end
            end
          when :stub_static
            if stubs_enabled
              line.gsub! /(^|[^A-Za-z0-9_.])([A-Za-z0-9_.]*\.)?#{source}\(/,
                         "\\1#{player_name}.#{target}("
            end
          end
        end
      end
    end
    contents = lines.join("\n")
    File.open(file, 'wb') { |f| f.write contents } unless contents == old_contents
  end
end

#setup(suite_path) ⇒ Object

Puts together an environment according to the blueprint.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/bcpm/tests/environment.rb', line 40

def setup(suite_path)
  return true if @available
  
  begin
    test_player = File.basename suite_path

    @player_path = Bcpm::Player.checkpoint test_player, 'master', player_name
    raise "Failed to checkpoint player at #{suite_path}" unless @player_path
    @player_src = Bcpm::Player.package_path(@player_path)
          
    file_ops
    patch_ops
    
    unless build
      print "Test environment build failed! Some tests will not run!\n"
      print "#{@build_log}\n"
      return false
    end
  rescue Exception => e
    print "Failed setting up test environment! Some tests will not run!\n"
    print "#{e.class.name}: #{e.to_s}\n#{e.backtrace.join("\n")}\n\n"
    return false
  end
  @available = true    
end

#suite_map_path(map_name) ⇒ Object

Path to the maps in the test suite for the player.



78
79
80
81
# File 'lib/bcpm/tests/environment.rb', line 78

def suite_map_path(map_name)
  File.join Bcpm::Player.local_root, player_name, 'suite', 'maps',
            map_name + '.xml'
end

#teardownObject

Undoes the effects of setup.



72
73
74
75
# File 'lib/bcpm/tests/environment.rb', line 72

def teardown
  Bcpm::Player.uninstall player_name
  @available = false
end