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 +1 -0
- data/lib/vhdl_doctest/dut.rb +3 -29
- data/lib/vhdl_doctest/test_case.rb +10 -1
- data/lib/vhdl_doctest/test_parser.rb +105 -0
- data/lib/vhdl_doctest/types.rb +1 -0
- data/lib/vhdl_doctest/types/std_logic.rb +1 -1
- data/lib/vhdl_doctest/types/std_logic_vector.rb +7 -2
- data/lib/vhdl_doctest/version.rb +1 -1
- data/spec/test_parser_spec.rb +165 -0
- data/spec/types_spec.rb +39 -0
- metadata +9 -4
data/lib/vhdl_doctest.rb
CHANGED
data/lib/vhdl_doctest/dut.rb
CHANGED
@@ -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 =
|
25
|
+
cases = TestParser.parse(ports, @vhdl)
|
26
26
|
|
27
|
-
[entity, ports, cases
|
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*\((
|
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
|
data/lib/vhdl_doctest/types.rb
CHANGED
@@ -13,8 +13,13 @@ module VhdlDoctest::Types
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.parse(str)
|
16
|
-
|
17
|
-
|
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
|
data/lib/vhdl_doctest/version.rb
CHANGED
@@ -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
|
data/spec/types_spec.rb
ADDED
@@ -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.
|
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-
|
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:
|
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:
|
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
|