qed 2.6.3 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data/.ruby +4 -3
  2. data/.yardopts +3 -0
  3. data/HISTORY.rdoc +71 -35
  4. data/README.rdoc +9 -10
  5. data/bin/qed +1 -1
  6. data/bin/qedoc +2 -1
  7. data/lib/qed.rb +2 -5
  8. data/lib/qed.yml +4 -3
  9. data/lib/qed/applique.rb +57 -24
  10. data/lib/qed/cli.rb +8 -0
  11. data/lib/qed/cli/qed.rb +124 -0
  12. data/lib/qed/demo.rb +35 -39
  13. data/lib/qed/document.rb +5 -3
  14. data/lib/qed/document/template.rhtml +1 -0
  15. data/lib/qed/evaluator.rb +227 -199
  16. data/lib/qed/parser.rb +60 -282
  17. data/lib/qed/reporter/abstract.rb +54 -58
  18. data/lib/qed/reporter/dotprogress.rb +6 -4
  19. data/lib/qed/reporter/html.rb +112 -31
  20. data/lib/qed/reporter/tapy.rb +95 -125
  21. data/lib/qed/reporter/verbatim.rb +80 -38
  22. data/lib/qed/scope.rb +35 -48
  23. data/lib/qed/session.rb +35 -140
  24. data/lib/qed/settings.rb +104 -67
  25. data/lib/qed/step.rb +237 -0
  26. data/{spec → qed}/01_demos.rdoc +0 -0
  27. data/{spec → qed}/02_advice.rdoc +18 -7
  28. data/qed/03_helpers.rdoc +44 -0
  29. data/{spec → qed}/04_samples.rdoc +4 -4
  30. data/{spec → qed}/05_quote.rdoc +3 -3
  31. data/{spec → qed}/07_toplevel.rdoc +0 -0
  32. data/{spec → qed}/08_cross_script.rdoc +0 -0
  33. data/{spec → qed}/09_cross_script.rdoc +0 -0
  34. data/{spec → qed}/10_constant_lookup.rdoc +2 -2
  35. data/qed/11_embedded_rules.rdoc +46 -0
  36. data/{test/integration/topcode.rdoc → qed/99_issues/02_topcode.rdoc} +0 -0
  37. data/{spec → qed}/applique/constant.rb +0 -0
  38. data/{spec → qed}/applique/env.rb +0 -0
  39. data/{spec → qed}/applique/fileutils.rb +0 -0
  40. data/{spec → qed}/applique/markup.rb +0 -0
  41. data/{spec → qed}/applique/toplevel.rb +0 -0
  42. data/{spec → qed}/helpers/advice.rb +6 -7
  43. data/{spec → qed}/helpers/toplevel.rb +0 -0
  44. data/{spec → qed}/samples/data.txt +0 -0
  45. data/{spec → qed}/samples/table.yml +0 -0
  46. metadata +44 -39
  47. data/LICENSE.rdoc +0 -31
  48. data/SPECSHEET.rdoc +0 -456
  49. data/lib/qed/advice.rb +0 -158
  50. data/lib/qed/reporter/bullet.rb +0 -91
  51. data/lib/qed/reporter/dtrace.rb +0 -67
  52. data/lib/yard-qed.rb +0 -1
  53. data/spec/03_helpers.rdoc +0 -43
  54. data/spec/applique/quote.rb +0 -4
  55. data/spec/helpers/sample.rb +0 -4
@@ -10,78 +10,120 @@ module Reporter #:nodoc:
10
10
  #
11
11
  def before_session(session)
12
12
  @start_time = Time.now
13
+
14
+ trap "INFO" do
15
+ print_time
16
+ print_tally
17
+ end if INFO_SIGNAL
13
18
  end
14
19
 
15
- #
16
- def head(step)
17
- io.print step.text.ansi(:bold)
20
+ def step(step)
21
+ @_explain = step.explain.dup
18
22
  end
19
23
 
20
24
  #
21
- def desc(step)
25
+ def match(step, md)
26
+ unless md[0].empty?
27
+ @_explain.sub!(md[0], md[0].ansi(:bold))
28
+ end
22
29
  end
23
30
 
24
31
  #
25
- def data(step)
26
- #io.puts step.clean_text.ansi(:blue)
27
- #io.puts
32
+ def applique(step)
33
+ io.print "#{@_explain}".ansi(:cyan)
34
+ io.print "#{step.example}" #.ansi(:blue)
28
35
  end
29
36
 
30
37
  #
31
38
  def pass(step)
32
39
  super(step)
33
- if step.code?
34
- io.print "#{step.text}".ansi(:green)
35
- elsif step.header?
36
- io.print "#{step.text}".ansi(:bold)
37
- elsif step.data?
38
- io.print "#{step.text}".ansi(:blue)
40
+ if step.heading?
41
+ if step.code?
42
+ io.print "#{@_explain}".ansi(:bold, :cyan)
43
+ else
44
+ io.print "#{@_explain}".ansi(:bold)
45
+ end
39
46
  else
40
- io.print "#{step.text}"
47
+ io.print "#{@_explain}".ansi(:cyan)
48
+ end
49
+
50
+ if step.has_example?
51
+ if step.data?
52
+ io.print "#{step.example}" #.ansi(:magenta)
53
+ else
54
+ io.print "#{step.example}".ansi(:green)
55
+ end
41
56
  end
42
57
  end
43
58
 
44
59
  #
45
60
  def fail(step, error)
46
61
  super(step, error)
47
- txt = step.text.rstrip #.sub("\n",'')
62
+
48
63
  tab = step.text.index(/\S/)
49
- io.print "#{txt}\n\n".ansi(:red)
64
+
65
+ if step.heading?
66
+ if step.code?
67
+ io.print "#{@_explain}".ansi(:bold, :magenta)
68
+ else
69
+ io.print "#{@_explain}".ansi(:bold)
70
+ end
71
+ else
72
+ io.print "#{@_explain}".ansi(:magenta)
73
+ end
74
+
75
+ if step.has_example?
76
+ if step.data?
77
+ io.print "#{step.example}".ansi(:red)
78
+ else
79
+ io.print "#{step.example}".ansi(:red)
80
+ end
81
+ end
82
+
50
83
  msg = []
51
- #msg << ANSI::Code.bold(ANSI::Code.red("FAIL: ")) + error.message
52
- #msg << ANSI::Code.bold(clean_backtrace(error.backtrace[0]))
53
84
  msg << "FAIL: ".ansi(:bold, :red) + error.message.to_s #to_str
54
- msg << sane_backtrace(error).first.ansi(:bold)
55
- io.puts msg.join("\n").tabto(tab||2)
85
+ msg << sane_backtrace(error).join("\n").ansi(:bold)
86
+ msg = msg.join("\n")
87
+
88
+ io.puts msg.tabto(tab||2)
56
89
  io.puts
57
90
  end
58
91
 
59
92
  #
60
93
  def error(step, error)
61
94
  super(step, error)
62
- raise error if $DEBUG
63
- txt = step.text.rstrip #.sub("\n",'')
95
+
96
+ raise error if $DEBUG # TODO: Should this be here?
97
+
64
98
  tab = step.text.index(/\S/)
65
- io.print "#{txt}\n\n".ansi(:red)
99
+
100
+ if step.heading?
101
+ if step.code?
102
+ io.print "#{@_explain}".ansi(:bold, :magenta)
103
+ else
104
+ io.print "#{@_explain}".ansi(:bold)
105
+ end
106
+ else
107
+ io.print "#{@_explain}".ansi(:magenta)
108
+ end
109
+
110
+ if step.has_example?
111
+ if step.data?
112
+ io.print "#{step.example}".ansi(:red)
113
+ else
114
+ io.print "#{step.example}".ansi(:red)
115
+ end
116
+ end
117
+
66
118
  msg = []
67
119
  msg << "ERROR: #{error.class} ".ansi(:bold,:red) + error.message #.sub(/for QED::Context.*?$/,'')
68
- msg << sane_backtrace(error).first.ansi(:bold)
69
- #msg = msg.ansi(:red)
70
- io.puts msg.join("\n").tabto(tab||2)
120
+ msg << sane_backtrace(error).join("\n").ansi(:bold)
121
+ msg = msg.join("\n") #.ansi(:red)
122
+
123
+ io.puts msg.tabto(tab||2)
71
124
  io.puts
72
125
  end
73
126
 
74
- #def report(str)
75
- # count[-1] += 1 unless count.empty?
76
- # str = str.chomp('.') + '.'
77
- # str = count.join('.') + ' ' + str
78
- # puts str.strip
79
- #end
80
-
81
- #def report_table(set)
82
- # puts set.to_yaml.tabto(2).ansi(:magenta)
83
- #end
84
-
85
127
  #
86
128
  #def macro(step)
87
129
  # #io.puts
@@ -92,6 +134,7 @@ module Reporter #:nodoc:
92
134
 
93
135
  #
94
136
  def after_session(session)
137
+ trap 'INFO', 'DEFAULT' if INFO_SIGNAL
95
138
  print_time
96
139
  print_tally
97
140
  end
@@ -100,4 +143,3 @@ module Reporter #:nodoc:
100
143
 
101
144
  end #module Reporter
102
145
  end #module QED
103
-
@@ -9,29 +9,18 @@ module QED
9
9
  # Location of `qed/scope.rb`.
10
10
  DIRECTORY = File.dirname(__FILE__)
11
11
 
12
- #
13
- # def self.new(applique, file)
14
- # @_applique = applique
15
- # super(applique, file)
16
- # end
12
+ # Setup new Scope instance.
13
+ def initialize(demo, options={})
14
+ super()
17
15
 
18
- # #
19
- # def self.const_missing(name)
20
- # @_applique.const_get(name)
21
- # end
16
+ @_applique = demo.applique_prime
22
17
 
23
- #
24
- def initialize(applique, cwd, file=nil)
25
- super()
26
- @_applique = applique
27
- @_cwd = cwd
28
- @_file = file
29
- #@loadlist = []
18
+ @_file = demo.file
19
+ @_root = options[:root] || $ROOT # FIXME
30
20
 
31
- include *applique
32
- #extend self
33
- #extend applique # TODO: extend or include applique or none ?
34
- #extend DSLi
21
+ @_features = []
22
+
23
+ include *demo.applique
35
24
 
36
25
  # TODO: custom extends?
37
26
 
@@ -40,9 +29,6 @@ module QED
40
29
 
41
30
  # This turns out to be the key to proper scoping.
42
31
  def __create_clean_binding_method__
43
- #define_method(:__binding__) do
44
- # @__binding__ ||= binding
45
- #end
46
32
  module_eval %{
47
33
  def __binding__
48
34
  @__binding__ ||= binding
@@ -53,7 +39,7 @@ module QED
53
39
  #
54
40
  def include(*modules)
55
41
  super(*modules)
56
- extend self
42
+ extend self # overcome dynamic inclusion problem
57
43
  end
58
44
 
59
45
  # Expanded dirname of +file+.
@@ -62,43 +48,39 @@ module QED
62
48
  end
63
49
 
64
50
  # Evaluate code in the context of the scope's special binding.
65
- # The return result of the evaluation is stored in `@_`.
51
+ # The return value of the evaluation is stored in `@_`.
66
52
  #
67
- # TODO: rename to avoid conflict with Kernel method.
68
- def eval(code, file=nil, line=nil)
53
+ def evaluate(code, file=nil, line=nil)
69
54
  if file
70
- @_ = super(code, __binding__, file.to_s, line.to_i)
55
+ @_ = eval(code, __binding__, file.to_s, line.to_i)
71
56
  else
72
- @_ = super(code, __binding__)
57
+ @_ = eval(code, __binding__)
73
58
  end
74
59
  end
75
60
 
61
+ # TODO: Alternative to Plugin gem? If not improve and make standard requirement.
62
+
76
63
  # Utilize is like #require, but will evaluate the script in the context
77
64
  # of the current scope.
78
- #--
79
- # TODO: Alternative to Plugin gem?
80
65
  #
81
- # TODO: Should work like require so same file isn't loaded twice.
82
- #++
83
66
  def utilize(file)
84
67
  file = Dir[DIRECTORY + "/helpers/#{file}"].first
85
68
  if !file
86
69
  require 'plugin'
87
70
  file = Plugin.find("#{file}{,.rb}", :directory=>nil)
88
71
  end
89
- if file
72
+ if file && !@_features.include?(file)
90
73
  code = File.read(file)
91
- eval(code, nil, file)
74
+ evaluate(code, nil, file)
92
75
  else
93
76
  raise LoadError, "no such file -- #{file}"
94
77
  end
95
78
  end
96
79
 
97
-
98
80
  # Define "when" advice.
99
81
  def When(*patterns, &procedure)
100
- patterns = patterns.map{ |pat| pat == :text ? :desc : pat }
101
- @_applique.first.When(*patterns, &procedure)
82
+ #patterns = patterns.map{ |pat| pat == :text ? :desc : pat }
83
+ @_applique.When(*patterns, &procedure)
102
84
  end
103
85
 
104
86
  # Define "before" advice. Default type is :each, which
@@ -106,7 +88,7 @@ module QED
106
88
  def Before(type=:each, &procedure)
107
89
  type = :step if type == :each
108
90
  type = :demo if type == :all
109
- @_applique.first.Before(type, &procedure)
91
+ @_applique.Before(type, &procedure)
110
92
  end
111
93
 
112
94
  # Define "after" advice. Default type is :each, which
@@ -114,7 +96,7 @@ module QED
114
96
  def After(type=:each, &procedure)
115
97
  type = :step if type == :each
116
98
  type = :demo if type == :all
117
- @_applique.first.After(type, &procedure)
99
+ @_applique.After(type, &procedure)
118
100
  end
119
101
 
120
102
  # Directory of current document.
@@ -126,13 +108,14 @@ module QED
126
108
  end
127
109
  end
128
110
 
129
- # TODO: Should Table and Data be extensions that can be loaded if desired?
111
+ # TODO: Should Table and Data be extensions that can be optionally loaded?
112
+
113
+ # TODO: Cache Table and Data for speed ?
130
114
 
131
115
  # Use sample table to run steps. The table file is located relative to
132
116
  # the demo, failing that it will be looked for relative to the working
133
117
  # directory.
134
118
  #
135
- # TODO: Cache data for speed ?
136
119
  def Table(file=nil, options={}) #:yield:
137
120
  if file
138
121
  file = Dir.glob(File.join(File.dirname(@_file), file)).first || file
@@ -166,9 +149,6 @@ module QED
166
149
  # Read a static data file and yield contents to block if given.
167
150
  #
168
151
  # This method no longer automatically uses YAML.load.
169
- #--
170
- # TODO: Cache data for speed ?
171
- #++
172
152
  def Data(file) #:yield:
173
153
  file = Dir.glob(File.join(File.dirname(@_file), file)).first || file
174
154
  #case File.extname(file)
@@ -184,9 +164,10 @@ module QED
184
164
  end
185
165
  end
186
166
 
187
- # Clear temporary work directory.
167
+ # Helper method to clear temporary work directory.
168
+ #
188
169
  def clear_working_directory!
189
- dir = @_cwd
170
+ dir = @_root
190
171
  dir = File.expand_path(dir)
191
172
 
192
173
  if dir == '/' or dir == File.expand_path('~')
@@ -201,7 +182,13 @@ module QED
201
182
  dirs.each { |dir| FileUtils.rmdir(dir) }
202
183
  end
203
184
 
204
- #
185
+ # TODO: Project's root directory.
186
+ #def rootdir
187
+ # @_root
188
+ #end
189
+
190
+ # Redirect constant missing to toplevel (i.e. Object). This is
191
+ # to allow the evaluation scope to emulate the toplevel.
205
192
  def const_missing(const)
206
193
  Object.const_get(const)
207
194
  end
@@ -58,8 +58,9 @@ module QED
58
58
 
59
59
  @files = [files].flatten.compact
60
60
  @files = [DEFAULT_FILES.find{ |d| File.directory?(d) }] if @files.empty?
61
+ @files = @files.compact
61
62
 
62
- @format = options[:format] || :dotprogress
63
+ @format = options[:format] || :dot
63
64
  @trace = options[:trace] || false
64
65
  @mode = options[:mode] || nil
65
66
  @profile = options[:profile] || :default
@@ -93,29 +94,28 @@ module QED
93
94
  # Instance of selected Reporter subclass.
94
95
  def reporter
95
96
  @reporter ||= (
96
- name = Reporter.constants.find{ |c| /#{format}/ =~ c.downcase }
97
+ name = Reporter.constants.find{ |c| /#{format}/ =~ c.to_s.downcase }
97
98
  Reporter.const_get(name).new(:trace => trace)
98
99
  )
99
100
  end
100
101
 
101
- # Returns an Array of Demo instances.
102
- #--
103
102
  # TODO: Pass settings to demo, so we can get temporary_directory.
104
- #++
103
+
104
+ # Returns an Array of Demo instances.
105
105
  def demos
106
106
  @demos ||= demo_files.map{ |file| Demo.new(file, :mode=>mode, :at=>directory) }
107
107
  end
108
108
 
109
+ # List of observers to pass to the evaluator. Only includes the reporter
110
+ # instance, by default.
109
111
  #
110
112
  def observers
111
113
  [reporter]
112
114
  end
113
115
 
114
- # Run session.
115
- #--
116
116
  # TODO: remove loadpath additions when done
117
- #++
118
- # COMMIT: Pre-parse demos before running them.
117
+
118
+ # Run session.
119
119
  def run
120
120
  abort "No documents." if demo_files.empty?
121
121
 
@@ -128,19 +128,19 @@ module QED
128
128
 
129
129
  Dir.chdir(directory) do
130
130
  # pre-parse demos
131
- demos.each do |demo|
132
- demo.steps
133
- end
134
-
135
- #profile.before_session(self)
136
- reporter.before_session(self)
137
- demos.each do |demo|
138
- demo.run(*observers)
139
- #pid = fork { demo.run(*observers) }
140
- #Process.detach(pid)
131
+ demos.each{ |demo| demo.steps }
132
+
133
+ # Let's do it.
134
+ observers.each{ |o| o.before_session(self) }
135
+ begin
136
+ demos.each do |demo|
137
+ Evaluator.run(demo, :observers=>observers, :settings=>settings) #demo.run(*observers)
138
+ #pid = fork { demo.run(*observers) }
139
+ #Process.detach(pid)
140
+ end
141
+ ensure
142
+ observers.each{ |o| o.after_session(self) }
141
143
  end
142
- reporter.after_session(self)
143
- #profile.after_session(self)
144
144
  end
145
145
 
146
146
  reporter.success?
@@ -163,7 +163,7 @@ module QED
163
163
 
164
164
  #
165
165
  def require_profile
166
- settings.require_profile(profile)
166
+ settings.load_profile(profile)
167
167
  end
168
168
 
169
169
  # Returns a list of demo files. The files returned depends on the
@@ -171,22 +171,22 @@ module QED
171
171
  def demo_files
172
172
  @demo_files ||= (
173
173
  if mode == :comment
174
- demos_in_comment_mode
174
+ demo_files_in_comment_mode
175
175
  else
176
- demos_in_normal_mode
176
+ demo_files_in_normal_mode
177
177
  end
178
178
  )
179
179
  end
180
180
 
181
181
  # Collect default files to process in normal demo mode.
182
- def demos_in_normal_mode
183
- demos_gather(DEMO_TYPES)
182
+ def demo_files_in_normal_mode
183
+ demos_gather #(DEMO_TYPES)
184
184
  end
185
185
 
186
186
  # Collect default files to process in code comment mode.
187
187
  #
188
188
  # TODO: Sure removing applique files is the best approach here?
189
- def demos_in_comment_mode
189
+ def demo_files_in_comment_mode
190
190
  files = demos_gather(CODE_TYPES)
191
191
  files = files.reject{ |f| f.index('applique/') } # don't include applique files ???
192
192
  files
@@ -206,7 +206,8 @@ module QED
206
206
  end
207
207
  end
208
208
  files = files.flatten.uniq
209
- files = files.reject{ |f| f =~ Regexp.new('\/'+omit.join('|')+'\/') }
209
+ #files = files.reject{ |f| f =~ Regexp.new("/\/(#{omit.join('|')})\//") }
210
+ files = files.reject{ |f| omit.any?{ |o| f.index("/#{o}/") } }
210
211
  files.map{|f| File.expand_path(f) }.uniq.sort
211
212
  end
212
213
 
@@ -217,123 +218,17 @@ module QED
217
218
  # end
218
219
  #end
219
220
 
221
+ # Get the total test count. This method tallies up the number of
222
+ # _assertive_ steps.
220
223
  #
221
224
  def total_step_count
222
225
  count = 0
223
- QED.all_steps.each do |step|
224
- count += 1 unless step.header?
225
- end
226
- count
227
- end
228
-
229
- #
230
- def self.cli(*argv)
231
- require 'optparse'
232
- require 'shellwords'
233
-
234
- files, options = cli_parse(argv)
235
-
236
- #if files.empty?
237
- # puts "No files."
238
- # exit -1
239
- #end
240
-
241
- session = new(files, options)
242
- success = session.run
243
-
244
- exit -1 unless success
245
- end
246
-
247
- # Instance of OptionParser
248
- def self.cli_parse(argv)
249
- options = {}
250
- options_parser = OptionParser.new do |opt|
251
- opt.banner = "Usage: qed [options] <files...>"
252
-
253
- opt.separator("Custom Profiles:") unless settings.profiles.empty?
254
-
255
- settings.profiles.each do |name, value|
256
- o = "--#{name}"
257
- opt.on(o, "#{name} custom profile") do
258
- options[:profile] = name.to_sym
259
- end
260
- end
261
-
262
- opt.separator("Report Formats (pick one):")
263
- opt.on('--dotprogress', '-d', "use dot-progress reporter [default]") do
264
- options[:format] = :dotprogress
265
- end
266
- opt.on('--verbatim', '-v', "use verbatim reporter") do
267
- options[:format] = :verbatim
268
- end
269
- opt.on('--tapy', '-y', "use TAP-Y reporter") do
270
- options[:format] = :tapy
271
- end
272
- opt.on('--bullet', '-b', "use bullet-point reporter") do
273
- options[:format] = :bullet
274
- end
275
- opt.on('--html', '-h', "use underlying HTML reporter") do
276
- options[:format] = :html
277
- end
278
- #opt.on('--script', "psuedo-reporter") do
279
- # options[:format] = :script # psuedo-reporter
280
- #end
281
- opt.on('--format', '-f FORMAT', "use custom reporter") do |format|
282
- options[:format] = format.to_sym
283
- end
284
-
285
- opt.separator("Control Options:")
286
- opt.on('--comment', '-c', "run comment code") do
287
- options[:mode] = :comment
288
- end
289
- opt.on('--profile', '-p NAME', "load runtime profile") do |name|
290
- options[:profile] = name
291
- end
292
- opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |paths|
293
- options[:loadpath] = paths.split(/[:;]/).map{|d| File.expand_path(d)}
294
- end
295
- opt.on('--require', "-r LIB", "require library") do |paths|
296
- options[:requires] = paths.split(/[:;]/)
297
- end
298
- opt.on('--rooted', '-R', "run from project root instead of temporary directory") do
299
- options[:rooted] = true
300
- end
301
- # COMMIT:
302
- # The qed command --trace option takes a count.
303
- # Use 0 to mean all.
304
- opt.on('--trace', '-t [COUNT]', "show full backtraces for exceptions") do |cnt|
305
- #options[:trace] = true
306
- ENV['trace'] = cnt
307
- end
308
- opt.on('--warn', "run with warnings turned on") do
309
- $VERBOSE = true # wish this were called $WARN!
310
- end
311
- opt.on('--debug', "exit immediately upon raised exception") do
312
- $DEBUG = true
313
- end
314
-
315
- opt.separator("Optional Commands:")
316
- opt.on_tail('--version', "display version") do
317
- puts "QED #{QED::VERSION}"
318
- exit
319
- end
320
- opt.on_tail('--copyright', "display copyrights") do
321
- puts "Copyright (c) 2008 Thomas Sawyer, Apache 2.0 License"
322
- exit
323
- end
324
- opt.on_tail('--help', '-h', "display this help message") do
325
- puts opt
326
- exit
226
+ demos.each do |demo|
227
+ demo.steps.each do |step|
228
+ count += 1 if step.assertive?
327
229
  end
328
230
  end
329
- options_parser.parse!(argv)
330
- return argv, options
331
- end
332
-
333
- # TODO: Pass to Session class, instead of acting global.
334
- # It is used at the class level to get profiles for the cli.
335
- def self.settings
336
- @settings ||= Settings.new
231
+ count
337
232
  end
338
233
 
339
234
  end#class Session