rdl 2.0.0.rc4 → 2.0.0.rc5

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.
@@ -25,9 +25,9 @@ module RDL::Type
25
25
  end
26
26
 
27
27
  # default behavior, override in appropriate subclasses
28
- def canonical
29
- return self
30
- end
28
+ def canonical; return self; end
29
+ def optional?; return false; end
30
+ def vararg?; return false; end
31
31
 
32
32
  # [+ other +] is a Type
33
33
  # [+ inst +] is a Hash<Symbol, Type> representing an instantiation
@@ -143,6 +143,7 @@ module RDL::Type
143
143
  return false # one has a block and the other doesn't
144
144
  end
145
145
  end
146
+ return true if left.is_a?(MethodType) && right.is_a?(NominalType) && right.name == 'Proc'
146
147
 
147
148
  # structural
148
149
  if left.is_a?(StructuralType) && right.is_a?(StructuralType)
@@ -175,18 +176,26 @@ module RDL::Type
175
176
  if left.is_a?(FiniteHashType) && right.is_a?(FiniteHashType)
176
177
  # Like Tuples, FiniteHashes are immutable, so covariant subtyping allowed
177
178
  # But note, no width subtyping allowed, to match #member?
178
- rest = right.elts.clone # shallow copy
179
+ right_elts = right.elts.clone # shallow copy
179
180
  left.elts.each_pair { |k, tl|
180
- return false unless rest.has_key? k
181
- tr = rest[k]
182
- tl = tl.type if tl.is_a? OptionalType
183
- tr = tr.type if tr.is_a? OptionalType
184
- return false unless leq(tl, tr, inst, ileft)
185
- rest.delete k
181
+ if right_elts.has_key? k
182
+ tr = right_elts[k]
183
+ return false if tl.is_a?(OptionalType) && !tr.is_a?(OptionalType) # optional left, required right not allowed, since left may not have key
184
+ tl = tl.type if tl.is_a? OptionalType
185
+ tr = tr.type if tr.is_a? OptionalType
186
+ return false unless leq(tl, tr, inst, ileft)
187
+ right_elts.delete k
188
+ else
189
+ return false unless right.rest && leq(tl, right.rest, inst, ileft)
190
+ end
186
191
  }
187
- rest.each_pair { |k, t|
192
+ right_elts.each_pair { |k, t|
188
193
  return false unless t.is_a? OptionalType
189
194
  }
195
+ unless left.rest.nil?
196
+ # If left has optional stuff, right needs to accept it
197
+ return false unless !(right.rest.nil?) && leq(left.rest, right.rest, inst, ileft)
198
+ end
190
199
  left.ubounds << right
191
200
  right.lbounds << left
192
201
  return true
@@ -15,6 +15,9 @@ module RDL::Type
15
15
  ts.concat t.types
16
16
  else
17
17
  raise RuntimeError, "Attempt to create union type with non-type" unless t.is_a? Type
18
+ raise RuntimeError, "Attempt to create union with optional type" if t.is_a? OptionalType
19
+ raise RuntimeError, "Attempt to create union with vararg type" if t.is_a? VarargType
20
+ raise RuntimeError, "Attempt to create union with annotated type" if t.is_a? AnnotatedArgType
18
21
  ts << t
19
22
  end
20
23
  }
@@ -17,8 +17,9 @@ module RDL::Type
17
17
  end
18
18
 
19
19
  def initialize(type)
20
- raise "Can't have vararg optional type" if type.class == OptionalType
21
- raise "Can't have vararg vararg type" if type.class == VarargType
20
+ raise "Can't have vararg optional type" if type.is_a? OptionalType
21
+ raise "Can't have vararg vararg type" if type.is_a? VarargType
22
+ raise "Can't have optional annotated type" if type.is_a? AnnotatedArgType
22
23
  @type = type
23
24
  super()
24
25
  end
@@ -55,5 +56,9 @@ module RDL::Type
55
56
  def hash # :nodoc:
56
57
  return 59 + @type.hash
57
58
  end
59
+
60
+ def vararg?
61
+ return true
62
+ end
58
63
  end
59
64
  end
@@ -535,13 +535,26 @@ class Object
535
535
  nil
536
536
  end
537
537
 
538
- # Type check all methods that had annotation `typecheck: sym' at type call
538
+ # Register [+ blk +] to be executed when `rdl_do_typecheck [+ sym +]` is called.
539
+ # The blk will be called with sym as its argument. The order
540
+ # in which multiple blks for the same sym will be executed is unspecified
541
+ def rdl_at(sym, &blk)
542
+ $__rdl_at[sym] = [] unless $__rdl_at[sym]
543
+ $__rdl_at[sym] << blk
544
+ end
545
+
546
+ # Invokes all callbacks from rdl_at(sym), in unspecified order.
547
+ # Afterwards, type checks all methods that had annotation `typecheck: sym' at type call, in unspecified order.
539
548
  def rdl_do_typecheck(sym)
549
+ if $__rdl_at[sym]
550
+ $__rdl_at[sym].each { |blk| blk.call(sym) }
551
+ end
552
+ $__rdl_at[sym] = Array.new
540
553
  return unless $__rdl_to_typecheck[sym]
541
554
  $__rdl_to_typecheck[sym].each { |klass, meth|
542
555
  RDL::Typecheck.typecheck(klass, meth)
543
556
  }
544
- $__rdl_to_typecheck[sym] = Array.new
557
+ $__rdl_to_typecheck[sym] = Set.new
545
558
  nil
546
559
  end
547
560
 
@@ -0,0 +1,16 @@
1
+ class Proc
2
+ rdl_nowrap
3
+
4
+ type :arity, '() -> %integer'
5
+ type :binding, '() -> Binding'
6
+ type :curry, '(?%integer arity) -> Proc'
7
+ type :hash, '() -> %integer'
8
+ rdl_alias :inspect, :to_s
9
+ type :lambda, '() -> %bool'
10
+ type :parameters, '() -> Array<[Symbol, Symbol]>'
11
+ type :source_location, '() -> [String, %integer]'
12
+ type :to_proc, '() -> self'
13
+ type :to_s, '() -> String'
14
+
15
+
16
+ end
@@ -1,6 +1,8 @@
1
1
  # :integer, :bigint, :float, :decimal, :numeric, :datetime, :time, :date, :binary, :boolean.
2
2
  # null allowed
3
3
 
4
+ type_alias '%symstr', 'Symbol or String'
5
+
4
6
  module RDL
5
7
  class Rails
6
8
 
@@ -28,5 +30,23 @@ module RDL
28
30
  raise RuntimeError, "Unrecoganized column type #{rails_type}"
29
31
  end
30
32
  end
33
+
34
+ # [+ model +] is an ActiveRecord::Base subclass that has been loaded.
35
+ # Gets the columns_hash of the model and returns a String that can
36
+ # serve as the paramter list to a method that accepts any number
37
+ # of the model's attributes keyed by the attribute names.
38
+ def self.attribute_types(model)
39
+ args = []
40
+
41
+ model.columns_hash.each { |name, col|
42
+ t = column_to_rdl(col.type)
43
+ if col.null
44
+ args << "#{name}: ?#{t}"
45
+ else
46
+ args << "#{name}: ?!#{t}"
47
+ end
48
+ }
49
+ return args.join ','
50
+ end
31
51
  end
32
52
  end
@@ -0,0 +1,15 @@
1
+ class ActiveModel::Errors
2
+ type :clear, '() -> %any'
3
+ type :delete, '(%symstr) -> %any'
4
+ type :[], '(%symstr) -> Array<String>'
5
+ type :each, '() { (%symstr, String) -> %any } -> %any'
6
+ type :size, '() -> %integer'
7
+ rdl_alias :count, :size
8
+ type :values, '() -> Array<String>'
9
+ type :keys, '() -> Array<Symbol>'
10
+ type :empty?, '() -> %bool'
11
+ rdl_alias :blank?, :empty?
12
+ type :hash, '(?%bool full_messages) -> Hash<Symbol, String>'
13
+ type :add, '(%symstr, %symstr, Hash<Symbol, %any>) -> %any'
14
+ type :add, '(%symstr, { () -> String }, Hash<Symbol, %any>) -> %any' # TODO: combine with prev with union once supported
15
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveModel
2
+ module Validations
3
+ type :errors, '() -> ActiveModel::Errors'
4
+ end
5
+ end
@@ -0,0 +1,190 @@
1
+ class ActiveRecord::Associations::CollectionProxy
2
+ type_params [:t], :all?
3
+
4
+ type :<<, '(*(t or Array<t>)) -> self'
5
+ type :==, '(%any) -> %bool'
6
+ type :any?, '() ?{ (t) -> %bool } -> %bool'
7
+ rdl_alias :append, :<<
8
+ type :build, '(Hash<Symbol, %any> or Array<Hash<Symbol, %any>>) -> self'
9
+ type :clear, '() -> self'
10
+ type :concat, '(*t) -> self'
11
+ type :count, '() -> %integer'
12
+ type :create, '(Hash<Symbol, %any> or Array<Hash<Symbol, %any>>) -> self'
13
+ type :create!, '(Hash<Symbol, %any> or Array<Hash<Symbol, %any>>) -> self'
14
+ type :delete, '(*t) -> Array<t>'
15
+ type :delete_all, '(?Symbol dependent) -> Array<t>'
16
+ type :destroy, '(*t) -> Array<t>'
17
+ type :destroy_all, '() -> %any'
18
+ type :distinct, '() -> self'
19
+ type :empty?, '() -> %bool'
20
+ type :find, '(%integer) -> t'
21
+ type :find, '(%integer, %integer, *%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
22
+ type :include?, '(t) -> %bool'
23
+ type :length, '() -> %integer'
24
+ type :load_target, '() -> %any'
25
+ type :loaded?, '() -> %bool'
26
+ type :many?, '() ?{ (t) -> %bool } -> %bool'
27
+ rdl_alias :new, :build
28
+ rdl_alias :push, :<<
29
+ type :reload, '() -> self'
30
+ type :replace, '(Array<t>) -> %any'
31
+ type :reset, '() -> self'
32
+ type :scope, '() -> ActiveRecord::Relation' # TODO fix
33
+ type :select, '(*Symbol) -> t'
34
+ type :select, '() { (t) -> %bool } -> ActiveRecord::Associations::CollectionProxy<t>'
35
+ type :size, '() -> %integer'
36
+ rdl_alias :spawn, :scope
37
+ type :take, '() -> t or nil'
38
+ type :take, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
39
+ type :to_ary, '() -> Array<t>'
40
+ rdl_alias :to_a, :to_ary
41
+ rdl_alias :unique, :distinct
42
+
43
+ type :first, '() -> t or nil'
44
+ type :first, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
45
+ type :second, '() -> t or nil'
46
+ type :second, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
47
+ type :third, '() -> t or nil'
48
+ type :third, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
49
+ type :fourth, '() -> t or nil'
50
+ type :fourth, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
51
+ type :fifth, '() -> t or nil'
52
+ type :fifth, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
53
+ type :forty_two, '() -> t or nil'
54
+ type :forty_two, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
55
+ type :third_to_last, '() -> t or nil'
56
+ type :third_to_last, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
57
+ type :second_to_last, '() -> t or nil'
58
+ type :second_to_last, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
59
+ type :last, '() -> t or nil'
60
+ type :last, '(%integer) -> ActiveRecord::Associations::CollectionProxy<t>'
61
+ end
62
+
63
+ module ActiveRecord::Associations::ClassMethods
64
+
65
+ # TODO: Check presence of methods required by, e.g., foreign_key, primary_key, etc.
66
+ # TODO: Check counter_cache, add to model attr_readonly_type
67
+
68
+
69
+ type :belongs_to, '(%symstr name, ?{ (?ActiveRecord::Base) -> %any } scope, class_name: ?%symstr, foreign_key: ?%symstr,' +
70
+ 'primary_key: ?%symstr, dependent: ?(:delete or :destroy), counter_cache: ?(%bool or %symstr),' +
71
+ 'polymorphic: ?%bool, validate: ?%bool, autosave: ?%bool, touch: ?(%bool or %symstr),' +
72
+ 'inverse_of: ?%symstr, optional: ?%bool, required: ?%bool, anonymous_class: ?Class) -> %any'
73
+
74
+ pre :belongs_to do |name, scope=nil, class_name: nil, foreign_key: nil, primary_key: nil,
75
+ dependent: nil, counter_cache: nil, polymorphic: nil,
76
+ validate: nil, autosave: nil, touch: nil, inverse_of: nil,
77
+ optional: nil, required: nil, anonymous_class: nil|
78
+ if polymorphic
79
+ assoc_type = '%any' # type is data-driven, can't determine statically
80
+ elsif class_name
81
+ assoc_type = class_name.to_s.classify
82
+ elsif anonymous_class
83
+ assoc_type = anonymous_class.to_s
84
+ else
85
+ assoc_type = name.to_s.classify # camelize?
86
+ end
87
+ type name, "(?%bool force_reload) -> #{assoc_type}"
88
+ type "#{name}=", "(#{assoc_type}) -> #{assoc_type}"
89
+ unless polymorphic
90
+ rdl_at(:model) { |sym|
91
+ assoc_attribute_types = RDL::Rails.attribute_types(assoc_type.constantize)
92
+ type "build_#{name}", "(#{assoc_attribute_types}) -> #{assoc_type}"
93
+ type "create_#{name}", "(#{assoc_attribute_types}) -> #{assoc_type}"
94
+ type "create_#{name}!", "(#{assoc_attribute_types}) -> #{assoc_type}"
95
+ }
96
+ end
97
+ true
98
+ end
99
+
100
+ type :has_one, '(%symstr name, ?{ (?ActiveRecord::Base) -> %any } scope, class_name: ?%symstr,'+
101
+ 'dependent: ?(:destroy or :delete or :nullify or :restrict_with_exception or :restrict_with_error),' +
102
+ 'foreign_key: ?%symstr, foreign_type: ?%symstr, primary_key: ?%symstr, as: ?%symstr,' +
103
+ 'through: ?%symstr, source: ?%symstr, source_type: ?%symstr, validate: ?%bool, autosave: ?%bool,' +
104
+ 'inverse_of: ?%symstr, required: ?%bool, anonymous_class: ?Class) -> %any'
105
+
106
+ pre :has_one do |name, scope=nil, class_name: nil, dependent: nil, foreign_key: nil,
107
+ foreign_type: nil, primary_key: nil, as: nil, through: nil, source: nil,
108
+ source_type: nil, vadliate: nil, autosave: nil, inverse_of: nil,
109
+ required: nil|
110
+ if as
111
+ assoc_type = '%any' # type is data-driven, can't determine statically
112
+ elsif class_name
113
+ assoc_type = class_name.to_s.classify
114
+ elsif anonymous_class # not sure this has anonymou_class
115
+ assoc_type = anonymous_class.to_s.classify
116
+ else
117
+ assoc_type = name.to_s.classify # camelize?
118
+ end
119
+ type name, "(?%bool force_reload) -> #{assoc_type}"
120
+ type "#{name}=", "(#{assoc_type}) -> #{assoc_type}"
121
+ unless as
122
+ rdl_at(:model) { |sym|
123
+ assoc_attribute_types = RDL::Rails.attribute_types(assoc_type.constantize)
124
+ type "build_#{name}", "(#{assoc_attribute_types}) -> #{assoc_type}"
125
+ type "create_#{name}", "(#{assoc_attribute_types}) -> #{assoc_type}"
126
+ type "create_#{name}!", "(#{assoc_attribute_types}) -> #{assoc_type}"
127
+ }
128
+ end
129
+ end
130
+
131
+ type :has_many, '(%symstr name, ?{ (?ActiveRecord::Base) -> %any } scope, class_name: ?%symstr,' +
132
+ 'foreign_key: ?%symstr, foreign_type: ?%symstr, primary_key: ?%symstr,' +
133
+ 'dependent: ?(:destroy or :delete_all or :nullify or :restrict_with_exception or :restrict_with_error),' +
134
+ 'counter_cache: ?(%bool or %symstr), as: ?%symstr, through: ?%symstr, source: ?%symstr,' +
135
+ 'source_type: ?%symstr, validate: ?%bool, inverse_of: ?%symstr, extend: ?(Module or Array<Module>))' +
136
+ '?{ () -> %any } -> %any'
137
+
138
+ pre :has_many do |name, scope=nil, class_name: nil, foreign_key: nil, foreign_type: nil, primary_key: nil,
139
+ dependent: nil, counter_cache: nil, as: nil, through: nil, source: nil, source_type: nil,
140
+ validate: nil, inverse_of: nil, extend: nil|
141
+ if class_name
142
+ collect_type = class_name.to_s.classify
143
+ else
144
+ collect_type = name.to_s.singularize.classify
145
+ end
146
+ type name, "() -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>"
147
+ type "#{name}=", "(Array<t>) -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>" # TODO not sure of type
148
+ id_type = RDL::Rails.column_to_rdl(collect_type.constantize.columns_hash['id'].type) # TODO assumes id field is "id"
149
+ type "#{name.to_s.singularize}_ids", "() -> Array<#{id_type}>"
150
+ type "#{name.to_s.singularize}_ids=", "() -> Array<#{id_type}>"
151
+
152
+ true
153
+ end
154
+
155
+ type :has_and_belongs_to_many, '(%symstr name, ?{ (?ActiveRecord::Base) -> %any } scope, class_name: ?%symstr,' +
156
+ 'join_table: ?%symstr, foreign_key: ?%symstr, association_foreign_key: ?%symstr,' +
157
+ 'validate: ?%bool, autosave: ?%bool) ?{ () -> %any } -> %any'
158
+
159
+ pre :has_and_belongs_to_many do |name, scope=nil, class_name: nil, join_table: nil,
160
+ foreign_key: nil, association_foreign_key: nil,
161
+ validate: nil, autosave: nil|
162
+ if class_name
163
+ collect_type = class_name.to_s.classify
164
+ else
165
+ collect_type = name.to_s.singularize.classify
166
+ end
167
+ type name, "() -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>"
168
+ type "#{name}=", "(Array<t>) -> ActiveRecord::Associations::CollectionProxy<#{collect_type}>" # TODO not sure of type
169
+ id_type = RDL::Rails.column_to_rdl(collect_type.constantize.columns_hash['id'].type) # TODO assumes id field is "id"
170
+ type "#{name.to_s.singularize}_ids", "() -> Array<#{id_type}>"
171
+ type "#{name.to_s.singularize}_ids=", "() -> Array<#{id_type}>"
172
+
173
+ # Remaining methods are from CollectionProxy
174
+ # TODO give these precise types for this particular model
175
+ # collection<<(object, ...)
176
+ # collection.delete(object, ...)
177
+ # collection.destroy(object, ...)
178
+ # collection.clear
179
+ # collection.empty?
180
+ # collection.size
181
+ # collection.find(...)
182
+ # collection.where(...)
183
+ # collection.exists?(...)
184
+ # collection.build(attributes = {})
185
+ # collection.create(attributes = {})
186
+ # collection.create!(attributes = {})
187
+ true
188
+ end
189
+
190
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveRecord::Core
2
+ type :==, '(%any) -> %bool'
3
+ end
@@ -2,8 +2,6 @@ module ActiveRecord
2
2
  module ModelSchema
3
3
  module ClassMethods
4
4
  post(:load_schema!) { |ret| # load_schema! doesn't return anything interesting
5
- find_by_args = []
6
-
7
5
  columns_hash.each { |name, col|
8
6
  t = RDL::Rails.column_to_rdl(col.type)
9
7
  if col.null
@@ -13,7 +11,6 @@ module ActiveRecord
13
11
  type "write_attribute", "(:#{name}, #{t}) -> %bool"
14
12
  type "update_attribute", "(:#{name}, #{t}) -> %bool"
15
13
  type "update_column", "(:#{name}, #{t}) -> %bool"
16
- find_by_args << "#{name}: ?#{t}"
17
14
  else
18
15
  # not null; can't truly check in type system but hint via the name
19
16
  type name, "() -> !#{t}" # getter
@@ -21,15 +18,15 @@ module ActiveRecord
21
18
  type "write_attribute", "(:#{name}, !#{t}) -> %bool"
22
19
  type "update_attribute", "(:#{name}, #{t}) -> %bool"
23
20
  type "update_column", "(:#{name}, #{t}) -> %bool"
24
- find_by_args << "#{name}: ?!#{t}"
25
21
  end
26
22
  }
27
- hash_args = find_by_args.join(',')
28
- type 'self.find_by', '(' + hash_args + ") -> #{self} or nil"
29
- type 'self.find_by!', '(' + hash_args + ") -> #{self}"
30
- type 'update', '(' + hash_args + ') -> %bool'
31
- type 'update_columns', '(' + hash_args + ') -> %bool'
32
- type 'attributes=', '(' + hash_args + ') -> %bool'
23
+
24
+ attribute_types = RDL::Rails.attribute_types(self)
25
+ type 'self.find_by', '(' + attribute_types + ") -> #{self} or nil"
26
+ type 'self.find_by!', '(' + attribute_types + ") -> #{self}"
27
+ type 'update', '(' + attribute_types + ') -> %bool'
28
+ type 'update_columns', '(' + attribute_types + ') -> %bool'
29
+ type 'attributes=', '(' + attribute_types + ') -> %bool'
33
30
 
34
31
  # If called with String arguments, can't check types as precisely
35
32
  type 'write_attribute', '(String, %any) -> %bool'
@@ -4,8 +4,8 @@
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'rdl'
7
- s.version = '2.0.0.rc4'
8
- s.date = '2016-08-13'
7
+ s.version = '2.0.0.rc5'
8
+ s.date = '2016-08-24'
9
9
  s.summary = 'Ruby type and contract system'
10
10
  s.description = <<-EOF
11
11
  RDL is a gem that adds types and contracts to Ruby. RDL includes extensive
@@ -168,6 +168,29 @@ class TestLe < Minitest::Test
168
168
  assert (tfs2_4 <= thfs_4) # allowed, both tfs2_4 and tfs_4 promoted to hash
169
169
  tfs3_4 = tt "{x: Fixnum, y: String}"
170
170
  assert (not(tfs_4 <= tfs3_4)) # not allowed, tfs_4 has been promoted
171
+
172
+ tfss_5 = tt "{x: Fixnum, y: String, **Symbol}"
173
+ tfns_5 = tt "{x: Fixnum, **Symbol}"
174
+ tfsn_5 = tt "{x: Fixnum, y: String}"
175
+ tftn_5 = tt "{x: Fixnum, z: Symbol}"
176
+ tooo_5 = tt "{x: Object, y: Object, **Object}"
177
+ tono_5 = tt "{x: Object, **Object}"
178
+ assert (tfss_5 <= tooo_5)
179
+ assert (tfns_5 <= tono_5)
180
+ assert (not (tfss_5 <= tfns_5))
181
+ assert (not (tfss_5 <= tfsn_5))
182
+ assert (not (tfss_5 <= tftn_5))
183
+ assert (not (tfns_5 <= tfss_5))
184
+ assert (not (tfns_5 <= tfsn_5))
185
+ assert (not (tfns_5 <= tftn_5))
186
+ assert (tfsn_5 <= tfss_5)
187
+ assert (not (tfsn_5 <= tfns_5))
188
+ assert (not (tfsn_5 <= tftn_5))
189
+ assert (not (tftn_5 <= tfss_5))
190
+ assert (tftn_5 <= tfns_5)
191
+ assert (not (tftn_5 <= tfsn_5))
192
+
193
+ assert (not (tt("{x: ?Fixnum}") <= tt("{x: Fixnum}")))
171
194
  end
172
195
 
173
196
  def test_method
@@ -197,6 +220,7 @@ class TestLe < Minitest::Test
197
220
  assert (not (tbos <= tbso))
198
221
  assert (tbso <= tbso)
199
222
  assert (tbso <= tbos)
223
+ assert (tss <= $__rdl_proc_type)
200
224
  end
201
225
 
202
226
  def test_structural