hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
150
151
152
153
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#!/usr/bin/env ruby
 
# Funning xeno-test once with each option takes a long time (around 15 minutes)
# For each test a full log is stored in the logs subdirectory.
# Therefore the results of the tests (the ruby variable) is stored using Pstore in
# the file logs/xeno-test.store.
# If this file exists and is newer than xeno-test and the first parameter is != clean
# The tst variable is fetched from the file.
# This is convenient if you want to add new checks.
#
# Todo: - Why do the mailing tests not work  ?
#       - How does xeno-test react to Ctrl-C ?
#       - Thorougher analyses of the log files
#
require 'timeout'
require 'ostruct'
require 'ftools'
require 'pstore'
require 'tempfile'
 
XenoInstall = "/usr/xenomai/bin"
XenoHead    = File.expand_path(File.dirname(File.dirname(__FILE__)))
XenoTestOrg = XenoHead+'/xeno-test.in'
XenoTestRun = XenoInstall+'/xeno-test'
LogDir      = XenoHead+'/logs'
MainLog     = LogDir+'/runtest.log'
StorageName = LogDir+'/xeno-test.store'
withFinished = `grep finished /usr/xenomai/bin/xeno*`
withFinished.size > 0 ? FinishedMsg = "xeno-test: finished" : FinishedMsg = nil
 
$clean  = ARGV[0] == 'clean' && ! (
     File.exists?(XenoTestRun) &&
          File.exists?(StorageName) &&
          File.stat(XenoTestRun).mtime >  File.stat(StorageName).mtime
          )
if $clean && File.exists?(StorageName) then
  puts "Do you really want to run all tesst? <Ctrl-C> to abort"
  STDIN.getc
end
 
class XenoTest
  attr_reader :mainLog
  attr_reader :blockDevice
  attr_writer :maxTimeout
  attr_writer :chkProc
 
  def initialize(options)
    @from    = XenoHead
    @inst    = XenoInstall
    @busybox = false
    File.makedirs(LogDir) if defined?(LogDir) && ! File.directory?(LogDir)
    @@mainLog = open(MainLog,"w");
    @@mainLog.puts("#{Time.now}: started run")
    @@mainLog.sync = true
    options.each { |name,val|
      if val.class == String then
        cmd =  "@#{name}='#{val}'"
      else
   cmd =  "@#{name}=#{val}"
      end
      eval cmd
    }
  end
 
  def add(opts,  chkProc=nil)
    @nr   = -1        if !defined?(@nr)
    @tsts = Array.new if !defined?(@tsts)
    @nr  += 1
    elem = OpenStruct.new({'nr' => @nr, 'opts'=>opts})
    elem.chkProc = chkProc if chkProc
    @tsts << elem
    return elem
  end
 
  def runTests
    @tsts.each { |x| runOneTest(x) ; }
  end
 
  def XenoTest::writeLogEntry(tst, logMsg)
    msg = "#{Time.now}: #{sprintf("%2d", tst.nr)} "+ logMsg
    if ! File.exists?(MainLog) then
      puts msg
    end
    outs = [ STDOUT]
    outs << @@mainLog if defined?(@@mainLog) && @@mainLog
    outs.each {|dev| dev.puts msg }
  end
 
  def runOneTest(x)
    puts "runOneTest #{x.inspect}"  if $VERBOSE
    opts = x.opts
    startTime = Time.now
    tstRes = nil
    x.tst  = `which xeno-test`.chomp
    x.log  = "#{LogDir}/test_xeno_test.log.#{x.nr}"
    x.cmd  = "#{x.tst} -T 1 #{x.opts} | tee #{x.log}"   # -T 1 to make the test time shorter
 
    # Start thread to enfore maxTimeout
    if @maxTimeout then
      @rThread = Thread.new{
       puts "@rThread: "+Process.pid.to_s if $VERBOSE
       writeLogEntry(x, "started: #{opts} Timeout #{@maxTimeout}")
       sleep(@maxTimeout);
       writeLogEntry(x, "rThread will kill #{$childPid.inspect}")
       system("kill -9 #{$childPid}") if $childPid
       raise Timeout::Error 
      }
    end
    # Now we are doing calling our script
    $childPid = nil
    Dir.chdir(@inst)
    res = IO.popen(x.cmd) { |f|
      puts "cmd: "+Process.pid.to_s if $VERBOSE
      $childPid = Process.pid
      f.each{|a| puts a}
    } 
    tstRes = $?
  rescue Timeout::Error => details
    puts detail.backtrace.join("\n")
    writeLogEntry(x, "FAILED: #{opts} Timeout error #{$childPid}")
    system("kill -9 #{$childPid}")
  rescue => details
    writeLogEntry(x, "FAILED: #{opts} runtime error #{@rThread.inspect} pid #{$childPid.inspect}")
    puts details.backtrace.join("\n")
    system("kill -9 #{$childPid}")
    @rThread.kill if defined?(@rThread)
  ensure
    # save results,
    # clean up dd process if there are any, if xeno-test did not clean it up as it should have
    endTime = Time.now
    duration = (endTime-startTime).round
    if duration <= 5 then
      str    =  "FAILED"
      tstRes =  'too fast'
    else
      str = tstRes ? 'PASSED' : 'FAILED'
    end
    x.tstRes   = tstRes
    x.duration = duration
    XenoTest::writeLogEntry(x, "#{str}: #{sprintf("%3d", duration)} seconds. opts <#{x.opts}> returned #{tstRes}")
 
    IO.popen("ps -ef | grep -w dd") { |f|
      line= f.gets
      if ! line.index('grep') then
   msg = "#{Time.now}: #{sprintf("%2d", x.nr)} FAILED: dd not killed \n    >>> #{line}"
   [ STDOUT, @@mainLog].each {|dev| dev.puts msg if dev}
   system "killall dd"
      end
    }
    @rThread.kill if @rThread
    @rThread = nil
  end
 
  def runChecks
    puts "running all Checks"
    @tsts.each { |aTst|
      checkFinished(aTst)
      chks = aTst.chkProc
      next if ! chks
      z = eval "#{chks[0]}"+"(aTst, chks[1])" if chks[1]
    }
    puts "completed all Checks"
  end
 
  def checkFinished(tst)
    return if ! FinishedMsg
    logInhalt = IO.readlines(tst.log)
    finished  = false
    logInhalt.each{|line|
      finished =  true if line.index(FinishedMsg)
    }
    if !finished then
      puts "CHK #{tst.nr} FAILED opts #{tst.opts} could not find #{FinishedMsg}"
      puts "  searched in #{tst.log}" 
    end
  end
 
  def getTstByOpt(opts)
    @tsts.each{ |aTst|
      return aTst if aTst.opts == opts
    }
    return nil
  end
end
 
# Here follow the various routines, that check the effect of each options
def expectInLogFile(x, string)
  puts "x #{x.inspect}\nlooking for #{string}" if $VERBOSE
  logInhalt = IO.readlines(x.log)
  found = false
  logInhalt.each{|line|
    found = true if line.index(string)
  }
  puts "CHK #{x.nr} FAILED, could not find #{string} in #{x.log}" if !found
end
 
# Get a name for a temporary file as a target to upload via curl (option -U)
tf = Tempfile.new("xeno-test")
TstUrlTarget = tf.path
tf.close
File.delete if File.exists?(TstUrlTarget)
 
tst   = nil
store = nil
File.delete(StorageName) if $clean &&  File.exists?(StorageName)
if File.exists?(StorageName) then
  # Read in our saved test state (if we have a valid combination)
  store = PStore.new(StorageName)
  puts "reading tst from #{StorageName}"
  store.transaction do
    tst = store['tst']
  end
else
  # we prepare to run our tests
 
  # Get the first block-Device
  blockDevices = `mount | cut -d\\  -f1 | egrep -ve 'sysfs|proc|depts'`
  blockDevices = blockDevices[0..blockDevices.index("\n")].chomp
  bDev = blockDevices
 
  tst = XenoTest.new({'blockDevice' => bDev})
  tst.maxTimeout = 120
 
  # Define test run for each option and/or combination thereof
  
  # Test for a bad option. This happens to be fast, too
  tst_0  = tst.add("-O",                [ 'expectInLogFile', "xeno-test [options]"])
  tst_1  = tst.add("",                  [ 'expectInLogFile', FinishedMsg])
  tst_2  = tst.add("-T 5")
  if true then # To speed up our test,s we may turn off defining more testruns
      tst_3  = tst.add("-p 200",        ['expectInLogFile',"Sampling period: 200 us"])
      tst_4  = tst.add("-w 2",          ['expectInLogFile',FinishedMsg])
      tst_5  = tst.add("-d #{bDev}",
     ['expectInLogFile',"creating workload using dd if=#{bDev}"])
 
      tst_6  = tst.add("-w /bin/dd #{bDev}",
                   ['expectInLogFile',FinishedMsg])
      tst_7  = tst.add("-P 'echo marker'", 
                   ['expectInLogFile',FinishedMsg])
      tst_8  = tst.add("-L",            ['expectInLogFile',FinishedMsg])
      tst_9  = tst.add("-N marker",     ['expectInLogFile',FinishedMsg])
      tst_10 = tst.add("-v",            ['expectInLogFile',FinishedMsg])
      tst_11 = tst.add("-U #{TstUrlTarget}",
                                        ['expectInLogFile',FinishedMsg])
      tst_12 = tst.add("-D '%Ymarker%Mmarker%D'", 
                                        ['expectInLogFile',FinishedMsg])
#      tst_13 = tst.add("-M #{ENV['USER']}@localhost", 
#                    ['expectInLogFile',FinishedMsg])
#      tst_14 = tst.add("-m",            ['expectInLogFile',FinishedMsg])
  end
 
  # Remove files, we might have left behind in a previous test
  ( Dir.glob("/tmp/test-#{`uname -r`.chomp}*") + 
    Dir.glob("#{XenoInstall}/marker*")).each { |file|
    puts "Removing #{file}"
    File.delete file
  }
  tst.runTests
 
  # Now we are ready to store our testresult (our ruby variable) to file
  puts "Writing tst to #{StorageName}"
  store = PStore.new(StorageName)
  store.transaction do
    store['tst'] = tst
  end
end
 
# If we did run the curl/-N, -L test, did it generate the expected file?
{
 "-U #{TstUrlTarget}" => TstUrlTarget,
 "-L" => "/tmp/test-#{`uname -r`.chomp}*",
 "-N marker" =>  "#{XenoInstall}/marker*",
}.each { |opt, path|
  myTst  = tst.getTstByOpt(opt)
  if myTst && Dir.glob(path).size == 0  then
    XenoTest::writeLogEntry(myTst, "FAILED: #{opt}: no file #{path} found")
  else 
    XenoTest::writeLogEntry(myTst, "PASSED: #{opt}: #{Dir.glob(path).inspect}")
  end if myTst
}
 
# This string is probably wrong, but I think xeno-test doesn't accept this
# string as it should neither
myTst  = tst.getTstByOpt("-D '%Ymarker%Mmarker%D'")
tstString = "-D #{Time.now.strftime('%Ymarker%Mmarker%D')}"
tstString = "marker"
expectInLogFile(myTst, tstString) if myTst
 
# Test whether we really take more time with -T 5 than with the default -T 1
long  = tst.getTstByOpt("-T 5")
short = tst.getTstByOpt("")
if long and short then
  if long.duration < short.duration + 5
  then
    XenoTest::writeLogEntry(long, 
      "FAILED: with '#{short.opts}' #{sprintf("%3d", short.duration)} "+
      "+ 5 not smaller <  #{sprintf("%3d", long.duration)} seconds with '#{long.opts}'.")
  end
end
 
tst.runChecks