Commit: 7f972b820fb9e0f5dd8f37cb2c39d941480e8d29
Author: [email protected] <[email protected]> Fri, 2 Dec 2011 21:57:09
-0800
Parents: 0c5cddd28769cf96b3d11073e0d89fecdf56c384
Branches: master
Link:
http://git.php.net/?p=pftt2.git;a=commitdiff;h=7f972b820fb9e0f5dd8f37cb2c39d941480e8d29
Log:
improvements to Diff Engine support searching, managing and filtering Diff
files from PHPT results
Former-commit-id: 01c5eb16c74924b591e2947a456f48c8ac4e6fc5
Changed paths:
A PFTT/lib/diff/engine.rb
A PFTT/lib/diff/zd.rb
A PFTT/lib/diff/zd/all_test_cases.rb
M PFTT/lib/host.rb
M PFTT/lib/host/local.rb
M PFTT/lib/host/remote/psc/client.rb
M PFTT/lib/host/remote/psc/recovery_manager.rb
M PFTT/lib/host/remote/psc/remote_host.rb
M PFTT/lib/host/remote/ssh.rb
M PFTT/lib/middleware.rb
M PFTT/lib/middleware/cli.rb
M PFTT/lib/test/case/phpt.rb
M PFTT/lib/test/result/phpt.rb
M PFTT/lib/test/runner/stage/phpt/package.rb
M PFTT/lib/test/runner/stage/phpt/upload.rb
M PFTT/lib/tracing/context.rb
M PFTT/lib/tracing/prompt/diff.rb
M PFTT/lib/util/package.rb
A PFTT/src/se/datadosen/component/RiverLayout.class
A PFTT/src/se/datadosen/component/RiverLayout.java
A PFTT/src/se/datadosen/component/Ruler.class
diff --git a/PFTT/lib/diff/engine.rb b/PFTT/lib/diff/engine.rb
new file mode 100644
index 0000000..e69de29
diff --git a/PFTT/lib/diff/zd.rb b/PFTT/lib/diff/zd.rb
new file mode 100644
index 0000000..d5b1b5d
--- /dev/null
+++ b/PFTT/lib/diff/zd.rb
@@ -0,0 +1,108 @@
+
+module Diff
+ module ZD
+
+class BaseZD
+
+ attr_reader :diff
+
+ def initialize(diff)
+ unless [:expected, :actual, :added, :removed, :added_and_removed,
:org_expected, :org_actual].include?(diff)
+ raise ArgumentError, diff
+ end
+
+ @diff = diff
+ end
+
+ def zd_label
+ "#{level_label()} #{diff_label()}"
+ end
+
+ def diff_label
+ # Override
+ end
+
+ def sym
+ # Override
+ end
+
+ def has_sym?
+ sym.length > 0
+ end
+
+ def level_label
+ # Override
+ end
+
+ def level
+ # Override
+ end
+
+ def is_diff?(o_diff)
+ diff()==o_diff
+ end
+
+ def is_level?(o_level)
+ level() == o_level
+ end
+
+ def save_chunk_replacements
+ end
+
+ def load_chunk_replacements
+ end
+
+ def save_diff
+ end
+
+ def delete(id)
+ end
+
+ def add(id)
+ end
+
+ def pass(id)
+ end
+
+ def change(id, to)
+ end
+
+ def find(needle)
+ # Override
+ end
+
+ def iterate
+ # Override
+ # DiffIterator.new
+ end
+
+end # class ZD
+
+class DiffIterator
+
+ def zd
+ end
+
+ def has_next?
+ end
+
+ def next
+ end
+
+ def delete
+ end
+
+ def add
+ end
+
+ def pass
+ end
+
+ def change(to)
+ end
+
+end # class DiffIterator
+
+
+ end # module ZD
+end # module Diff
\ No newline at end of file
diff --git a/PFTT/lib/diff/zd/all_test_cases.rb
b/PFTT/lib/diff/zd/all_test_cases.rb
new file mode 100644
index 0000000..231204b
--- /dev/null
+++ b/PFTT/lib/diff/zd/all_test_cases.rb
@@ -0,0 +1,262 @@
+
+module Diff
+ module ZD
+ module AllTestCases
+
+class BaseAllTestCases < BaseZD
+
+ def level
+ :all
+ end
+
+ def level_label
+ 'All'
+ end
+
+ def find(needle)
+ down_needle = needle.downcase
+ up_needle = needle.upcase
+
+ dirs().each do |dir|
+ files().each do |file_ext|
+ Dir.glob("#{dir}/**/*#{file_ext}") do |file_name|
+ # TODO document
+ #
+ unless accept_file(dir, Host.sub(dir, file_name))
+ next
+ end
+
+ # search file for needle
+ IO.readlines(file_name).each do |line|
+ if true#accept_line(line)
+ if check_line(line, down_needle, up_needle)
+
+ # TODO report match
+ puts line
+
+ end
+ end
+ end
+ end
+ end
+ end
+ end # def find
+
+ protected
+
+ def accept_file(dir, file_name)
+ # can Override
+ true
+ end
+
+ def dirs
+ # Override
+ []
+ end
+
+ def check_line(line, down_needle, up_needle)
+ return ( line.downcase.include?(down_needle) or
line.upcase.include?(up_needle) )
+ end
+
+ def files
+ if [:added, :removed, :added_and_removed].include?(diff())
+ return ['.diff']
+ elsif [:org_expected, :expected].include?(diff())
+ return ['.expectf'] # TODO complete list?
+ elsif [:actual, :org_actual].include?(diff())
+ return ['.result']
+ else
+ return [] # shouldn't happen
+ end
+ end
+
+ def accept_line(line)
+ case diff()
+ when :added
+ return line.starts_with?('+')
+ when :removed
+ return line.starts_with?('-')
+ when :added_and_removed
+ return ( line.starts_with?('+') or line.starts_with?('-') )
+ else
+ # search any line of Expected output, actual output, etc... files
+ return true
+ end
+ end
+
+end # class BaseAllTestCases
+
+class BaseSingleRun < BaseAllTestCases
+ attr_reader :dir, :rev
+
+ def initialize(diff, dir, rev=nil)
+ super(diff)
+ @dir = dir
+ @rev = rev
+ end
+
+ def dirs
+ [@dir]
+ end
+
+ def sym
+ ''
+ end
+
+end # class SingleRun
+
+class BaseRun < BaseSingleRun
+ def diff_label
+ if @rev
+ @rev # LESSON BaseRun and TestRun DRY not normalized
+ else
+ 'Base'
+ end
+ end
+end
+
+class TestRun < BaseSingleRun
+ def diff_label
+ if @rev
+ @rev
+ else
+ 'Test'
+ end
+ end
+end
+
+class Base2Runs < BaseAllTestCases
+ attr_reader :base_dir, :test_dir, :base_rev, :test_rev
+
+ def initialize(diff, base_dir, test_dir, base_rev=nil, test_rev=nil)
+ super(diff)
+ @base_dir = base_dir
+ @test_dir = test_dir
+ @base_rev = base_rev
+ @test_rev = test_rev
+ end
+
+ protected
+
+ def dirs
+ [@base_dir, @test_dir]
+ end
+
+ def get_contents(dir, file_name)
+ file_name = File.join(dir, file_name)
+ unless File.exist?(file_name)
+ return nil
+ end
+
+ # TODO should only have to read a file in once
+ # comparing arrays of lines instead of the file contents as a string
+ # eliminates comparison problems due to line ending characters
+ IO.readlines(file_name)
+ end
+
+end # class Base2Runs
+
+class BaseTestRun < Base2Runs
+ def diff_label
+ if @base_rev and @test_rev
+ "#{@base_rev}#{sym()}#{@test_rev}"
+ else
+ "Base#{sym()}Test"
+ end
+ end
+end
+
+class TestMinusBase < Base2Runs
+ # Test-Base => only results from Test not matching those in Base run
+
+ def diff_label
+ if @base_rev and @test_rev
+ "#{@test_rev}#{sym()}#{@base_rev}"
+ else
+ "Test#{sym()}Base"
+ end
+ end
+
+ def sym
+ '-'
+ end
+
+ protected
+
+ def accept_file(dir, file_name)
+ if dir == @base_dir
+ test_file = get_contents(@test_dir, file_name)
+ base_file = get_contents(@base_dir, file_name)
+ return ( base_file.nil? or test_file != base_file )
+ end
+ return false
+ end
+
+end # class TestMinusBase
+
+class BaseMinusTest < BaseTestRun
+ # Base-Test => only results from Test not matching those in Base run
+
+ def sym
+ '-'
+ end
+
+ protected
+
+ def accept_file(dir, file_name)
+ if dir == @test_dir
+ base_file = get_contents(@base_dir, file_name)
+ test_file = get_contents(@test_dir, file_name)
+ return ( test_file.nil? or test_file != base_file )
+ end
+ return false
+ end
+
+end # class BaseMinusTest
+
+class BasePlusTest < BaseTestRun
+ # Base+Test => all results from Base and Test (duplicates removed)
+
+ def sym
+ '+'
+ end
+
+ protected
+
+ def accept_file(dir, file_name)
+ if dir == @test_dir
+ # TODO == (no duplicate)
+ test_file = get_contents(@test_dir, file_name)
+ base_file = get_contents(@base_dir, file_name)
+ return ( base_file.nil? or test_file == base_file )
+ end
+ return false
+ end
+
+end # class BasePlusTest
+
+class BaseEqTest < BaseTestRun
+ # Base=Test => only results from Base and Test that match
+
+ def sym
+ '='
+ end
+
+ protected
+
+ def accept_file(dir, file_name)
+ if dir == @test_dir
+ # TODO ==
+ test_file = get_contents(@test_dir, file_name)
+ base_file = get_contents(@base_dir, file_name)
+ return ( base_file.nil? or test_file != base_file )
+ end
+ return false
+ end
+
+end # class BaseEqTest
+
+
+ end # module AllTestCases
+ end
+end
diff --git a/PFTT/lib/host.rb b/PFTT/lib/host.rb
index b66cb09..0ac446b 100644
--- a/PFTT/lib/host.rb
+++ b/PFTT/lib/host.rb
@@ -17,6 +17,17 @@ module Host
@@hosts||={}
end
end
+
+ def self.sub base, path
+ if path.starts_with?(base)
+ return path[base.length..path.length]
+ end
+ return path
+ end
+
+ def self.join *path_array
+ path_array.join('/')
+ end
def self.administrator_user (platform)
if platform == :windows
@@ -44,11 +55,15 @@ module Host
def self.to_windows_path!(path)
# remove \\ from path too. they may cause problems on some Windows SKUs
- return path.gsub!('/', '\\').gsub!('\\\\', '\\').gsub!('\\\\', '\\')
+ path.gsub!('/', '\\')
+ path.gsub!('\\\\', '\\')
+ path.gsub!('\\\\', '\\')
end
def self.to_posix_path!(path)
path.gsub!('\\', '/')
+ path.gsub!('//', '/')
+ path.gsub!('//', '/')
end
def self.fs_op_to_cmd(fs_op, src, dst)
@@ -109,27 +124,23 @@ module Host
end
def no_trailing_slash(path)
- if path.ends_with?('/') or path.ends_with?('\\')
- path = path[0..path.length-1]
- end
- return path
+ Host.no_trailing_slash(path)
end
def to_windows_path(path)
- # remove \\ from path too. they may cause problems on some Windows SKUs
- return path.gsub('/', '\\').gsub('\\\\', '\\').gsub('\\\\', '\\')
+ Host.to_windows_path(path)
end
def to_posix_path(path)
- return path.gsub('\\', '/')
+ Host.to_posix_path(path)
end
def to_windows_path!(path)
- path.gsub!('/', '\\')
+ Host.to_windows_path!(path)
end
def to_posix_path!(path)
- path.gsub!('\\', '/')
+ Host.to_posix_path!(path)
end
def number_of_processors(ctx=nil)
@@ -212,14 +223,14 @@ module Host
end
end
- cmd!(case
- when posix? then %Q{cp -R "#{from}" "#{to}"}
- else
- to_windows_path!(from)
- to_windows_path!(to)
-
- %Q{xcopy /Y /s /i /q "#{from}" "#{to}"}
- end, ctx)
+ if !directory?(from)
+ copy_file(from, to, ctx, mk)
+ return
+ elsif mk
+ mkdir(File.dirname(to), ctx)
+ end
+
+ copy_cmd(from, to, ctx)
end
def move from, to, ctx
@@ -229,17 +240,12 @@ module Host
end
end
- from = no_trailing_slash(from)
- to = no_trailing_slash(to)
+ if !directory?(from)
+ move_file(from, to, ctx)
+ return
+ end
- cmd!(case
- when posix? then %Q{mv "#{from}" "#{to}"}
- else
- to_windows_path!(from)
- to_windows_path!(to)
-
- %Q{move "#{from}" "#{to}"}
- end, ctx)
+ move_cmd(from, to, ctx)
end
def time=(time)
@@ -290,6 +296,9 @@ module Host
end
def userprofile(ctx=nil)
+ unless @_userprofile.nil?
+ return @_userprofile
+ end
p = nil
if posix?
p = env_value('HOME', ctx)
@@ -298,44 +307,50 @@ module Host
end
if exists?(p, ctx)
- return p
+ return @_userprofile = p
else
- return nil
+ return @_userprofile = systemdrive(ctx)
end
end
def appdata(ctx=nil)
+ unless @_appdata.nil?
+ return @_appdata
+ end
if posix?
p = env_value('HOME', ctx)
if p and exists?(p, ctx)
- return p
+ return @_appdata = p
end
else
p = env_value('USERPROFILE', ctx)
if p
q = p + '\\AppData\\'
if exists?(q, ctx)
- return q
+ return @_appdata = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_appdata = q
end
end
end
- return systemdrive(ctx)
+ return @_appdata = systemdrive(ctx)
end # def appdata
def appdata_local(ctx=nil)
+ unless @_appdata_local.nil?
+ return @_appdata_local
+ end
if posix?
p = env_value('HOME', ctx)
if p
q = p + '/PFTT'
if exists?(q, ctx)
- return q
+ return @_appdata_local = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_appdata_local = q
end
end
else
@@ -343,34 +358,37 @@ module Host
if p
q = p + '\\AppData\\Local'
if exists?(q, ctx)
- return q
+ return @_appdata_local = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_appdata_local = q
end
end
end
- return systemdrive(ctx)
+ return @_appdata_local = systemdrive(ctx)
end # def appdata_local
def tempdir ctx
+ unless @_tempdir.nil?
+ return @_tempdir
+ end
if posix?
p = '/usr/local/tmp'
q = p + '/PFTT'
if exists?(q, ctx)
- return q
+ return @_tempdir = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_tempdir = q
end
p = '/tmp'
q = p + '/PFTT'
if exists?(q, ctx)
- return q
+ return @_tempdir = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_tempdir = q
end
else
# try %TEMP%\\PFTT
@@ -378,10 +396,10 @@ module Host
if p
q = p + '\\PFTT'
if exists?(q, ctx)
- return q
+ return @_tempdir = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_tempdir = q
end
end
@@ -390,10 +408,10 @@ module Host
if p
q = p + '\\PFTT'
if exists?(q, ctx)
- return q
+ return @_tempdir = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_tempdir = q
end
end
@@ -403,10 +421,10 @@ module Host
p = '\\AppData\\Local\\Temp\\' + p
q = p + '\\PFTT'
if exists?(q, ctx)
- return q
+ return @_tempdir = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_tempdir = q
end
end
@@ -414,15 +432,15 @@ module Host
p = systemdrive(ctx)+'\\temp'
q = p + '\\PFTT'
if exists?(q, ctx)
- return q
+ return @_tempdir = q
elsif exists?(p, ctx)
mkdir(q, ctx)
- return q
+ return @_tempdir = q
end
end
- return systemdrive(ctx)
+ return @_tempdir = systemdrive(ctx)
end # def tempdir
alias :tmpdir :tempdir
@@ -592,7 +610,7 @@ module Host
# the current directory of the command to run
# :debug true|false
# runs the command with the host's debugger. if host has no debugger
installed, command will be run normally
- # :stdin ''
+ # :stdin_data ''
# feeds given string to the commands Standard Input
# :null_output true|false
# if true, returns '' for both STDOUT and STDERR. (if host is remote,
STDOUT and STDERR are not sent over
@@ -606,6 +624,7 @@ module Host
# :success_exit_code int, or [int] array default=0
# what exit code(s) defines success
# note: this is ignored if Command::Expected is used (which evaluates
success internally)
+ # LATER :elevate and :sudo support for windows and posix
# other options are silently ignored
#
#
@@ -676,7 +695,7 @@ module Host
# Windows will always have a C:\ even if the C:\ drive is not the
systemdrive
# posix doesn't have C: D: etc... drives
- @is_windows = exist?('C:\\', ctx)
+ @is_windows = _exist?('C:\\', ctx)
if ctx
# cool stuff: allow user to override OS detection
@@ -687,7 +706,6 @@ module Host
end
def posix?(ctx=nil)
- return false # TODO TUE
unless @posix.nil?
return @posix
end
@@ -696,7 +714,7 @@ module Host
end
ctx = ctx==nil ? nil :
ctx.new(Tracing::Context::Dependency::Detect::OS::Type)
- @posix = exist?('/usr', ctx)
+ @posix = _exist?('/usr', ctx)
if ctx
@posix = ctx.check_os_type_detect(:posix, @posix)
@@ -707,13 +725,32 @@ module Host
def make_absolute! *paths
paths.map do |path|
+ # support for Windows drive letters
+ # (if drive letter present, path is absolute)
return path if !posix? && path =~ /\A[A-Za-z]:\//
- return path if path =~ /\A[A-Za-z]:\//
+ return path if path =~ /\A[A-Za-z]:\//
+ #
path.replace( File.absolute_path( path, cwd() ) )
path
end
end
+
+ def exist? path, ctx=nil
+ make_absolute! path
+
+ if ctx
+ ctx.fs_op1(self, :exist, path) do |path|
+ return exist?(path, ctx)
+ end
+ end
+
+ _exist?(path, ctx)
+ end
+
+ alias :exists? :exist?
+ alias :exist :exist?
+ alias :exists :exist?
def format_path path, ctx=nil
case
@@ -753,10 +790,14 @@ module Host
else
return '/'
end
+ end
+
+ def sub base, path
+ Host.sub(base, path)
end
def join *path_array
- path_array.join(separator)
+ Host.join(path_array)
end
def upload_if_not(local, remote, ctx)
@@ -832,13 +873,16 @@ module Host
_mkdir(path, ctx) unless directory? path
end
- def mktmpdir path, ctx
- ctx = ctx==nil ? nil : ctx.new(SystemSetup::TempDirectory)
+ def mktmpdir ctx, path=nil, suffix=''
+ ctx = ctx==nil ? nil :
ctx.new(Tracing::Context::SystemSetup::TempDirectory)
+ unless path
+ path = tempdir(ctx)
+ end
make_absolute! path
tries = 10
begin
- dir = File.join( path, String.random(4) )
+ dir = File.join( path, String.random(6)+suffix )
raise 'exists' if directory? dir
mkdir(dir, ctx)
rescue
@@ -865,6 +909,9 @@ module Host
raise $!
end
end
+
+ alias :mktempfile :mktmpfile
+ alias :mktempdir :mktmpdir
def sane? path
make_absolute! path
@@ -894,6 +941,7 @@ module Host
end
def name ctx=nil
+ return 'OI1-PHP-FUNC-15' # TODO TUE
unless @_name
# find a name that other hosts on the network will use to reference
localhost
if windows?(ctx)
@@ -907,6 +955,31 @@ module Host
protected
+ def move_cmd(from, to, ctx)
+ from = no_trailing_slash(from)
+ to = no_trailing_slash(to)
+
+ cmd!(case
+ when posix? then %Q{mv "#{from}" "#{to}"}
+ else
+ from = to_windows_path(from)
+ to = to_windows_path(to)
+
+ %Q{move "#{from}" "#{to}"}
+ end, ctx)
+ end
+
+ def copy_cmd(from, to, ctx)
+ cmd!(case
+ when posix? then %Q{cp -R "#{from}" "#{to}"}
+ else
+ from = to_windows_path(from)
+ to = to_windows_path(to)
+
+ %Q{xcopy /Y /s /i /q "#{from}" "#{to}"}
+ end, ctx)
+ end
+
def _exec in_thread, command, opts, ctx, block
@cwd = nil # clear cwd cache
@@ -940,7 +1013,7 @@ module Host
end
#
- if !opts.has_key?(:max_len) or opts[:max_len].is_a?(Integer) or
opts[:max_len] < 0
+ if !opts.has_key?(:max_len) or !opts[:max_len].is_a?(Integer) or
opts[:max_len] < 0
opts[:max_len] = 128*1024
end
@@ -949,8 +1022,6 @@ module Host
command = debug_wrap(command)
end
- stdin_data = (opts.has_key?(:stdin))? opts[:stdin] : nil
-
if in_thread
Thread.start do
ret = _exec_thread(command, opts, ctx, block)
@@ -1042,7 +1113,7 @@ module Host
return [stdout, stderr, exit_code]
end # def _exec_thread
- attr_accessor :_systeminfo, :_name, :_osname, :_systeminfo, :_systemdrive,
:_systemroot, :posix, :is_windows
+ attr_accessor :_systeminfo, :_name, :_osname, :_systeminfo, :_systemdrive,
:_systemroot, :posix, :is_windows, :_appdata, :_appdata_local, :_tempdir,
:userprofile
def clone(clone)
clone._systeminfo = @systeminfo
@@ -1054,6 +1125,10 @@ module Host
clone.posix = @posix
clone.is_windows = @is_windows
clone._name = @_name
+ clone._appdata = @_appdata
+ clone._appdata_local = @_appdata_local
+ clone._tempdir = @_tempdir
+ clone._userprofile = @_userprofile
clone
end
diff --git a/PFTT/lib/host/local.rb b/PFTT/lib/host/local.rb
index 5f9fe67..200c5ad 100644
--- a/PFTT/lib/host/local.rb
+++ b/PFTT/lib/host/local.rb
@@ -2,6 +2,7 @@
require 'java'
include_class 'java.util.Timer'
include_class 'java.util.TimerTask'
+require 'FileUtils'
module Host
class Local < HostBase
@@ -102,20 +103,6 @@ module Host
alias :upload :copy
alias :download :copy
- def exist? file, ctx=nil
- if ctx
- ctx.fs_op1(self, :exist, file) do |file|
- return exist?(file, ctx)
- end
- end
- make_absolute! file
- File.exist? file
- end
-
- alias :exists? :exist?
- alias :exist :exist?
- alias :exists :exist?
-
def directory? path, ctx=nil
if ctx
ctx.fs_op1(self, :is_dir, path) do |path|
@@ -154,8 +141,29 @@ module Host
false
end
+ def mtime(file)
+ # TODO implement for ssh
+ File.mtime(file).to_i
+ end
+
protected
+ def _exist?(file, ctx)
+ File.exist? file
+ end
+
+ def move_file(from, to, ctx)
+ FileUtils.move_file(from, to)
+ end
+
+ def copy_file(from, to, ctx, mk)
+ if mk
+ mkdir(File.dirname(to), ctx)
+ end
+
+ FileUtils.copy_file(from, to)
+ end
+
class LocalExecHandle < ExecHandle
def initialize(stdout, stderr, process=nil)
@stdout = stdout
diff --git a/PFTT/lib/host/remote/psc/client.rb
b/PFTT/lib/host/remote/psc/client.rb
index efba21c..fb7fbd5 100644
--- a/PFTT/lib/host/remote/psc/client.rb
+++ b/PFTT/lib/host/remote/psc/client.rb
@@ -25,8 +25,8 @@ class Client < BaseRemoteHostAndClient
#
# TODO temp do we really need to do this anymore?
- @host.exec!('taskkill /im:jruby.exe /f', ctx)
- @host.exec!('taskkill /im:jruby.exe /f', ctx)
+ @host.exec!('taskkill /im:jruby.exe /f', ctx, {:success_exit_code=>128})
+ @host.exec!('taskkill /im:jruby.exe /f', ctx, {:success_exit_code=>128})
rescue
puts @host.name+" "+$!.inspect+" "+$!.backtrace.inspect
end
@@ -97,30 +97,21 @@ class Client < BaseRemoteHostAndClient
# end
#
+ # TODO
+ send_php(PhpBuild.new('g:/php-sdk/PFTT-PHPS/'+$php_build_path))
@running = true
@wait_lock.synchronize do
begin
+
+
# set JAVA_HOME=\\jruby-1.6.5\\jre
- @host.exec!('_pftt_hc.cmd ""',
Tracing::Context::Phpt::RunHost.new(),
{:chdir=>@host.systemdrive+'\\php-sdk\\1\\PFTT', :stdin=>@stdin}) do |handle|
- if handle.has_stderr?
- recv_ssh_block(handle.read_stderr)
- end
- end
+ do_it()
+
rescue
puts @host.name+" "+$!.inspect+" "+$!.backtrace.inspect
- # TODO be able to resume: rerun _pftt_hc, but limit the list
of tests to those that results weren't received for
+ do_it() #again
- # retry
- begin
- @host.exec!('_pftt_hc.cmd ""',
Tracing::Context::Phpt::RunHost.new(),
{:chdir=>@host.systemdrive+'\\php-sdk\\1\\PFTT', :stdin=>@stdin}) do |handle|
- if handle.has_stderr?
- recv_ssh_block(handle.read_stderr)
- end
- end
- rescue
- puts @host.name+" "+$!.inspect+" "+$!.backtrace.inspect
- end
ensure
@running = false
@started = false
@@ -155,6 +146,13 @@ class Client < BaseRemoteHostAndClient
end
end # thread
end
+ def do_it
+ @host.exec!(@host.systemdrive+'/php-sdk/1/pftt/_pftt_hc.cmd',
Tracing::Context::Phpt::RunHost.new(), {:stdin_data=>@stdin, :max_len=>0,
:chdir=>@host.systemdrive+'\\php-sdk\\1\\PFTT', :stdin=>@stdin}) do |handle|
+ if handle.has_stderr?
+ recv_ssh_block(handle.read_stderr)
+ end
+ end
+ end
def hosted_client_failed
# terminate _pftt_hc.rb if its still running
@host.close
@@ -166,6 +164,7 @@ class Client < BaseRemoteHostAndClient
def send_full_block(block)
block += "<Boundary>\n"
+ puts block
@stdin += block
end
def dispatch_recvd_xml(xml)
@@ -194,7 +193,7 @@ class Client < BaseRemoteHostAndClient
end
end
def send_start
- send_xml({}, 'start')
+ # TODO TUE send_xml({}, 'start')
end
def send_stop
send_xml({}, 'stop')
diff --git a/PFTT/lib/host/remote/psc/recovery_manager.rb
b/PFTT/lib/host/remote/psc/recovery_manager.rb
index eeeba62..68bedee 100644
--- a/PFTT/lib/host/remote/psc/recovery_manager.rb
+++ b/PFTT/lib/host/remote/psc/recovery_manager.rb
@@ -8,12 +8,12 @@ module Host
class HostRecoveryManager
def initialize(hosts, php, middleware, scn_set)
#hosts = [hosts.first]
- php =
PhpBuild.new('C:/php-sdk/builds/php-5.4-nts-windows-vc9-x86-r319120')
+ php = PhpBuild.new('C:\\php-sdk\\builds\\php-5.4.0rc2-nts-Win32-VC9-x86')
threads = []
hosts.each do |host|
- file_name = 'C:/php-sdk/PFTT-PSCC/r319120/'+host.name
+ file_name = 'C:/php-sdk/PFTT-PSCC/540rc2'# r319120/'+host.name
if File.exists?(file_name)
#t = Thread.start do
recover(file_name, host, php, middleware, scn_set)
@@ -136,13 +136,17 @@ def to_simple(raw_xml)
when 4#Xml.TEXT
text = parser.getText();
-
+# if text.include?('+Warning: strtotime()')
+# puts text
+# end
if t['text']
t['text'] += text
else
t['text'] = text
end
-
+ if text.include?('+Warning: strtotime()')
+ puts t.inspect
+ end
when 3#Xml.END_TAG
if s.length > 1
s.pop # double pop
diff --git a/PFTT/lib/host/remote/psc/remote_host.rb
b/PFTT/lib/host/remote/psc/remote_host.rb
index ce6a4b2..4c9cd4f 100644
--- a/PFTT/lib/host/remote/psc/remote_host.rb
+++ b/PFTT/lib/host/remote/psc/remote_host.rb
@@ -39,7 +39,10 @@ class RemoteHost < BaseRemoteHostAndClient
while @run
line = STDIN.gets()
- recv_ssh_block(line)
+ # TODO fucked up recv_ssh_block(line)
+ xml = to_simple(line)
+ dispatch_recvd_xml(xml)
+ dispatch_recvd_xml({'@msg_type'=>'start'}) # TODO
end
end
def send_result(result)
diff --git a/PFTT/lib/host/remote/ssh.rb b/PFTT/lib/host/remote/ssh.rb
index 7b16a89..942104a 100644
--- a/PFTT/lib/host/remote/ssh.rb
+++ b/PFTT/lib/host/remote/ssh.rb
@@ -152,35 +152,6 @@ module Host
end
end
- def exist? path, ctx=nil
- if ctx
- ctx.fs_op1(self, :exist, path) do |path|
- return exist?(path, ctx)
- end
- end
-
- # see T_* constants in Net::SFTP::Protocol::V01::Attributes
- # v04 and v06 attributes don't have a directory? or file? method (which
v01 does)
- # doing it this way will work for all 3 (v01, v04, v06 attributes)
- begin
- a = wait_for(sftp(ctx).stat(path), :attrs)
- # types: regular(1), directory(2), symlink, special, unknown, socket,
char_device, block_device, fifo
- # # if type is any of those, then path exists
- if a.nil?
- return false
- else
- return ( a.type > 0 and a.type < 10 )
- end
- rescue
- if_closed
- return false
- end
- end
-
- alias :exists? :exist?
- alias :exist :exist?
- alias :exists :exist?
-
def list(path, ctx)
if ctx
ctx.fs_op1(self, :list, path) do |path|
@@ -267,11 +238,11 @@ module Host
# LATER remove this gotcha/rule/limitation (implement using
File.basename)
#
if windows?
- to_windows_path!(local_file)
- to_windows_path!(remote_path)
+ local_file = to_windows_path(local_file)
+ remote_path = to_windows_path(remote_path)
else
- to_posix_path!(local_file)
- to_posix_path!(remote_path)
+ local_file = to_posix_path(local_file)
+ remote_path = to_posix_path(remote_path)
end
#
@@ -318,6 +289,38 @@ module Host
protected
+ def _exist?(path, ctx)
+ # see T_* constants in Net::SFTP::Protocol::V01::Attributes
+ # v04 and v06 attributes don't have a directory? or file? method (which
v01 does)
+ # doing it this way will work for all 3 (v01, v04, v06 attributes)
+ begin
+ a = wait_for(sftp(ctx).stat(path), :attrs)
+ # types: regular(1), directory(2), symlink, special, unknown, socket,
char_device, block_device, fifo
+ # # if type is any of those, then path exists
+ if a.nil?
+ return false
+ else
+ return ( a.type > 0 and a.type < 10 )
+ end
+ rescue
+ if_closed
+ return false
+ end
+ end
+
+ def move_file(from, to, ctx)
+ move_cmd(from, to, ctx)
+ end
+
+ def copy_file(from, to, ctx, mk)
+ to = File.dirname(to)
+ if mk
+ mkdir(to, ctx)
+ end
+
+ copy_cmd(from, to, ctx)
+ end
+
# for #clone()
attr_accessor :rebooting, :rebooting_reconnect_tries
@@ -355,11 +358,21 @@ module Host
def write_stdin(stdin_data)
@channel.send_data(stdin_data)
end
+ def has_stderr?
+ @stderr.length > 0
+ end
+ def has_stdout?
+ @stdout.length > 0
+ end
def read_stderr
- @stderr
+ x = @stderr
+ @stderr = ''
+ return x
end
def read_stdout
- @stdout
+ x = @stdout
+ @stdout = ''
+ return x
end
def post_stdout(data)
@stdout = data
@@ -388,7 +401,7 @@ module Host
stdout, stderr = '',''
stdin_data = opts[:stdin_data]
exit_code = -254 # assume error unless success
-
+
ssh(ctx).open_channel do |channel|
channel.exec(command) do |channel, success|
unless success
@@ -404,7 +417,7 @@ module Host
if stdin_data
ch.send_data(stdin_data)
stdin_data = nil
- end
+ end
if block
sh.post_stdout(data)
block.call(sh)
@@ -470,11 +483,11 @@ module Host
def _delete path, ctx
if windows?
- to_windows_path!(path)
+ path = to_windows_path(path)
cmd!("DEL /Q /F \"#{path}\"", ctx)
else
- to_posix_path!(path)
+ path = to_posix_path(path)
exec!("rm -rf \"#{path}\"", ctx)
end
diff --git a/PFTT/lib/middleware.rb b/PFTT/lib/middleware.rb
index 734ae25..06c2331 100644
--- a/PFTT/lib/middleware.rb
+++ b/PFTT/lib/middleware.rb
@@ -98,14 +98,19 @@ module Middleware
# if $force_deploy, make a new directory! otherwise, reuse existing
directory (for quick manual testing can't take the time
# to copy everything again)
- @deployed_php ||= @host.join(deploy_to, ( @php_build[:version] +
((@php_build[:threadsafe])?'-TS':'-NTS') + ( $force_deploy ?
'_'+String.random(4) : '' ) ) )
+ @deployed_php ||= @php_build.path # TODO @host.join(deploy_to, (
@php_build[:version] + ((@php_build[:threadsafe])?'-TS':'-NTS') + (
$force_deploy ? '_'+String.random(4) : '' ) ) )
#
# TODO TUE if $force_deploy or not File.exists?(php_binary()) or
File.mtime(@php_build.path) >= File.mtime(php_binary())
unless $hosted_int
puts "PFTT:deploy: uploading... "+@deployed_php
- host.upload_force("c:/php-sdk/5.4.0beta2-NTS.7z",
host.systemdrive(ctx)+'/5.4.0beta2-NTS.7z', false, ctx) # critical: false
+
+ zip_name = package_php_build(Host::Local.new(),
'c:/php-sdk/builds/'+$php_build_path)
+
+ # TODO package_php_build
+ host.upload_force(zip_name, host.systemdrive(ctx)+'/5.4.0beta2-NTS.7z',
false, ctx) # critical: false
+ # TODO check if build already on remote host (so compression and upload
can be skipped)
sd = host.systemdrive
ctx = Tracing::Context::Dependency::Check.new # TODO ctx.new
host.delete_if("#{sd}\\php-sdk\\PFTT-PHPs\\5.4.0beta2-NTS", ctx)
diff --git a/PFTT/lib/middleware/cli.rb b/PFTT/lib/middleware/cli.rb
index 622283b..8189aae 100644
--- a/PFTT/lib/middleware/cli.rb
+++ b/PFTT/lib/middleware/cli.rb
@@ -20,10 +20,10 @@ module Middleware
# phpt thread)
config_ctx = Tracing::Context::Middleware::Config.new()
- @host.exec!('REG DELETE "HKLM\\Software\\Microsoft\\Windows
NT\\CurrentVersion\\AeDebug" /v Debugger /f', config_ctx)
+ # TODO @host.exec!('REG DELETE "HKLM\\Software\\Microsoft\\Windows
NT\\CurrentVersion\\AeDebug" /v Debugger /f', config_ctx)
# disable Hard Error Popup Dialog boxes (will still get this even
without a debugger)
# see http://support.microsoft.com/kb/128642
- @host.exec!('REG ADD
"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Windows" /v ErrorMode /d 2 /t
REG_DWORD /f', config_ctx)
+ # TODO @host.exec!('REG ADD
"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Windows" /v ErrorMode /d 2 /t
REG_DWORD /f', config_ctx)
# disable windows firewall
# LATER edit firewall rules instead (what if on public network, ex:
Azure)
diff --git a/PFTT/lib/test/case/phpt.rb b/PFTT/lib/test/case/phpt.rb
index 8bc78c7..cdc3ad7 100644
--- a/PFTT/lib/test/case/phpt.rb
+++ b/PFTT/lib/test/case/phpt.rb
@@ -497,7 +497,9 @@ class Phpt
@parts[:file][email protected](:fileeof).gsub(/\r?\n\Z/,'')
elsif @parts.has_key? :file_external
context = File.dirname( @phpt_path )
- external_file = File.absolute_path(
@parts.delete(:file_external).gsub(/\r?\n\Z/,''), context )
+ external_file = File.absolute_path(
@parts.delete(:file_external).gsub(/\r?\n\Z/,''), context )
+ # TODO TUE c:/abc/
+ # ('c:/abc/'+external_file).gsub('C:/php-sdk/0/PFTT2/PFTT', '')
@parts[:file]= IO.read( external_file ).lines do |line|
parse_line line, context
end
diff --git a/PFTT/lib/test/result/phpt.rb b/PFTT/lib/test/result/phpt.rb
index 014a09e..42db006 100644
--- a/PFTT/lib/test/result/phpt.rb
+++ b/PFTT/lib/test/result/phpt.rb
@@ -17,20 +17,33 @@ module Test
end
def self.from_xml(xml, test_bench, deploydir, php)
- rclass = case xml['@result_type']
- when 'XSkip'
+ rclass = case xml['@status']
+ when 'xskip'
Test::Result::Phpt::XSkip
- when 'Skip'
+ when 'skip'
Test::Result::Phpt::Skip
- when 'Bork'
+ when 'bork'
Test::Result::Phpt::Bork
- when 'Unsupported'
+ when 'unsupported'
Test::Result::Phpt::Unsupported
- when 'Mean'
+ when 'xfail'
+ Test::Result::Phpt::Meaningful
+ when 'fail'
+ Test::Result::Phpt::Meaningful
+ when 'works'
+ Test::Result::Phpt::Meaningful
+ when 'pass'
Test::Result::Phpt::Meaningful
end
- if xml['@status'] == 'unsupported'
+ if rclass.nil?
+# puts rclass
+# puts xml['@result_type']
+# puts xml.inspect
+ end
+
+ if xml['@status'] == 'unsupported' or xml['@status'] == 'bork'
r = rclass.new(Test::Case::Phpt.from_xml(xml['test_case'][0]),
test_bench, deploydir, php)
+ # TODO included borked reasons array (field)
elsif xml.has_key?('@reason')
r = rclass.new(Test::Case::Phpt.from_xml(xml['test_case'][0]),
test_bench, deploydir, php, xml['@reason'])
else
@@ -51,6 +64,7 @@ module Test
result_str = result_str.to_s
end
r = rclass.new(Test::Case::Phpt.from_xml(xml['test_case'][0]),
test_bench, deploydir, php, result_str)
+
end
case xml['@status']
when 'pass'
@@ -70,11 +84,14 @@ module Test
when 'xfail'
r.status = :xfail
end
+ #puts xml.inspect
if xml.has_key?('diff')
+ #r.diff = xml['diff'][0]['result_str'][0]['text']
r.diff = xml['diff'][0]
unless r.diff.is_a?(String)
r.diff = ''
end
+ #puts xml.inspect
end
r.set_files
return r
@@ -82,24 +99,24 @@ module Test
def to_xml
#
- result_type = case self.class
- when Test::Result::Phpt::XSkip
- 'XSkip'
- when Test::Result::Phpt::Skip
- 'Skip'
- when Test::Result::Phpt::Bork
- 'Bork'
- when Test::Result::Phpt::Unsupported
- 'Unsupported'
- when Test::Result::Phpt::Meaningful
- 'Meaningful'
- else
- ''
- end
+# result_type = case self.class
+# when Test::Result::Phpt::XSkip
+# 'XSkip'
+# when Test::Result::Phpt::Skip
+# 'Skip'
+# when Test::Result::Phpt::Bork
+# 'Bork'
+# when Test::Result::Phpt::Unsupported
+# 'Unsupported'
+# when Test::Result::Phpt::Meaningful
+# 'Meaningful'
+# else
+# ''
+# end
#
xml = {
- '@result_type' => result_type,
+ #'@result_type' => result_type,
'test_case' => @test_case.to_xml,
'@status' => @status
}
@@ -277,8 +294,10 @@ module Test
unless @diff.is_a?(String)
@diff = @diff.to_s
end
+ #puts @diff.length.to_s
if @diff.length > 0
files['diff']= @diff
+
end
end
attr_accessor :diff
diff --git a/PFTT/lib/test/runner/stage/phpt/package.rb
b/PFTT/lib/test/runner/stage/phpt/package.rb
index 36813f1..440f58c 100644
--- a/PFTT/lib/test/runner/stage/phpt/package.rb
+++ b/PFTT/lib/test/runner/stage/phpt/package.rb
@@ -7,10 +7,10 @@ module Test
class Package < Tracing::Stage
def run()
- notify_start
+ notify_start
puts 'PFTT:compress: compressing PHPTs...'
- local_phpt_zip = package_svn(Host::Local.new(),
'c:/php-sdk/svn/branches/PHP_5_4')
+ local_phpt_zip = package_svn(Host::Local.new(), $phpt_path)
puts 'PFTT:compress: compressed PHPTs...'
notify_end(true)
@@ -18,7 +18,7 @@ class Package < Tracing::Stage
return local_phpt_zip
end
-end
+end # class Package
end # module PHPT
end # module Stage
diff --git a/PFTT/lib/test/runner/stage/phpt/upload.rb
b/PFTT/lib/test/runner/stage/phpt/upload.rb
index 33d36c9..be758be 100644
--- a/PFTT/lib/test/runner/stage/phpt/upload.rb
+++ b/PFTT/lib/test/runner/stage/phpt/upload.rb
@@ -12,20 +12,20 @@ class Upload < Tracing::Stage::ByHostMiddleware
local_host = Host::Local.new()
- upload_7zip(local_host, host)
- remote_phpt_zip = host.systemdrive+'/PHP_5_4.7z'
- host.upload_force(local_phpt_zip, remote_phpt_zip,
Tracing::Context::Phpt::Upload.new)
- host.delete_if(host.systemdrive+'/abc',
Tracing::Context::Phpt::Decompress.new)
- host.delete_if(host.systemdrive+'/PHP_5_4',
Tracing::Context::Phpt::Decompress.new)
- # TODO unpackage(host, host.systemdrive, remote_phpt_zip)
- sd = host.systemdrive
- #sleep(20)
- # critical: must chdir to output directory or directory where 7zip
file is stored!!!
- host.exec!("#{sd}\\php-sdk\\bin\\7za.exe x -o#{sd}\\
#{sd}\\PHP_5_4.7z ", Tracing::Context::Phpt::Decompress.new,
{:chdir=>"#{sd}\\", :null_output=>true})
- # TODO host.move(host.systemdrive+'/PHP_5_4',
host.systemdrive+'/abc')
- #sleep(20)
- # TODO use test_ctx.new
- host.cmd!("move #{sd}\\php_5_4 #{sd}\\abc",
Tracing::Context::PhpBuild::Compress.new)
+ upload_7zip(local_host, host)
+ remote_phpt_zip = host.systemdrive+'/PHP_5_4.7z'
+ host.upload_force(local_phpt_zip, remote_phpt_zip,
Tracing::Context::Phpt::Upload.new)
+ host.delete_if(host.systemdrive+'/abc',
Tracing::Context::Phpt::Decompress.new)
+ host.delete_if(host.systemdrive+'/PHP_5_4',
Tracing::Context::Phpt::Decompress.new)
+ # TODO unpackage(host, host.systemdrive, remote_phpt_zip)
+ sd = host.systemdrive
+ #sleep(20)
+ # critical: must chdir to output directory or directory where 7zip file is
stored!!!
+ host.exec!("#{sd}\\php-sdk\\bin\\7za.exe x -o#{sd}\\ #{sd}\\PHP_5_4.7z ",
Tracing::Context::Phpt::Decompress.new, {:chdir=>"#{sd}\\", :null_output=>true})
+ # TODO host.move(host.systemdrive+'/PHP_5_4', host.systemdrive+'/abc')
+ #sleep(20)
+ # TODO use test_ctx.new
+ host.cmd!("move #{sd}\\php_5_4 #{sd}\\abc",
Tracing::Context::PhpBuild::Compress.new)
notify_end(true)
end # def run
diff --git a/PFTT/lib/tracing/context.rb b/PFTT/lib/tracing/context.rb
index fd1a514..9d7fd8a 100644
--- a/PFTT/lib/tracing/context.rb
+++ b/PFTT/lib/tracing/context.rb
@@ -112,7 +112,7 @@ module Tracing
while true do
# TODO empty all STDIN chars now (before user is prompted to enter a
char we shouldn't ignore)
-
+
# show the prompt line to the user
STDOUT.write(host.name+'('+host.osname_short+')'+prompt.prompt_str)
diff --git a/PFTT/lib/tracing/prompt/diff.rb b/PFTT/lib/tracing/prompt/diff.rb
index 5c7e5b3..57a7651 100644
--- a/PFTT/lib/tracing/prompt/diff.rb
+++ b/PFTT/lib/tracing/prompt/diff.rb
@@ -3,37 +3,144 @@
# TODO be able to print a list of all insertions or all insertions contianing
'Warning', etc...
module Tracing
module Prompt
-
-class Diff < TestCaseRunPrompt
+ module Diff
+
+
+
+#class BaseDiff < TestCaseRunPrompt
+#
+# def axis_label
+#
+# end
+#
+# def prompt_str
+# axis_label+'::Diff> '
+# end
+#
+#
+# def help
+# super
+# puts
+# puts 'Axes: [Run] [Host] [[Zoom] [Diff]]'
+# puts ' [Run] - Base - only Base run Test - only Test run'
+# puts ' Base+Test - all results from Base and Test (duplicates
removed)'
+# puts ' Base=Test - only results from Base and Test that match'
+# puts ' Test-Base - only results from Base not matching those
in Test run'
+# puts ' Base-Test - only results from Test not matching those
in Base run'
+# puts ' [Host] - All - all selected hosts {Hostname} - named host'
+# puts ' [Zoom] - A - All tests E - One extension'
+# puts ' T - One test case L - One line from a
test case'
+# puts ' C - One chunk from one line'
+# puts ' [Diff] - E - original Expected output A - original Actual
output'
+# puts ' + - only added output - - only removed output'
+# puts ' d - + and -'
+# puts ' e - Expected output (changed?) a - Actual (changed?)'
+# puts
+# help_diff_cmds
+# puts ' c - change'
+# puts ' C - change (&next)'
+# puts ' t - Triage'
+# puts ' h - help'
+# puts ' v - view diff'
+# puts ' V - view PHPT documentation'
+# puts ' y - save chunk replacements'
+# puts ' Y - load chunk replacements'
+# puts ' k - save diff to file'
+# puts ' l - locate - os middleware build and other info'
+# help_zoom_out
+# puts ' F - find'
+# puts ' R - run'
+# puts ' N - network/host'
+# puts ' Z - zoom'
+# puts ' D - diff'
+#
+# end # def help
+#
+# def help_zoom_out
+# # top level can't zoom out
+# end
+#
+# def confirm_find
+# true
+# end
+#
+# def help_diff_cmds
+# end
+#
+#end # class BaseDiff
+#
+#class All < BaseDiff
+#
+#
+# def confirm_find
+# # LATER ask user to confirm searching everything (b/c it can be slow)
+# end
+#end
+#
+#class BaseNotAll < BaseDiff
+# def help_zoom_out
+# puts ' o - zoom out'#
+# end
+#
+# def execute(ans)
+# if ans == 'o'
+# end
+# end
+#end
+#
+#class Ext < BaseNotAll
+#end
+#
+#class TestCaseLineChunk < BaseNotAll
+# def help_diff_cmds
+# puts ' d - delete (&next)'
+# puts ' a - add (&next)'
+# puts ' s - skip (&next)'
+# puts ' p - pass (&next)'
+# end
+#
+# def execute(ans)
+# if ans == 'd' or ans == '-'
+# elsif ans == 'a' or ans == '+'
+# elsif ans == 's'
+# elsif ans == 'p'
+# end
+# end
+#end
+#
+#class TestCase < TestCaseLineChunk
+#end
+#
+#class Line < TestCaseLineChunk
+#end
+#
+#class Chunk < TestCaseLineChunk
+#end
+#
+class Diff
def initialize(dlm)
@dlm = dlm
# TODO
end
-
- def help
- super
- puts ' d - (or -) delete: modify expect'
- puts ' a - (or +) add: modify expect'
- puts ' i - ignore: remove from diffs' # TODO
- puts ' s - skip line, count'
- puts ' m - display more commands (this whole list)'
- puts ' t - Triage diffs in this test'
- puts ' T - Triage all diffs from all tests'
- puts ' r - replace expect with regex to match actual'
- puts ' R - replace all in file'
- puts ' A - replace all in test case set'
- puts ' l - show modified expect line (or original if not modified)'
- puts ' L - show original expect line'
- puts ' P - show original expect section'
- puts ' p - show modified expect section (or original if not modified)'
- puts ' v - show PHPT file format documentation (HTML)'
- puts ' H - highlight diff (Swing UI)'
- puts ' y - save chunk replacements to file'
- puts ' Y - load chunk replacements from a file'
- puts ' k - save diff to file (insertions and deletions)'
- puts ' K - save all inserted chunks to file'
- end # def help
+
+
+
+ def show_diff
+ if host.windows?
+ if host.exist?('winmerge')
+ host.exec!("winmerge #{expected} #{result}")
+ return
+ end
+ else
+ if host.exec('winmeld')
+ host.exec("winmeld #{expected} #{result}")
+ return
+ end
+ end
+
+ # LATER fallback swing display
+ end
def execute(ans)
if ans=='-' or ans=='d'
diff --git a/PFTT/lib/util/package.rb b/PFTT/lib/util/package.rb
index 414347f..b41b302 100644
--- a/PFTT/lib/util/package.rb
+++ b/PFTT/lib/util/package.rb
@@ -17,12 +17,21 @@ end
def package_php_build(local_host, build_path)
ctx = Tracing::Context::PhpBuild::Compress.new
- zip_name = File.basename(build_path)+'.7z'
+ cached_zip_name = 'c:/php-sdk/0/PFTT2/cache/PHP_5_4.7z'
+ if local_host.exist?(cached_zip_name)
+ puts "PFTT: compress: reusing #{cached_zip_name}"
+ return cached_zip_name
+ end
+
+ zip_name = local_host.mktempdir(ctx) + '/' + File.basename(build_path)+'.7z'
local_host.format_path!(zip_name, ctx)
local_host.format_path!(build_path, ctx)
- local_host.exec!(host.systemdrive+"/php-sdk/0/PFTT2/PFTT/7za #{zip_name}
#{build_path}", ctx)
+ local_host.exec!(local_host.systemdrive+"/php-sdk/0/PFTT2/PFTT/7za a
#{zip_name} #{build_path}", ctx)
+
+ # cache archive for next time
+ local_host.copy(zip_name, cached_zip_name, ctx)
return zip_name
end
@@ -40,33 +49,73 @@ end
def package_svn(local_host, path)
ctx = Tracing::Context::PhpBuild::Compress.new
- tmp_dir = File.join(local_host.tmpdir(ctx), File.basename(path))
+ tmp_dir = File.join(local_host.mktmpdir(ctx), File.basename(path))
+
+ # put the PHPTs in a sub-folder of tmp_dir (tmp_dir will also store the
archive)
+ package_tmp_dir = tmp_dir+'/PHP_5_4' # TODO
- local_host.format_path!(tmp_dir, ctx)
+ test_dirs = {}
+ greatest_mtime = 0
- # there's a bunch of files we should remove/not include in zip, copy folder
to a temporary folder
- # and then remove the files from that and then compress it
- local_host.copy(path, tmp_dir, ctx)
+ # copy all files, sub folders and files from any directory named 'test' (in
ext, sapi, tests, or Zend, or others that get added)
+ # (don't need the complete source code, just the PHPTs and files they might
need)
+ # (the fewer the files, the smaller the archive, the faster the test cycle
=> greater SDE/SDET productivity)
+ # TODO local_host.glob(path, '**/*', ctx) do |file|
+ Dir.glob(path+'/**/*') do |file|
+ # for each file
+ s_file = Host.sub(path, file)
+
+ if s_file.include?('/tests/') or s_file.include?('/test/')
+
+ mtime = local_host.mtime(file)
+ if mtime > greatest_mtime
+ greatest_mtime = mtime
+ end
+
+ # TODO cache test_files copy and only add to test_dirs if the cached
file is older or missing
+
+ # save the list of dirs... we could copy each test file, buts its fewer
copy operations
+ # to copy entire directories => so the copy process is faster
+ test_dirs[File.dirname(file)] = File.dirname(Host.join(package_tmp_dir,
s_file))
+ end
+ end
+ #
- # remove any .svn directories
- if local_host.windows?(ctx)
- # FOR /F "tokens=*" %G IN ('DIR /B /AD /S *.svn*') DO RMDIR /S /Q "%G"
- local_host.cmd!("FOR /F \"tokens=*\" %%G IN ('DIR /B /AD /S *.svn*') DO
RMDIR /S /Q \"%%G\"'", ctx)
- else
- local_host.exec!("rm -rf `find . -type d -name .svn`", ctx)
+ # TODO (also have separate cache folders for test files, builds, etc...)
+ cached_zip_name = 'c:/php-sdk/0/PFTT2/cache/PHPT_5_4.7z'
+
+ if local_host.exists?(cached_zip_name)
+ # if none of the test files has been modified since creation of cached
copy of archive, then
+ # there is no need to copy and archive the test files a second time (just
use the cached archive)
+ if local_host.mtime(cached_zip_name) > greatest_mtime
+ # don't need to copy+compress again
+ puts "PFTT: reusing cached tests #{cached_zip_name}"
+ return cached_zip_name
+ # TODO check copy on remote_host
+ end
+ puts "PFTT: tests updated, re-creating #{cached_zip_name}"
end
+ # go ahead and create a new archive of test_files
+ #
- # remove Release and Release_TS (maybe this svn copy was compiled?)
- local_host.delete_if(File.join(tmp_dir, 'Release'), ctx)
- local_host.delete_if(File.join(tmp_dir, 'Release_TS'), ctx)
- # LATER local_host.delete_if(File.join(tmp_dir, 'php_test_results_*'))
+ # copy test dirs
+ test_dirs.each do |entry|
+ local_host.copy(entry[0], entry[1], ctx)
+ end
+
+ # LATER check path for any PHPTs that didn't get included (PHPTs in wrong
place)
zip_name = tmp_dir+'.7z'
- # we've removed as much as we really can, compress it
- local_host.exec!(local_host.systemdrive+"/php-sdk/0/PFTT2/PFTT/7za a
#{zip_name} #{tmp_dir}", ctx)
+ # make it even smaller, compress it! (copying lots of little files takes a
lot of overhead bandwidth)
+ local_host.exec!(local_host.systemdrive+"/php-sdk/0/PFTT2/PFTT/7za a
#{zip_name} #{package_tmp_dir}", ctx)
+
+ # cleanup temp dir
+ local_host.delete(package_tmp_dir, ctx)
- local_host.delete(tmp_dir, ctx)
+ # cache archive for next time
+ local_host.copy(zip_name, cached_zip_name, ctx)
+ # return archive name (LATER delete tmp_dir and zip_name when no longer
needed)
return zip_name
end
diff --git a/PFTT/src/se/datadosen/component/RiverLayout.class
b/PFTT/src/se/datadosen/component/RiverLayout.class
new file mode 100644
index 0000000..44c7c16
Binary files /dev/null and b/PFTT/src/se/datadosen/component/RiverLayout.class
differ
diff --git a/PFTT/src/se/datadosen/component/RiverLayout.java
b/PFTT/src/se/datadosen/component/RiverLayout.java
new file mode 100644
index 0000000..2c24489
--- /dev/null
+++ b/PFTT/src/se/datadosen/component/RiverLayout.java
@@ -0,0 +1,492 @@
+package se.datadosen.component;
+
+import java.io.ObjectInputStream;
+import java.io.IOException;
+import java.awt.*;
+import java.util.*;
+
+/**
+ * <p>RiverLayout makes it very simple to construct user interfaces as
components
+ * are laid out similar to how text is added to a word processor (Components
flow
+ * like a "river". RiverLayout is however much more powerful than FlowLayout:
+ * Components added with the add() method generally gets laid out horizontally,
+ * but one may add a string before the component being added to specify
"constraints"
+ * like this:
+ * add("br hfill", new JTextField("Your name here");
+ * The code above forces a "line break" and extends the added component
horizontally.
+ * Without the "hfill" constraint, the component would take on its preferred
size.
+ *</p>
+ * <p>
+ * List of constraints:<ul>
+ * <li>br - Add a line break
+ * <li>p - Add a paragraph break
+ * <li>tab - Add a tab stop (handy for constructing forms with labels followed
by fields)
+ * <li>hfill - Extend component horizontally
+ * <li>vfill - Extent component vertically (currently only one allowed)
+ * <li>left - Align following components to the left (default)
+ * <li>center - Align following components horizontally centered
+ * <li>right - Align following components to the right
+ * <li>vtop - Align following components vertically top aligned
+ * <li>vcenter - Align following components vertically centered (default)
+ * </ul>
+ * </p>
+ * RiverLayout is LGPL licenced - use it freely in free and commercial programs
+ *
+ * @author David Ekholm
+ * @version 1.1 (2005-05-23) -Bugfix: JScrollPanes were oversized (sized to
their containing component)
+ * if the container containing the JScrollPane was resized.
+ */
+public class RiverLayout
+ extends FlowLayout
+ implements LayoutManager, java.io.Serializable {
+
+ public static final String LINE_BREAK = "br";
+ public static final String PARAGRAPH_BREAK = "p";
+ public static final String TAB_STOP = "tab";
+ public static final String HFILL = "hfill";
+ public static final String VFILL = "vfill";
+ public static final String LEFT = "left";
+ public static final String RIGHT = "right";
+ public static final String CENTER = "center";
+ public static final String VTOP = "vtop";
+ public static final String VCENTER = "vcenter";
+
+ Map constraints = new HashMap();
+ String valign = VCENTER;
+ int hgap;
+ int vgap;
+ Insets extraInsets;
+ Insets totalInsets = new Insets(0, 0, 0, 0);// Dummy values. Set by
getInsets()
+
+
+ public RiverLayout() {
+ this(10, 5);
+ }
+
+ public RiverLayout(int hgap, int vgap) {
+ this.hgap = hgap;
+ this.vgap = vgap;
+ setExtraInsets(new Insets(0, hgap, hgap, hgap));
+ }
+
+ /**
+ * Gets the horizontal gap between components.
+ */
+ public int getHgap() {
+ return hgap;
+ }
+
+ /**
+ * Sets the horizontal gap between components.
+ */
+ public void setHgap(int hgap) {
+ this.hgap = hgap;
+ }
+
+ /**
+ * Gets the vertical gap between components.
+ */
+ public int getVgap() {
+ return vgap;
+ }
+
+ public Insets getExtraInsets() {
+ return extraInsets;
+ }
+
+ public void setExtraInsets(Insets newExtraInsets) {
+ extraInsets = newExtraInsets;
+ }
+
+ protected Insets getInsets(Container target) {
+ Insets insets = target.getInsets();
+ totalInsets.top = insets.top + extraInsets.top;
+ totalInsets.left = insets.left + extraInsets.left;
+ totalInsets.bottom = insets.bottom + extraInsets.bottom;
+ totalInsets.right = insets.right + extraInsets.right;
+ return totalInsets;
+ }
+
+ /**
+ * Sets the vertical gap between components.
+ */
+ public void setVgap(int vgap) {
+ this.vgap = vgap;
+ }
+
+
+ /**
+ * @param name the name of the component
+ * @param comp the component to be added
+ */
+ public void addLayoutComponent(String name, Component comp) {
+ constraints.put(comp, name);
+ }
+
+ /**
+ * Removes the specified component from the layout. Not used by
+ * this class.
+ * @param comp the component to remove
+ * @see java.awt.Container#removeAll
+ */
+ public void removeLayoutComponent(Component comp) {
+ constraints.remove(comp);
+ }
+
+ boolean isFirstInRow(Component comp) {
+ String cons = (String) constraints.get(comp);
+ return cons != null && (cons.indexOf(RiverLayout.LINE_BREAK) != -1 ||
+ cons.indexOf(RiverLayout.PARAGRAPH_BREAK) != -1);
+ }
+
+ boolean hasHfill(Component comp) {
+ return hasConstraint(comp, RiverLayout.HFILL);
+ }
+
+ boolean hasVfill(Component comp) {
+ return hasConstraint(comp, RiverLayout.VFILL);
+ }
+
+ boolean hasConstraint(Component comp, String test) {
+ String cons = (String) constraints.get(comp);
+ if (cons == null) return false;
+ StringTokenizer tokens = new StringTokenizer(cons);
+ while (tokens.hasMoreTokens())
+ if (tokens.nextToken().equals(test)) return true;
+ return false;
+ }
+
+ /**
+ * Figure out tab stop x-positions
+ */
+ protected Ruler calcTabs(Container target) {
+ Ruler ruler = new Ruler();
+ int nmembers = target.getComponentCount();
+
+ int x = 0;
+ int tabIndex = 0; // First tab stop
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+// if (m.isVisible()) {
+ if (isFirstInRow(m) || i == 0) {
+ x = 0;
+ tabIndex = 0;
+ }
+ else x+= hgap;
+ if (hasConstraint(m, TAB_STOP)) {
+ ruler.setTab(tabIndex, x); // Will only increase
+ x = ruler.getTab(tabIndex++); // Jump forward if neccesary
+ }
+ Dimension d = m.getPreferredSize();
+ x += d.width;
+ }
+// }
+ return ruler;
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given the
+ * <i>visible</i> components in the specified target container.
+ * @param target the component which needs to be laid out
+ * @return the preferred dimensions to lay out the
+ * subcomponents of the specified container
+ * @see Container
+ * @see #minimumLayoutSize
+ * @see java.awt.Container#getPreferredSize
+ */
+ public Dimension preferredLayoutSize(Container target) {
+ synchronized (target.getTreeLock()) {
+ Dimension dim = new Dimension(0, 0);
+ Dimension rowDim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+ boolean firstVisibleComponent = true;
+ int tabIndex = 0;
+ Ruler ruler = calcTabs(target);
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+// if (m.isVisible()) {
+ if (isFirstInRow(m)) {
+ tabIndex = 0;
+ dim.width = Math.max(dim.width, rowDim.width);
+ dim.height += rowDim.height + vgap;
+ if (hasConstraint(m, PARAGRAPH_BREAK)) dim.height += 2*vgap;
+ rowDim = new Dimension(0, 0);
+ }
+ if (hasConstraint(m, TAB_STOP)) rowDim.width =
ruler.getTab(tabIndex++);
+ Dimension d = m.getPreferredSize();
+ rowDim.height = Math.max(rowDim.height, d.height);
+ if (firstVisibleComponent) {
+ firstVisibleComponent = false;
+ }
+ else {
+ rowDim.width += hgap;
+ }
+ rowDim.width += d.width;
+ // }
+ }
+ dim.width = Math.max(dim.width, rowDim.width);
+ dim.height += rowDim.height;
+
+ Insets insets = getInsets(target);
+ dim.width += insets.left + insets.right;// + hgap * 2;
+ dim.height += insets.top + insets.bottom;// + vgap * 2;
+ return dim;
+ }
+ }
+
+ /**
+ * Returns the minimum dimensions needed to layout the <i>visible</i>
+ * components contained in the specified target container.
+ * @param target the component which needs to be laid out
+ * @return the minimum dimensions to lay out the
+ * subcomponents of the specified container
+ * @see #preferredLayoutSize
+ * @see java.awt.Container
+ * @see java.awt.Container#doLayout
+ */
+ public Dimension minimumLayoutSize(Container target) {
+ synchronized (target.getTreeLock()) {
+ Dimension dim = new Dimension(0, 0);
+ Dimension rowDim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+ boolean firstVisibleComponent = true;
+ int tabIndex = 0;
+ Ruler ruler = calcTabs(target);
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ // if (m.isVisible()) {
+ if (isFirstInRow(m)) {
+ tabIndex = 0;
+ dim.width = Math.max(dim.width, rowDim.width);
+ dim.height += rowDim.height + vgap;
+ if (hasConstraint(m, PARAGRAPH_BREAK)) dim.height += 2*vgap;
+ rowDim = new Dimension(0, 0);
+ }
+ if (hasConstraint(m, TAB_STOP)) rowDim.width =
ruler.getTab(tabIndex++);
+ Dimension d = m.getMinimumSize();
+ rowDim.height = Math.max(rowDim.height, d.height);
+ if (firstVisibleComponent) {
+ firstVisibleComponent = false;
+ }
+ else {
+ rowDim.width += hgap;
+ }
+ rowDim.width += d.width;
+// }
+ }
+ dim.width = Math.max(dim.width, rowDim.width);
+ dim.height += rowDim.height;
+
+ Insets insets = getInsets(target);
+ dim.width += insets.left + insets.right;// + hgap * 2;
+ dim.height += insets.top + insets.bottom;// + vgap * 2;
+ return dim;
+ }
+ }
+
+ /**
+ * Centers the elements in the specified row, if there is any slack.
+ * @param target the component which needs to be moved
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width dimensions
+ * @param height the height dimensions
+ * @param rowStart the beginning of the row
+ * @param rowEnd the the ending of the row
+ */
+ protected void moveComponents(Container target, int x, int y, int width,
+ int height,
+ int rowStart, int rowEnd, boolean ltr, Ruler
ruler) {
+ synchronized (target.getTreeLock()) {
+ switch (getAlignment()) {
+ case FlowLayout.LEFT:
+ x += ltr ? 0 : width;
+ break;
+ case FlowLayout.CENTER:
+ x += width / 2;
+ break;
+ case FlowLayout.RIGHT:
+ x += ltr ? width : 0;
+ break;
+ case LEADING:
+ break;
+ case TRAILING:
+ x += width;
+ break;
+ }
+ int tabIndex = 0;
+ for (int i = rowStart; i < rowEnd; i++) {
+ Component m = target.getComponent(i);
+// if (m.isVisible()) {
+ if (hasConstraint(m, TAB_STOP)) x = getInsets(target).left +
ruler.getTab(tabIndex++);
+ int dy = (valign == VTOP) ? 0 : (height - m.getHeight()) / 2;
+ if (ltr) {
+ m.setLocation(x, y + dy);
+ }
+ else {
+ m.setLocation(target.getWidth() - x - m.getWidth(),
+ y + dy);
+ }
+ x += m.getWidth() + hgap;
+// }
+ }
+ }
+ }
+
+
+ protected void relMove(Container target, int dx, int dy, int rowStart,
+ int rowEnd) {
+ synchronized (target.getTreeLock()) {
+ for (int i = rowStart; i < rowEnd; i++) {
+ Component m = target.getComponent(i);
+// if (m.isVisible()) {
+ m.setLocation(m.getX() + dx, m.getY() + dy);
+// }
+ }
+
+ }
+ }
+
+ protected void adjustAlignment(Component m) {
+ if (hasConstraint(m, RiverLayout.LEFT)) setAlignment(FlowLayout.LEFT);
+ else if (hasConstraint(m, RiverLayout.RIGHT))
setAlignment(FlowLayout.RIGHT);
+ else if (hasConstraint(m, RiverLayout.CENTER))
setAlignment(FlowLayout.CENTER);
+ if (hasConstraint(m, RiverLayout.VTOP)) valign = VTOP;
+ else if (hasConstraint(m, RiverLayout.VCENTER)) valign = VCENTER;
+
+ }
+ /**
+ * Lays out the container. This method lets each component take
+ * its preferred size by reshaping the components in the
+ * target container in order to satisfy the constraints of
+ * this <code>FlowLayout</code> object.
+ * @param target the specified component being laid out
+ * @see Container
+ * @see java.awt.Container#doLayout
+ */
+ public void layoutContainer(Container target) {
+ setAlignment(FlowLayout.LEFT);
+ synchronized (target.getTreeLock()) {
+ Insets insets = getInsets(target);
+ int maxwidth = target.getWidth() -
+ (insets.left + insets.right);
+ int maxheight = target.getHeight() -
+ (insets.top + insets.bottom);
+
+ int nmembers = target.getComponentCount();
+ int x = 0, y = insets.top + vgap;
+ int rowh = 0, start = 0, moveDownStart = 0;
+
+ boolean ltr = target.getComponentOrientation().isLeftToRight();
+ Component toHfill = null;
+ Component toVfill = null;
+ Ruler ruler = calcTabs(target);
+ int tabIndex = 0;
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ //if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ m.setSize(d.width, d.height);
+
+ if (isFirstInRow(m)) tabIndex = 0;
+ if (hasConstraint(m, TAB_STOP)) x = ruler.getTab(tabIndex++);
+ if (!isFirstInRow(m)) {
+ if (i > 0 && !hasConstraint(m, TAB_STOP)) {
+ x += hgap;
+ }
+ x += d.width;
+ rowh = Math.max(rowh, d.height);
+ }
+ else {
+ if (toVfill != null && moveDownStart == 0) {
+ moveDownStart = i;
+ }
+ if (toHfill != null) {
+ toHfill.setSize(toHfill.getWidth() + maxwidth - x,
+ toHfill.getHeight());
+ x = maxwidth;
+ }
+ moveComponents(target, insets.left, y,
+ maxwidth - x,
+ rowh, start, i, ltr, ruler);
+ x = d.width;
+ y += vgap + rowh;
+ if (hasConstraint(m, PARAGRAPH_BREAK)) y += 2*vgap;
+ rowh = d.height;
+ start = i;
+ toHfill = null;
+ }
+ //}
+ if (hasHfill(m)) {
+ toHfill = m;
+ }
+ if (hasVfill(m)) {
+ toVfill = m;
+ }
+ adjustAlignment(m);
+ }
+
+ if (toVfill != null && moveDownStart == 0) { // Don't move anything
if hfill component is last component
+ moveDownStart = nmembers;
+ }
+ if (toHfill != null) { // last component
+ toHfill.setSize(toHfill.getWidth() + maxwidth - x,
+ toHfill.getHeight());
+ x = maxwidth;
+ }
+ moveComponents(target, insets.left, y, maxwidth - x, rowh,
+ start, nmembers, ltr, ruler);
+ int yslack = maxheight - (y+rowh);
+ if (yslack != 0 && toVfill != null) {
+ toVfill.setSize(toVfill.getWidth(), yslack + toVfill.getHeight());
+ relMove(target, 0, yslack, moveDownStart, nmembers);
+ }
+ }
+ }
+
+}
+
+class Ruler {
+ private Vector tabs = new Vector();
+
+ public void setTab(int num, int xpos) {
+ if (num >= tabs.size()) tabs.add(num, new Integer(xpos));
+ else {
+ // Transpose all tabs from this tab stop and onwards
+ int delta = xpos - getTab(num);
+ if (delta > 0) {
+ for (int i = num; i < tabs.size(); i++) {
+ tabs.set(i, new Integer(getTab(i) + delta));
+ }
+ }
+ }
+ }
+
+ public int getTab(int num) {
+ return ((Integer)tabs.get(num)).intValue();
+ }
+
+ public String toString() {
+ StringBuffer ret = new StringBuffer(getClass().getName() + " {");
+ for (int i=0; i<tabs.size(); i++) {
+ ret.append(tabs.get(i));
+ if (i < tabs.size()-1) ret.append(',');
+ }
+ ret.append('}');
+ return ret.toString();
+ }
+
+ public static void main(String[] args) {
+ Ruler r = new Ruler();
+ r.setTab(0,10);
+ r.setTab(1,20);
+ r.setTab(2,30);
+ System.out.println(r);
+ r.setTab(1,25);
+ System.out.println(r);
+ System.out.println(r.getTab(0));
+ }
+}
diff --git a/PFTT/src/se/datadosen/component/Ruler.class
b/PFTT/src/se/datadosen/component/Ruler.class
new file mode 100644
index 0000000..98aaf53
Binary files /dev/null and b/PFTT/src/se/datadosen/component/Ruler.class differ