vhdl_doctest 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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