vhdl_doctest 0.0.2 → 0.0.3

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.
data/lib/vhdl_doctest.rb CHANGED
@@ -5,6 +5,7 @@ require "vhdl_doctest/types"
5
5
  require "vhdl_doctest/test_file"
6
6
  require "vhdl_doctest/test_case"
7
7
  require "vhdl_doctest/dut"
8
+ require "vhdl_doctest/test_parser"
8
9
 
9
10
  module VhdlDoctest
10
11
  # Your code goes here...
@@ -22,15 +22,15 @@ module VhdlDoctest
22
22
  def parse
23
23
  entity = @vhdl.match(/entity\s+(.*)\s+is/)[1]
24
24
  ports = extract_ports
25
- cases = extract_test_cases
25
+ cases = TestParser.parse(ports, @vhdl)
26
26
 
27
- [entity, ports, cases.map{ |c| TestCase.new(Hash[ports.zip(c)]) }]
27
+ [entity, ports, cases]
28
28
  end
29
29
 
30
30
  def extract_ports
31
31
  return @ports if @ports
32
32
  @ports = []
33
- definitions = @vhdl.match(/entity.*is\s+port\s*\((.*)\);\s*end/m)[1]
33
+ definitions = @vhdl.match(/entity.*is\s+port\s*\((.*?)\);\s*end/m)[1]
34
34
  definitions.split("\n").each do |l|
35
35
  names, attributes = l.strip.gsub(/;$/, '').split(":")
36
36
  next unless attributes
@@ -41,32 +41,6 @@ module VhdlDoctest
41
41
  end
42
42
  @ports
43
43
  end
44
-
45
- def extract_test_cases
46
- definitions = @vhdl.match(/-- TEST\n(.*)-- \/TEST/m)[1]
47
- header, *body = definitions.split("\n").map { |l| l[3..-1].split("|").map(&:strip) }
48
-
49
- header.each_with_index do |h, idx|
50
- radix = 10
51
- if h.include?(' ')
52
- case h[-1]
53
- when 'b'
54
- radix = 2
55
- when 'h', 'x'
56
- radix = 16
57
- end
58
- end
59
- prev = ''
60
- body.each do |l|
61
- if l[idx].empty?
62
- l[idx] = prev
63
- else
64
- prev = l[idx] = l[idx].to_i(radix)
65
- end
66
- end
67
- end
68
- body
69
- end
70
44
  end
71
45
  end
72
46
  end
@@ -1,7 +1,12 @@
1
1
  module VhdlDoctest
2
2
  class TestCase
3
+ attr_reader :in_mapping, :out_mapping
3
4
  def initialize(mapping)
4
5
  @in_mapping, @out_mapping = mapping.partition{ |port, _| port.in? }
6
+ if @in_mapping.find { |_, v| v == :dont_care }
7
+ raise NotImplementedError.new("Don't care for input value is not supported")
8
+ end
9
+ @out_mapping.select!{ |k, v| v != :dont_care }
5
10
  end
6
11
 
7
12
  def to_vhdl
@@ -16,8 +21,12 @@ module VhdlDoctest
16
21
  end
17
22
 
18
23
  def assertion
19
- cond = @out_mapping.map { |port, value| port.equation(value) }.join(" and ")
20
24
  inputs = @in_mapping.map { |port, value| "#{port.name} = #{value}" }.join(", ")
25
+ if @out_mapping.empty?
26
+ warn "There is no assertion for #{inputs}."
27
+ return ''
28
+ end
29
+ cond = @out_mapping.map { |port, value| port.equation(value) }.join(" and ")
21
30
  expected = @out_mapping.map { |port, value| "#{port.name} = #{value}" }.join(", ")
22
31
  actual = @out_mapping.map { |port, value| "#{port.name} = \" & to_string(#{port.name}) & \"" }.join(", ")
23
32
  %Q{assert #{ cond } report "FAILED: #{inputs} expected to #{expected}, but #{actual}" severity warning;}
@@ -0,0 +1,105 @@
1
+ module VhdlDoctest
2
+ class OutOfRangeSymbolError < RuntimeError
3
+ def initialize(port, radix, bad_value)
4
+ @port, @bad_value = port, bad_value
5
+
6
+ @radix = case radix
7
+ when 2; 'binary'
8
+ when 10; 'decimal'
9
+ when 16; 'hex'
10
+ end
11
+ end
12
+
13
+ def to_s
14
+ "#@port expects #@radix, but received #@bad_value"
15
+ end
16
+ end
17
+
18
+ module TestParser
19
+ extend self
20
+ def parse(ports, vhdl)
21
+ names, vectors = extract_values(vhdl)
22
+ defined_ports = names.map { |name| ports.find { |p| p.name == name } }
23
+ vectors.map { |v| TestCase.new(Hash[defined_ports.zip(v)]) }
24
+ end
25
+
26
+ private
27
+ def assert_in_range(port_name, radix, string)
28
+ symbols = case radix
29
+ when 2
30
+ %w{ 0 1 }
31
+ when 10
32
+ ("0".."9").to_a + %w{ - }
33
+ when 16
34
+ ("0".."9").to_a + ("a".."f").to_a
35
+ else
36
+ []
37
+ end
38
+
39
+ unless string.split(//).all? { |s| symbols.include? s }
40
+ raise OutOfRangeSymbolError.new(port_name, radix, string)
41
+ end
42
+ end
43
+
44
+ def remove_comment(line)
45
+ line.match(/--\s*([^#]*)/)[1]
46
+ rescue
47
+ raise "line: #{line} is not formatted correctly"
48
+ end
49
+
50
+ def extract_fields(line)
51
+ line.split("|").map(&:strip)
52
+ end
53
+
54
+ def test_definitions(vhdl)
55
+ lines = vhdl.match(/-- TEST\n(.*)-- \/TEST/m)[1].split("\n")
56
+ lines.partition { |l| l.include? 'alias' }
57
+ rescue
58
+ raise "Test definition not found"
59
+ end
60
+
61
+ def radix(attr)
62
+ if attr
63
+ case attr[-1]
64
+ when 'b'; 2
65
+ when 'h', 'x'; 16
66
+ end
67
+ else
68
+ 10
69
+ end
70
+ end
71
+
72
+ def replace_aliases(defs, table)
73
+ pairs = defs.map { |l| l.match(/alias\s+(.*)\s+(.*)$/)[1..2] }
74
+ table.each { |l| pairs.each { |p| l.gsub!(p[0], p[1]) } }
75
+ table
76
+ end
77
+
78
+ def extract_values(vhdl)
79
+ table = replace_aliases(*test_definitions(vhdl))
80
+ header, *body = table.map { |l| extract_fields remove_comment l }
81
+ port_names = []
82
+
83
+ header.each_with_index do |h, idx|
84
+ port_name, attr = h.split(' ', 2)
85
+ port_names << port_name
86
+ prev = ''
87
+ radix = radix(attr)
88
+ body.each do |l|
89
+ if l[idx].empty?
90
+ l[idx] = prev
91
+ else
92
+ if l[idx].strip.match(/^-+$/)
93
+ l[idx] = :dont_care
94
+ else
95
+ assert_in_range(port_name, radix, l[idx])
96
+ l[idx] = l[idx].to_i(radix)
97
+ end
98
+ prev = l[idx]
99
+ end
100
+ end
101
+ end
102
+ [port_names, body]
103
+ end
104
+ end
105
+ end
@@ -11,6 +11,7 @@ module VhdlDoctest
11
11
  return result
12
12
  end
13
13
  end
14
+ raise "Type for #{str} is not found."
14
15
  end
15
16
  end
16
17
  end
@@ -14,7 +14,7 @@ module VhdlDoctest::Types
14
14
  end
15
15
 
16
16
  def self.parse(str)
17
- new if str.strip == 'std_logic'
17
+ new if str.strip.downcase == 'std_logic'
18
18
  end
19
19
  end
20
20
  end
@@ -13,8 +13,13 @@ module VhdlDoctest::Types
13
13
  end
14
14
 
15
15
  def self.parse(str)
16
- if str.strip.match(/\Astd_logic_vector\s*\((\d+)\s+downto\s+0\)\Z/i)
17
- new($1.to_i + 1)
16
+ str = str.strip
17
+ if str.match(/\Astd_logic_vector/i)
18
+ if str.strip.match(/\((\d+)\s+downto\s+0\)\Z/i)
19
+ new($1.to_i + 1)
20
+ else
21
+ raise "#{ str } is std_logic_vector, but not 'x downto 0'"
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -1,3 +1,3 @@
1
1
  module VhdlDoctest
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+ module VhdlDoctest
4
+ # Expect TestCase to set given hash pairs as stimulus
5
+ RSpec::Matchers.define :set do |expected|
6
+ match do |actual|
7
+ expected.all? { |k, v| actual.in_mapping.
8
+ find { |port, value| port.name == k.to_s && v == value } }
9
+ end
10
+ end
11
+
12
+ # Expect TestCase to assert given hash pairs
13
+ RSpec::Matchers.define :assert do |expected|
14
+ match do |actual|
15
+ expected.all? { |k, v| actual.out_mapping.
16
+ find { |port, value| port.name == k.to_s && (v == nil || v == value) } }
17
+ end
18
+ end
19
+
20
+ describe TestParser do
21
+ let(:ports) {[
22
+ Port.new("a", :in, Types::StdLogicVector.new(32)),
23
+ Port.new("b", :in, Types::StdLogicVector.new(32)),
24
+ Port.new("control", :in, Types::StdLogicVector.new(3)),
25
+ Port.new("output", :out, Types::StdLogicVector.new(32)),
26
+ Port.new("zero", :out, Types::StdLogic.new)
27
+ ]}
28
+ subject(:cases) { TestParser.parse(ports, input) }
29
+
30
+ describe 'header only' do
31
+ let(:input) { %q{
32
+ -- TEST
33
+ -- a | b | control | output | zero
34
+ -- /TEST
35
+ }}
36
+ it 'should not fail to parse' do
37
+ expect(cases).to have(0).items
38
+ end
39
+ end
40
+
41
+ describe 'single case' do
42
+ let(:input) { %q{
43
+ -- TEST
44
+ -- a | b | control | output | zero
45
+ -- 3 | 5 | 2 | 8 | 0
46
+ -- /TEST
47
+ }}
48
+
49
+ it { should have(1).item }
50
+ its(:first) { should set(a: 3, b: 5, control: 2) }
51
+ its(:first) { should assert(output: 8, zero: 0) }
52
+ end
53
+
54
+ describe 'two cases with an empty column' do
55
+ let(:input) { %q{
56
+ -- TEST
57
+ -- a | b | control | output | zero
58
+ -- 3 | 5 | 2 | 8 | 0
59
+ -- 9 | | 2 | 14 | 0
60
+ -- /TEST
61
+ }}
62
+
63
+ it { should have(2).items }
64
+ its(:last) { should set(a: 9, b: 5, control: 2) }
65
+ end
66
+
67
+ describe 'field redix specification' do
68
+ let(:input) { %q{
69
+ -- TEST
70
+ -- a h | b x | control b | output | zero
71
+ -- 10 | 20 | 010 | 8 | 0
72
+ -- /TEST
73
+ }}
74
+
75
+ specify { cases.first.should set(a: 16, b: 32, control: 2) }
76
+ end
77
+
78
+ describe 'wrong input for redix' do
79
+ let(:input) { %q{
80
+ -- TEST
81
+ -- a h | b x | control b | output | zero
82
+ -- 10 | 20 | 012 | 8 | 0
83
+ -- /TEST
84
+ }}
85
+
86
+ it { expect{ cases }.to raise_error(OutOfRangeSymbolError, /control.*binary.*012/) }
87
+ end
88
+
89
+ describe 'dont care in assertion' do
90
+ let(:input) { %q{
91
+ -- TEST
92
+ -- a | b | control b | output | zero
93
+ -- 10 | 20 | 010 | 30 | 0
94
+ -- 10 | -10 | | 0 | -
95
+ -- /TEST
96
+ }}
97
+
98
+ specify { cases[0].should assert(output: 30, zero: 0) }
99
+ specify { cases[1].should set(a: 10, b: -10, control: 2) }
100
+ specify { cases[1].should_not assert([:zero]) }
101
+ end
102
+
103
+ describe 'dont care in stimuli' do
104
+ let(:input) { %q{
105
+ -- TEST
106
+ -- a | b | control b | output | zero
107
+ -- 10 | - | 010 | 30 | 0
108
+ -- /TEST
109
+ }}
110
+
111
+ specify { expect{ cases }.to raise_error(NotImplementedError) }
112
+ end
113
+
114
+ describe 'all assertions are dont_care' do
115
+ let(:input) { %q{
116
+ -- TEST
117
+ -- a | b | control | output | zero
118
+ -- 10 | -10 | 2 | - | -
119
+ -- /TEST
120
+ }}
121
+
122
+ specify { cases.first.to_vhdl.should_not match /assert/ }
123
+ end
124
+
125
+ describe 'partial specification' do
126
+ let(:input) { %q{
127
+ -- TEST
128
+ -- a | b | control | zero
129
+ -- 10 | -10 | 2 | 1
130
+ -- /TEST
131
+ }}
132
+
133
+ specify { cases.first.should assert(zero: 1) }
134
+ specify { cases.first.should_not assert([:control]) }
135
+ end
136
+
137
+ describe 'comment' do
138
+ let(:input) { %q{
139
+ -- TEST
140
+ -- a | b | control | zero # header
141
+ -- 10 | -10 | 2 | 1 # case1
142
+ -- 10 | | 2 | 1 # case2 # important
143
+ -- /TEST
144
+ }}
145
+
146
+ specify { cases.first.should assert(zero: 1) }
147
+ specify { cases.last.should assert(zero: 1) }
148
+ end
149
+
150
+ describe 'alias' do
151
+ let(:input) { %q{
152
+ -- TEST
153
+ -- alias TRUE 1
154
+ -- alias FALSE 0
155
+ -- a | b | control | zero
156
+ -- 10 | -10 | 2 | TRUE
157
+ -- 10 | 10 | 2 | FALSE
158
+ -- /TEST
159
+ }}
160
+
161
+ specify { cases.first.should assert(zero: 1) }
162
+ specify { cases.last.should assert(zero: 0) }
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ module VhdlDoctest
4
+ describe Types do
5
+ describe ".parse" do
6
+ subject { Types.parse(string) }
7
+
8
+ describe 'std_logic' do
9
+ let(:string) { 'std_logic' }
10
+ it { should be_a Types::StdLogic }
11
+ end
12
+
13
+ describe 'std_logic_vector' do
14
+ let(:string) { 'std_logic_vector(8 downto 0)' }
15
+ it { should be_a Types::StdLogicVector }
16
+ end
17
+
18
+ describe 'std_logic_vector, but not in format' do
19
+ let(:string) { 'std_logic_vector(0 upto 8)' }
20
+ specify { expect { subject }.to raise_error }
21
+ end
22
+
23
+ describe 'upcase STD_LOGIC' do
24
+ let(:string) { 'STD_LOGIC' }
25
+ it { should be_a Types::StdLogic }
26
+ end
27
+
28
+ describe 'upcase STD_LOGIC_VECTOR' do
29
+ let(:string) { 'STD_LOGIC_VECTOR(2 DOWNTO 0)' }
30
+ it { should be_a Types::StdLogicVector }
31
+ end
32
+
33
+ describe 'unknown' do
34
+ let(:string) { 'unkonwn' }
35
+ specify { expect { subject }.to raise_error }
36
+ end
37
+ end
38
+ end
39
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vhdl_doctest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-23 00:00:00.000000000 Z
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -64,6 +64,7 @@ files:
64
64
  - lib/vhdl_doctest/port.rb
65
65
  - lib/vhdl_doctest/test_case.rb
66
66
  - lib/vhdl_doctest/test_file.rb
67
+ - lib/vhdl_doctest/test_parser.rb
67
68
  - lib/vhdl_doctest/test_runner.rb
68
69
  - lib/vhdl_doctest/types.rb
69
70
  - lib/vhdl_doctest/types/std_logic.rb
@@ -72,7 +73,9 @@ files:
72
73
  - spec/dut_spec.rb
73
74
  - spec/port_spec.rb
74
75
  - spec/spec_helper.rb
76
+ - spec/test_parser_spec.rb
75
77
  - spec/test_runner_spec.rb
78
+ - spec/types_spec.rb
76
79
  - vhdl_doctest.gemspec
77
80
  homepage: ''
78
81
  licenses: []
@@ -88,7 +91,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
91
  version: '0'
89
92
  segments:
90
93
  - 0
91
- hash: 574229357
94
+ hash: -473783249
92
95
  required_rubygems_version: !ruby/object:Gem::Requirement
93
96
  none: false
94
97
  requirements:
@@ -97,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
100
  version: '0'
98
101
  segments:
99
102
  - 0
100
- hash: 574229357
103
+ hash: -473783249
101
104
  requirements: []
102
105
  rubyforge_project:
103
106
  rubygems_version: 1.8.24
@@ -108,4 +111,6 @@ test_files:
108
111
  - spec/dut_spec.rb
109
112
  - spec/port_spec.rb
110
113
  - spec/spec_helper.rb
114
+ - spec/test_parser_spec.rb
111
115
  - spec/test_runner_spec.rb
116
+ - spec/types_spec.rb