1# ==================================================================== 2# Licensed to the Apache Software Foundation (ASF) under one 3# or more contributor license agreements. See the NOTICE file 4# distributed with this work for additional information 5# regarding copyright ownership. The ASF licenses this file 6# to you under the Apache License, Version 2.0 (the 7# "License"); you may not use this file except in compliance 8# with the License. You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, 13# software distributed under the License is distributed on an 14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15# KIND, either express or implied. See the License for the 16# specific language governing permissions and limitations 17# under the License. 18# ==================================================================== 19 20require "fileutils" 21require "pathname" 22 23# Tale of a hack... 24# 25# Here we are, %SVN-WC-ROOT%/subversion/bindings/swig/ruby/test/util.rb, 26# trying to require %SVN-WC-ROOT%/subversion/bindings/swig/ruby/svn/util.rb, 27# all the while supporting both Ruby 1.8 and 1.9. Simply using this, 28# 29# require "svn/util" 30# 31# works for Ruby 1.8 if the CWD is subversion/bindings/swig/ruby 32# when we are running the tests, e.g.: 33# 34# %SVN-WC-ROOT%/subversion/bindings/swig/ruby>ruby test\run-test.rb 35# 36# This is because the CWD is included in the load path when Ruby 1.8 37# searches for required files. But this doesn't work for Ruby 1.9, 38# which doesn't include the CWD this way, so instead we could use this: 39# 40# require "./svn/util" 41# 42# But that only works if ./svn/util is relative to the CWD (again if the 43# CWD is %SVN-WC-ROOT%/subversion/bindings/swig/ruby). However, if we run 44# the tests from a different CWD and specify 45# %SVN-WC-ROOT%/subversion/bindings/swig/ruby as an additional $LOAD_PATH 46# using the ruby -I option, then that fails on both 1.8 and 1.9 with a 47# LoadError. 48# 49# The usual solution in a case like this is to use require_relative, 50# 51# require_relative "../svn/util" 52# 53# But that's only available in Ruby 1.9. We could require the backports gem 54# but there is a simple workaround, just calculate the full path of util: 55require File.join(File.dirname(__FILE__), '../svn/util') 56 57require "tmpdir" 58 59require "my-assertions" 60 61class Time 62 unless instance_methods.include?("to_int") 63 alias to_int to_i 64 end 65end 66 67require 'greek_tree' 68 69module SvnTestUtil 70 def setup_default_variables 71 @author = ENV["USER"] || "sample-user" 72 @password = "sample-password" 73 @realm = "sample realm" 74 75 @svnserve_host = "127.0.0.1" 76 @svnserve_ports = (64152..64282).collect{|x| x.to_s} 77 78 @tmp_path = Dir.mktmpdir 79 @wc_path = File.join(@tmp_path, "wc") 80 @import_path = File.join(@tmp_path, "import") 81 @repos_path = File.join(@tmp_path, "repos") 82 @svnserve_pid_file = File.join(@tmp_path, "svnserve.pid") 83 @full_repos_path = File.expand_path(@repos_path) 84 @repos_uri = "file://#{@full_repos_path.sub(/^\/?/, '/')}" 85 86 @config_path = File.join(@tmp_path, "config") 87 @greek = Greek.new(@tmp_path, @import_path, @wc_path, @repos_uri) 88 end 89 90 def setup_basic(need_svnserve=false) 91 @need_svnserve = need_svnserve 92 setup_default_variables 93 setup_tmp 94 setup_tmp(@import_path) 95 setup_repository 96 add_hooks 97 setup_svnserve if @need_svnserve 98 setup_config 99 setup_wc 100 add_authentication 101 GC.stress = true if GC.respond_to?(:stress=) and $DEBUG 102 end 103 104 def teardown_basic 105 GC.stress = false if GC.respond_to?(:stress=) 106 teardown_svnserve if @need_svnserve 107 teardown_repository 108 teardown_wc 109 teardown_config 110 teardown_tmp 111 gc 112 end 113 114 def gc 115 if $DEBUG 116 before_pools = Svn::Core::Pool.number_of_pools 117 puts 118 puts "before pools: #{before_pools}" 119 end 120 gc_enable do 121 GC.start 122 end 123 if $DEBUG 124 after_pools = Svn::Core::Pool.number_of_pools 125 puts "after pools: #{after_pools}" 126 STDOUT.flush 127 end 128 end 129 130 def change_gc_status(prev_disabled) 131 begin 132 yield 133 ensure 134 if prev_disabled 135 GC.disable 136 else 137 GC.enable 138 end 139 end 140 end 141 142 def gc_disable(&block) 143 change_gc_status(GC.disable, &block) 144 end 145 146 def gc_enable(&block) 147 change_gc_status(GC.enable, &block) 148 end 149 150 def setup_tmp(path=@tmp_path) 151 remove_recursively_with_retry(path) 152 FileUtils.mkdir_p(path) 153 end 154 155 def teardown_tmp(path=@tmp_path) 156 remove_recursively_with_retry(path) 157 end 158 159 def setup_repository(path=@repos_path, config={}, fs_config={}) 160 require "svn/repos" 161 remove_recursively_with_retry(path) 162 FileUtils.mkdir_p(File.dirname(path)) 163 @repos = Svn::Repos.create(path, config, fs_config) 164 @fs = @repos.fs 165 end 166 167 def teardown_repository(path=@repos_path) 168 @fs.close unless @fs.nil? 169 @repos.close unless @repos.nil? 170 remove_recursively_with_retry(path) 171 @repos = nil 172 @fs = nil 173 end 174 175 def setup_wc 176 teardown_wc 177 make_context("") { |ctx| ctx.checkout(@repos_uri, @wc_path) } 178 end 179 180 def teardown_wc 181 remove_recursively_with_retry(@wc_path) 182 end 183 184 def setup_config 185 teardown_config 186 Svn::Core::Config.ensure(@config_path) 187 end 188 189 def teardown_config 190 remove_recursively_with_retry(@config_path) 191 end 192 193 def add_authentication 194 passwd_file = "passwd" 195 File.open(@repos.svnserve_conf, "w") do |conf| 196 conf.print <<-CONF 197[general] 198anon-access = none 199auth-access = write 200password-db = #{passwd_file} 201realm = #{@realm} 202 CONF 203 end 204 File.open(File.join(@repos.conf_dir, passwd_file), "w") do |f| 205 f.print <<-PASSWD 206[users] 207#{@author} = #{@password} 208 PASSWD 209 end 210 end 211 212 def add_hooks 213 add_pre_revprop_change_hook 214 end 215 216 def youngest_rev 217 @fs.youngest_rev 218 end 219 220 def root(rev=nil) 221 @fs.root(rev) 222 end 223 224 def prop(name, rev=nil) 225 @fs.prop(name, rev) 226 end 227 228 def make_context(log) 229 ctx = Svn::Client::Context.new 230 ctx.set_log_msg_func do |items| 231 [true, log] 232 end 233 ctx.add_username_prompt_provider(0) do |cred, realm, username, may_save| 234 cred.username = @author 235 cred.may_save = false 236 end 237 ctx.config = Svn::Core::Config.config(@config_path) 238 setup_auth_baton(ctx.auth_baton) 239 return ctx unless block_given? 240 begin 241 yield ctx 242 ensure 243 ctx.destroy 244 end 245 end 246 247 def setup_auth_baton(auth_baton) 248 auth_baton[Svn::Core::AUTH_PARAM_CONFIG_DIR] = @config_path 249 auth_baton[Svn::Core::AUTH_PARAM_DEFAULT_USERNAME] = @author 250 end 251 252 def normalize_line_break(str) 253 if Svn::Util.windows? 254 str.gsub(/\n/, "\r\n") 255 else 256 str 257 end 258 end 259 260 def setup_greek_tree 261 make_context("setup greek tree") { |ctx| @greek.setup(ctx) } 262 end 263 264 def remove_recursively_with_retry(path) 265 retries = 0 266 while (retries+=1) < 100 && File.exist?(path) 267 begin 268 FileUtils.rm_r(path, :secure=>true) 269 rescue 270 sleep 0.1 271 end 272 end 273 assert(!File.exist?(path), "#{Dir.glob(path+'/**/*').join("\n")} should not exist after #{retries} attempts to delete") 274 end 275 276 module Svnserve 277 def setup_svnserve 278 @svnserve_port = nil 279 @repos_svnserve_uri = nil 280 281 # Look through the list of potential ports until we're able to 282 # successfully start svnserve on a free one. 283 @svnserve_ports.each do |port| 284 @svnserve_pid = fork { 285 STDERR.close 286 exec("svnserve", 287 "--listen-host", @svnserve_host, 288 "--listen-port", port, 289 "-d", "--foreground") 290 } 291 pid, status = Process.waitpid2(@svnserve_pid, Process::WNOHANG) 292 if status and status.exited? 293 if $DEBUG 294 STDERR.puts "port #{port} couldn't be used for svnserve" 295 end 296 else 297 # svnserve started successfully. Note port number and cease 298 # startup attempts. 299 @svnserve_port = port 300 @repos_svnserve_uri = 301 "svn://#{@svnserve_host}:#{@svnserve_port}#{@full_repos_path}" 302 # Avoid a race by waiting a short time for svnserve to start up. 303 # Without this, tests can fail with "Connection refused" errors. 304 sleep 1 305 break 306 end 307 end 308 if @svnserve_port.nil? 309 msg = "Can't run svnserve because available port " 310 msg << "isn't exist in [#{@svnserve_ports.join(', ')}]" 311 raise msg 312 end 313 end 314 315 def teardown_svnserve 316 if @svnserve_pid 317 Process.kill(:TERM, @svnserve_pid) 318 begin 319 Process.waitpid(@svnserve_pid) 320 rescue Errno::ECHILD 321 end 322 end 323 end 324 325 def add_pre_revprop_change_hook 326 File.open(@repos.pre_revprop_change_hook, "w") do |hook| 327 hook.print <<-HOOK 328#!/bin/sh 329REPOS="$1" 330REV="$2" 331USER="$3" 332PROPNAME="$4" 333 334if [ "$PROPNAME" = "#{Svn::Core::PROP_REVISION_LOG}" -a \ 335 "$USER" = "#{@author}" ]; then 336 exit 0 337fi 338 339exit 1 340 HOOK 341 end 342 FileUtils.chmod(0755, @repos.pre_revprop_change_hook) 343 end 344 end 345 346 module SetupEnvironment 347 def setup_test_environment(top_dir, base_dir, ext_dir) 348 svnserve_dir = File.join(top_dir, 'subversion', 'svnserve') 349 ENV["PATH"] = "#{svnserve_dir}:#{ENV['PATH']}" 350 FileUtils.ln_sf(File.join(base_dir, ".libs"), ext_dir) 351 end 352 end 353 354 if Svn::Util.windows? 355 require 'windows_util' 356 include Windows::Svnserve 357 extend Windows::SetupEnvironment 358 else 359 include Svnserve 360 extend SetupEnvironment 361 end 362end 363