stamina 0.3.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.
- data/.gemtest +0 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +33 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +16 -0
- data/README.md +78 -0
- data/Rakefile +23 -0
- data/bin/adl2dot +12 -0
- data/bin/classify +12 -0
- data/bin/redblue +12 -0
- data/bin/rpni +12 -0
- data/example/adl/automaton.adl +49 -0
- data/example/adl/sample.adl +53 -0
- data/example/basic/characteristic_sample.adl +32 -0
- data/example/basic/target.adl +9 -0
- data/example/competition/31_test.adl +1500 -0
- data/example/competition/31_training.adl +1759 -0
- data/lib/stamina.rb +19 -0
- data/lib/stamina/adl.rb +298 -0
- data/lib/stamina/automaton.rb +1237 -0
- data/lib/stamina/automaton/walking.rb +336 -0
- data/lib/stamina/classifier.rb +37 -0
- data/lib/stamina/command/adl2dot_command.rb +73 -0
- data/lib/stamina/command/classify_command.rb +57 -0
- data/lib/stamina/command/redblue_command.rb +58 -0
- data/lib/stamina/command/rpni_command.rb +58 -0
- data/lib/stamina/command/stamina_command.rb +79 -0
- data/lib/stamina/errors.rb +20 -0
- data/lib/stamina/induction/commons.rb +170 -0
- data/lib/stamina/induction/redblue.rb +264 -0
- data/lib/stamina/induction/rpni.rb +188 -0
- data/lib/stamina/induction/union_find.rb +377 -0
- data/lib/stamina/input_string.rb +123 -0
- data/lib/stamina/loader.rb +0 -0
- data/lib/stamina/markable.rb +42 -0
- data/lib/stamina/sample.rb +190 -0
- data/lib/stamina/version.rb +14 -0
- data/stamina.gemspec +190 -0
- data/stamina.noespec +35 -0
- data/tasks/debug_mail.rake +78 -0
- data/tasks/debug_mail.txt +13 -0
- data/tasks/gem.rake +68 -0
- data/tasks/spec_test.rake +79 -0
- data/tasks/unit_test.rake +77 -0
- data/tasks/yard.rake +51 -0
- data/test/stamina/adl_test.rb +491 -0
- data/test/stamina/automaton_additional_test.rb +190 -0
- data/test/stamina/automaton_classifier_test.rb +155 -0
- data/test/stamina/automaton_test.rb +1092 -0
- data/test/stamina/automaton_to_dot_test.rb +64 -0
- data/test/stamina/automaton_walking_test.rb +206 -0
- data/test/stamina/exit.rb +3 -0
- data/test/stamina/induction/induction_test.rb +70 -0
- data/test/stamina/induction/redblue_mergesamestatebug_expected.adl +19 -0
- data/test/stamina/induction/redblue_mergesamestatebug_pta.dot +64 -0
- data/test/stamina/induction/redblue_mergesamestatebug_sample.adl +9 -0
- data/test/stamina/induction/redblue_test.rb +83 -0
- data/test/stamina/induction/redblue_universal_expected.adl +4 -0
- data/test/stamina/induction/redblue_universal_sample.adl +5 -0
- data/test/stamina/induction/rpni_inria_expected.adl +7 -0
- data/test/stamina/induction/rpni_inria_sample.adl +9 -0
- data/test/stamina/induction/rpni_test.rb +129 -0
- data/test/stamina/induction/rpni_test_pta.dot +22 -0
- data/test/stamina/induction/rpni_universal_expected.adl +4 -0
- data/test/stamina/induction/rpni_universal_sample.adl +4 -0
- data/test/stamina/induction/union_find_test.rb +124 -0
- data/test/stamina/input_string_test.rb +323 -0
- data/test/stamina/markable_test.rb +70 -0
- data/test/stamina/randdfa.adl +66 -0
- data/test/stamina/sample.adl +4 -0
- data/test/stamina/sample_classify_test.rb +149 -0
- data/test/stamina/sample_test.rb +218 -0
- data/test/stamina/small_dfa.dot +16 -0
- data/test/stamina/small_dfa.gif +0 -0
- data/test/stamina/small_nfa.dot +18 -0
- data/test/stamina/small_nfa.gif +0 -0
- data/test/stamina/stamina_test.rb +69 -0
- data/test/test_all.rb +7 -0
- metadata +279 -0
data/stamina.noespec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Noe template for ruby gem libraries (https://github.com/blambeau/noe) - short version
|
2
|
+
# Run 'noe show-spec' and 'noe help show-spec' for additional details.
|
3
|
+
template-info:
|
4
|
+
name: "ruby"
|
5
|
+
version: 1.3.0
|
6
|
+
variables:
|
7
|
+
lower:
|
8
|
+
stamina
|
9
|
+
upper:
|
10
|
+
Stamina
|
11
|
+
version:
|
12
|
+
0.3.0
|
13
|
+
summary: |-
|
14
|
+
Automaton and Regular Inference Toolkit
|
15
|
+
description: |-
|
16
|
+
Stamina is an automaton and regular inference toolkit initially developped for the baseline
|
17
|
+
of the Stamina Competition (stamina.chefbe.net).
|
18
|
+
authors:
|
19
|
+
- name: Bernard Lambeau
|
20
|
+
email: blambeau@gmail.com
|
21
|
+
links:
|
22
|
+
- http://stamina.chefbe.net/
|
23
|
+
- http://github.com/blambeau/stamina
|
24
|
+
dependencies:
|
25
|
+
# Rake is required for developers, as usual
|
26
|
+
- {name: rake, version: "~> 0.8.7", groups: [development]}
|
27
|
+
# Bundler is required for developers and is used by the Rakefile
|
28
|
+
- {name: bundler, version: "~> 1.0", groups: [development]}
|
29
|
+
# RSpec is required to run 'rake spec'. See tasks/spec.rake
|
30
|
+
- {name: rspec, version: "~> 2.4.0", groups: [development]}
|
31
|
+
# YARD and BlueCloth are required to run 'rake yard'. See tasks/yard.rake
|
32
|
+
- {name: yard, version: "~> 0.6.4", groups: [development]}
|
33
|
+
- {name: bluecloth, version: "~> 2.0.9", groups: [development]}
|
34
|
+
# wlang is required to run 'rake debug_mail'. See tasks/debug_mail.rake
|
35
|
+
- {name: wlang, version: "~> 0.10.1", groups: [development]}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Installs a rake task for debuging the announcement mail.
|
2
|
+
#
|
3
|
+
# This file installs the 'rake debug_mail' that flushes an announcement mail
|
4
|
+
# for your library on the standard output. It is automatically generated
|
5
|
+
# by Noe from your .noespec file, and should therefore be configured there,
|
6
|
+
# under the variables/rake_tasks/debug_mail entry, as illustrated below:
|
7
|
+
#
|
8
|
+
# variables:
|
9
|
+
# rake_tasks:
|
10
|
+
# debug_mail:
|
11
|
+
# rx_changelog_sections: /^#/
|
12
|
+
# nb_changelog_sections: 1
|
13
|
+
# ...
|
14
|
+
#
|
15
|
+
# If you have specific needs requiring manual intervention on this file,
|
16
|
+
# don't forget to set safe-override to false in your noe specification:
|
17
|
+
#
|
18
|
+
# template-info:
|
19
|
+
# manifest:
|
20
|
+
# tasks/debug_mail.rake:
|
21
|
+
# safe-override: false
|
22
|
+
#
|
23
|
+
# The mail template used can be found in debug_mail.txt. That file may be
|
24
|
+
# changed to tune the mail you want to send. If you do so, don't forget to
|
25
|
+
# add a manifest entry in your .noespec file to avoid overriding you
|
26
|
+
# changes. The mail template uses wlang, with parentheses for block
|
27
|
+
# delimiters.
|
28
|
+
#
|
29
|
+
# template-info:
|
30
|
+
# manifest:
|
31
|
+
# tasks/debug_mail.txt:
|
32
|
+
# safe-override: false
|
33
|
+
#
|
34
|
+
begin
|
35
|
+
require 'wlang'
|
36
|
+
require 'yaml'
|
37
|
+
|
38
|
+
desc "Debug the release announcement mail"
|
39
|
+
task :debug_mail do
|
40
|
+
# Check that a .noespec file exists
|
41
|
+
noespec_file = File.expand_path('../../stamina.noespec', __FILE__)
|
42
|
+
unless File.exists?(noespec_file)
|
43
|
+
raise "Unable to find .noespec project file, sorry."
|
44
|
+
end
|
45
|
+
|
46
|
+
# Load it as well as variables and options
|
47
|
+
noespec = YAML::load(File.read(noespec_file))
|
48
|
+
vars = noespec['variables'] || {}
|
49
|
+
|
50
|
+
# Changes are taken from CHANGELOG
|
51
|
+
logs = Dir[File.expand_path("../../CHANGELOG.*", __FILE__)]
|
52
|
+
unless logs.size == 1
|
53
|
+
abort "Unable to find a changelog file"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Load interesting changesets
|
57
|
+
changes, end_found = [], 0
|
58
|
+
File.readlines(logs.first).select{|line|
|
59
|
+
if line =~ /^#/
|
60
|
+
break if end_found >= 1
|
61
|
+
end_found += 1
|
62
|
+
end
|
63
|
+
changes << line
|
64
|
+
}
|
65
|
+
vars['changes'] = changes.join
|
66
|
+
|
67
|
+
# WLang template
|
68
|
+
template = File.expand_path('../debug_mail.txt', __FILE__)
|
69
|
+
|
70
|
+
# Let's go!
|
71
|
+
$stdout << WLang::file_instantiate(template, vars, "wlang/active-text")
|
72
|
+
end
|
73
|
+
|
74
|
+
rescue LoadError
|
75
|
+
task :debug_mail do
|
76
|
+
abort "wlang is not available. Try 'gem install wlang'"
|
77
|
+
end
|
78
|
+
end
|
data/tasks/gem.rake
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Installs rake tasks for gemming and packaging
|
2
|
+
#
|
3
|
+
# This file installs the 'rake package', 'rake gem' tasks and associates
|
4
|
+
# (clobber_package, repackage, ...). It is automatically generated by Noe
|
5
|
+
# from your .noespec file, and should therefore be configured there, under
|
6
|
+
# the variables/rake_tasks/gem entry, as illustrated below:
|
7
|
+
#
|
8
|
+
# variables:
|
9
|
+
# rake_tasks:
|
10
|
+
# gem:
|
11
|
+
# package_dir: pkg
|
12
|
+
# need_tar: false
|
13
|
+
# need_tar_gz: false
|
14
|
+
# need_tar_bz2: false
|
15
|
+
# need_zip: false
|
16
|
+
# ...
|
17
|
+
#
|
18
|
+
# If you have specific needs requiring manual intervention on this file,
|
19
|
+
# don't forget to set safe-override to false in your noe specification:
|
20
|
+
#
|
21
|
+
# template-info:
|
22
|
+
# manifest:
|
23
|
+
# tasks/gem.rake:
|
24
|
+
# safe-override: false
|
25
|
+
#
|
26
|
+
begin
|
27
|
+
require 'rubygems/package_task'
|
28
|
+
Gem::PackageTask.new($gemspec) do |t|
|
29
|
+
|
30
|
+
# Name of the package
|
31
|
+
t.name = $gemspec.name
|
32
|
+
|
33
|
+
# Version of the package
|
34
|
+
t.version = $gemspec.version
|
35
|
+
|
36
|
+
# Directory used to store the package files
|
37
|
+
t.package_dir = "pkg"
|
38
|
+
|
39
|
+
# True if a gzipped tar file (tgz) should be produced
|
40
|
+
t.need_tar = false
|
41
|
+
|
42
|
+
# True if a gzipped tar file (tar.gz) should be produced
|
43
|
+
t.need_tar_gz = false
|
44
|
+
|
45
|
+
# True if a bzip2'd tar file (tar.bz2) should be produced
|
46
|
+
t.need_tar_bz2 = false
|
47
|
+
|
48
|
+
# True if a zip file should be produced (default is false)
|
49
|
+
t.need_zip = false
|
50
|
+
|
51
|
+
# List of files to be included in the package.
|
52
|
+
t.package_files = $gemspec.files
|
53
|
+
|
54
|
+
# Tar command for gzipped or bzip2ed archives.
|
55
|
+
t.tar_command = "tar"
|
56
|
+
|
57
|
+
# Zip command for zipped archives.
|
58
|
+
t.zip_command = "zip"
|
59
|
+
|
60
|
+
end
|
61
|
+
rescue LoadError
|
62
|
+
task :gem do
|
63
|
+
abort 'rubygems/package_task is not available. You should verify your rubygems installation'
|
64
|
+
end
|
65
|
+
task :package do
|
66
|
+
abort 'rubygems/package_task is not available. You should verify your rubygems installation'
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Installs a rake task for for running examples written using rspec.
|
2
|
+
#
|
3
|
+
# This file installs the 'rake spec_test' (aliased as 'rake spec') as well as
|
4
|
+
# extends 'rake test' to run spec tests, if any. It is automatically generated
|
5
|
+
# by Noe from your .noespec file, and should therefore be configured there,
|
6
|
+
# under the variables/rake_tasks/spec_test entry, as illustrated below:
|
7
|
+
#
|
8
|
+
# variables:
|
9
|
+
# rake_tasks:
|
10
|
+
# spec_test:
|
11
|
+
# pattern: spec/**/*_spec.rb
|
12
|
+
# verbose: true
|
13
|
+
# rspec_opts: [--color, --backtrace]
|
14
|
+
# ...
|
15
|
+
#
|
16
|
+
# If you have specific needs requiring manual intervention on this file,
|
17
|
+
# don't forget to set safe-override to false in your noe specification:
|
18
|
+
#
|
19
|
+
# template-info:
|
20
|
+
# manifest:
|
21
|
+
# tasks/spec_test.rake:
|
22
|
+
# safe-override: false
|
23
|
+
#
|
24
|
+
# This file has been written to conform to RSpec v2.4.0. More information about
|
25
|
+
# rspec and options of the rake task defined below can be found on
|
26
|
+
# http://relishapp.com/rspec
|
27
|
+
#
|
28
|
+
begin
|
29
|
+
require "rspec/core/rake_task"
|
30
|
+
desc "Run RSpec code examples"
|
31
|
+
RSpec::Core::RakeTask.new(:spec_test) do |t|
|
32
|
+
# Glob pattern to match files.
|
33
|
+
t.pattern = "spec/**/*_spec.rb"
|
34
|
+
|
35
|
+
# By default, if there is a Gemfile, the generated command will include
|
36
|
+
# 'bundle exec'. Set this to true to ignore the presence of a Gemfile,
|
37
|
+
# and not add 'bundle exec' to the command.
|
38
|
+
t.skip_bundler = false
|
39
|
+
|
40
|
+
# Name of Gemfile to use
|
41
|
+
t.gemfile = "Gemfile"
|
42
|
+
|
43
|
+
# Whether or not to fail Rake when an error occurs (typically when
|
44
|
+
# examples fail).
|
45
|
+
t.fail_on_error = true
|
46
|
+
|
47
|
+
# A message to print to stderr when there are failures.
|
48
|
+
t.failure_message = nil
|
49
|
+
|
50
|
+
# Use verbose output. If this is set to true, the task will print the
|
51
|
+
# executed spec command to stdout.
|
52
|
+
t.verbose = true
|
53
|
+
|
54
|
+
# Use rcov for code coverage?
|
55
|
+
t.rcov = false
|
56
|
+
|
57
|
+
# Path to rcov.
|
58
|
+
t.rcov_path = "rcov"
|
59
|
+
|
60
|
+
# Command line options to pass to rcov. See 'rcov --help' about this
|
61
|
+
t.rcov_opts = []
|
62
|
+
|
63
|
+
# Command line options to pass to ruby. See 'ruby --help' about this
|
64
|
+
t.ruby_opts = []
|
65
|
+
|
66
|
+
# Path to rspec
|
67
|
+
t.rspec_path = "rspec"
|
68
|
+
|
69
|
+
# Command line options to pass to rspec. See 'rspec --help' about this
|
70
|
+
t.rspec_opts = ["--color", "--backtrace"]
|
71
|
+
end
|
72
|
+
rescue LoadError => ex
|
73
|
+
task :spec_test do
|
74
|
+
abort 'rspec is not available. In order to run spec, you must: gem install rspec'
|
75
|
+
end
|
76
|
+
ensure
|
77
|
+
task :spec => [:spec_test]
|
78
|
+
task :test => [:spec_test]
|
79
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Installs a rake task for for running unit tests.
|
2
|
+
#
|
3
|
+
# This file installs the 'rake unit_test' and extends 'rake test' to run unit
|
4
|
+
# tests, if any. It is automatically generated by Noe from your .noespec file,
|
5
|
+
# and should therefore be configured there, under the variables/rake_tasks/unit_test
|
6
|
+
# entry, as illustrated below:
|
7
|
+
#
|
8
|
+
# variables:
|
9
|
+
# rake_tasks:
|
10
|
+
# unit_test:
|
11
|
+
# pattern: test/test*.rb
|
12
|
+
# verbose: false
|
13
|
+
# warning: false
|
14
|
+
# ...
|
15
|
+
#
|
16
|
+
# If you have specific needs requiring manual intervention on this file,
|
17
|
+
# don't forget to set safe-override to false in your noe specification:
|
18
|
+
#
|
19
|
+
# template-info:
|
20
|
+
# manifest:
|
21
|
+
# tasks/unit_test.rake:
|
22
|
+
# safe-override: false
|
23
|
+
#
|
24
|
+
# More info about the TestTask and its options can be found on
|
25
|
+
# http://rake.rubyforge.org/classes/Rake/TestTask.html
|
26
|
+
#
|
27
|
+
begin
|
28
|
+
require 'rake/testtask'
|
29
|
+
desc "Run unit tests"
|
30
|
+
Rake::TestTask.new(:unit_test) do |t|
|
31
|
+
|
32
|
+
# List of directories to added to $LOAD_PATH before running the
|
33
|
+
# tests. (default is 'lib')
|
34
|
+
t.libs = ["lib"]
|
35
|
+
|
36
|
+
# True if verbose test output desired. (default is false)
|
37
|
+
t.verbose = false
|
38
|
+
|
39
|
+
# Test options passed to the test suite. An explicit TESTOPTS=opts
|
40
|
+
# on the command line will override this. (default is NONE)
|
41
|
+
t.options = nil
|
42
|
+
|
43
|
+
# Request that the tests be run with the warning flag set.
|
44
|
+
# E.g. warning=true implies "ruby -w" used to run the tests.
|
45
|
+
t.warning = false
|
46
|
+
|
47
|
+
# Glob pattern to match test files. (default is 'test/test*.rb')
|
48
|
+
t.pattern = "test/test*.rb"
|
49
|
+
|
50
|
+
# Style of test loader to use. Options are:
|
51
|
+
#
|
52
|
+
# * :rake -- Rake provided test loading script (default).
|
53
|
+
# * :testrb -- Ruby provided test loading script.
|
54
|
+
# * :direct -- Load tests using command line loader.
|
55
|
+
#
|
56
|
+
t.loader = :rake
|
57
|
+
|
58
|
+
# Array of commandline options to pass to ruby when running test
|
59
|
+
# loader.
|
60
|
+
t.ruby_opts = []
|
61
|
+
|
62
|
+
# Explicitly define the list of test files to be included in a
|
63
|
+
# test. +list+ is expected to be an array of file names (a
|
64
|
+
# FileList is acceptable). If both +pattern+ and +test_files+ are
|
65
|
+
# used, then the list of test files is the union of the two.
|
66
|
+
t.test_files = nil
|
67
|
+
|
68
|
+
end
|
69
|
+
rescue LoadError => ex
|
70
|
+
task :unit_test do
|
71
|
+
abort 'rspec is not available. In order to run spec, you must: gem install rspec'
|
72
|
+
end
|
73
|
+
ensure
|
74
|
+
desc "Run all tests"
|
75
|
+
task :test => [:unit_test]
|
76
|
+
end
|
77
|
+
|
data/tasks/yard.rake
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Installs a rake task to generate API documentation using yard.
|
2
|
+
#
|
3
|
+
# This file installs the 'rake yard' task. It is automatically generated by Noe from
|
4
|
+
# your .noespec file, and should therefore be configured there, under the
|
5
|
+
# variables/rake_tasks/yard entry, as illustrated below:
|
6
|
+
#
|
7
|
+
# variables:
|
8
|
+
# rake_tasks:
|
9
|
+
# yard:
|
10
|
+
# files: lib/**/*.rb
|
11
|
+
# options: []
|
12
|
+
# ...
|
13
|
+
#
|
14
|
+
# If you have specific needs requiring manual intervention on this file,
|
15
|
+
# don't forget to set safe-override to false in your noe specification:
|
16
|
+
#
|
17
|
+
# template-info:
|
18
|
+
# manifest:
|
19
|
+
# tasks/yard.rake:
|
20
|
+
# safe-override: false
|
21
|
+
#
|
22
|
+
# This file has been written to conform to yard v0.6.4. More information about
|
23
|
+
# yard and the rake task installed below can be found on http://yardoc.org/
|
24
|
+
#
|
25
|
+
begin
|
26
|
+
require "yard"
|
27
|
+
desc "Generate yard documentation"
|
28
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
29
|
+
# Array of options passed to yardoc commandline. See 'yardoc --help' about this
|
30
|
+
t.options = ["--output-dir", "doc/api", "-", "README.md", "CHANGELOG.md", "LICENCE.md"]
|
31
|
+
|
32
|
+
# Array of ruby source files (and any extra documentation files
|
33
|
+
# separated by '-')
|
34
|
+
t.files = ["lib/**/*.rb"]
|
35
|
+
|
36
|
+
# A proc to call before running the task
|
37
|
+
# t.before = proc{ }
|
38
|
+
|
39
|
+
# A proc to call after running the task
|
40
|
+
# r.after = proc{ }
|
41
|
+
|
42
|
+
# An optional lambda to run against all objects being generated.
|
43
|
+
# Any object that the lambda returns false for will be excluded
|
44
|
+
# from documentation.
|
45
|
+
# t.verifier = lambda{|obj| true}
|
46
|
+
end
|
47
|
+
rescue LoadError
|
48
|
+
task :yard do
|
49
|
+
abort 'yard is not available. In order to run yard, you must: gem install yard'
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,491 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'stamina'
|
3
|
+
module Stamina
|
4
|
+
|
5
|
+
# Tests ADL parser
|
6
|
+
class ADLTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
# Tests ADL#parse on a valid dfa
|
9
|
+
def test_can_parse_valid_empty_dfa
|
10
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
11
|
+
1 0
|
12
|
+
0 true false
|
13
|
+
AUTOMATON
|
14
|
+
assert_equal(1, fa.state_count)
|
15
|
+
assert_equal(0, fa.edge_count)
|
16
|
+
assert_equal(true, fa.states[0].initial?)
|
17
|
+
assert_equal(false, fa.states[0].accepting?)
|
18
|
+
assert_equal(true, fa.deterministic?)
|
19
|
+
assert_equal(false, fa.accepts?('+'))
|
20
|
+
assert_equal(false, fa.accepts?('+ a'))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Tests ADL#parse on a valid dfa
|
24
|
+
def test_can_parse_valid_small_dfa
|
25
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
26
|
+
3 4
|
27
|
+
0 true false
|
28
|
+
1 false false
|
29
|
+
2 false true
|
30
|
+
0 1 a
|
31
|
+
1 2 b
|
32
|
+
2 2 a
|
33
|
+
2 1 b
|
34
|
+
AUTOMATON
|
35
|
+
assert_equal(3, fa.state_count)
|
36
|
+
assert_equal(4, fa.edge_count)
|
37
|
+
fa.each_state {|s| assert_equal(s.index==0, s.initial?)}
|
38
|
+
fa.each_state {|s| assert_equal(s.index==2, s.accepting?)}
|
39
|
+
assert_equal(false, fa.accepts?('+'))
|
40
|
+
assert_equal(false, fa.accepts?('+ a'))
|
41
|
+
assert_equal(true, fa.accepts?('+ a b'))
|
42
|
+
assert_equal(true, fa.accepts?('+ a b a'))
|
43
|
+
assert_equal(false, fa.accepts?('+ a b a b'))
|
44
|
+
assert_equal(true, fa.accepts?('+ a b a a a'))
|
45
|
+
assert_equal(true, fa.accepts?('+ a b a a a b b a'))
|
46
|
+
assert_equal(true, fa.accepts?('+ a b a a a b b a a a'))
|
47
|
+
assert_equal(true, fa.accepts?('+ a b a a a b b a a a b b a'))
|
48
|
+
end
|
49
|
+
|
50
|
+
# Tests that ADL#parse detects a missing state
|
51
|
+
def test_detect_missing_header
|
52
|
+
assert_raise(ADL::ParseError) do
|
53
|
+
ADL::parse_automaton <<-AUTOMATON
|
54
|
+
0 true false
|
55
|
+
1 false false
|
56
|
+
0 1 a
|
57
|
+
1 2 b
|
58
|
+
2 2 a
|
59
|
+
2 1 b
|
60
|
+
AUTOMATON
|
61
|
+
end
|
62
|
+
assert_raise(ADL::ParseError) do
|
63
|
+
ADL::parse_automaton <<-AUTOMATON
|
64
|
+
# 3 4
|
65
|
+
0 true false
|
66
|
+
1 false false
|
67
|
+
0 1 a
|
68
|
+
1 2 b
|
69
|
+
2 2 a
|
70
|
+
2 1 b
|
71
|
+
AUTOMATON
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Tests that ADL#parse detects a missing state
|
76
|
+
def test_detect_missing_state
|
77
|
+
assert_raise(ADL::ParseError) do
|
78
|
+
ADL::parse_automaton <<-AUTOMATON
|
79
|
+
3 4
|
80
|
+
0 true false
|
81
|
+
1 false false
|
82
|
+
AUTOMATON
|
83
|
+
end
|
84
|
+
assert_raise(ADL::ParseError) do
|
85
|
+
ADL::parse_automaton <<-AUTOMATON
|
86
|
+
3 4
|
87
|
+
0 true false
|
88
|
+
1 false false
|
89
|
+
0 1 a
|
90
|
+
1 2 b
|
91
|
+
2 2 a
|
92
|
+
2 1 b
|
93
|
+
AUTOMATON
|
94
|
+
end
|
95
|
+
assert_raise(ADL::ParseError) do
|
96
|
+
ADL::parse_automaton <<-AUTOMATON
|
97
|
+
3 4
|
98
|
+
0 true false
|
99
|
+
1 false false
|
100
|
+
# 2 false true
|
101
|
+
0 1 a
|
102
|
+
1 2 b
|
103
|
+
2 2 a
|
104
|
+
2 1 b
|
105
|
+
AUTOMATON
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Tests that ADL#parse detects a missing edge
|
110
|
+
def test_detect_missing_edge
|
111
|
+
assert_raise(ADL::ParseError) do
|
112
|
+
ADL::parse_automaton <<-AUTOMATON
|
113
|
+
3 4
|
114
|
+
0 true false
|
115
|
+
1 false false
|
116
|
+
2 false true
|
117
|
+
0 1 a
|
118
|
+
2 2 a
|
119
|
+
2 1 b
|
120
|
+
AUTOMATON
|
121
|
+
end
|
122
|
+
assert_raise(ADL::ParseError) do
|
123
|
+
ADL::parse_automaton <<-AUTOMATON
|
124
|
+
3 4
|
125
|
+
0 true false
|
126
|
+
1 false false
|
127
|
+
2 false true
|
128
|
+
0 1 a
|
129
|
+
1 2 b
|
130
|
+
2 2 a
|
131
|
+
# 2 1 b
|
132
|
+
AUTOMATON
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Tests that ADL#parse detects a missing edge
|
137
|
+
def test_detect_trailing_data
|
138
|
+
assert_raise(ADL::ParseError) do
|
139
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
140
|
+
1 0
|
141
|
+
0 true false
|
142
|
+
trailing here
|
143
|
+
AUTOMATON
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Tests that ADL#parse detects a missing edge
|
148
|
+
def test_allows_comments_and_white_lines
|
149
|
+
fa = nil
|
150
|
+
assert_nothing_raised(ADL::ParseError) do
|
151
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
152
|
+
|
153
|
+
# a header is always allowed,
|
154
|
+
# with empty lines as well
|
155
|
+
#
|
156
|
+
3 4
|
157
|
+
|
158
|
+
# state definitions may be introduced...
|
159
|
+
0 true false
|
160
|
+
1 false false
|
161
|
+
# and perturbated
|
162
|
+
2 false true
|
163
|
+
0 1 a
|
164
|
+
|
165
|
+
# edge introduction may be misplaced
|
166
|
+
1 2 b
|
167
|
+
2 2 a
|
168
|
+
|
169
|
+
2 1 b
|
170
|
+
|
171
|
+
# and end of file may contain documentation as well
|
172
|
+
# as empty lines:
|
173
|
+
|
174
|
+
AUTOMATON
|
175
|
+
end
|
176
|
+
assert_equal(3, fa.state_count)
|
177
|
+
assert_equal(4, fa.edge_count)
|
178
|
+
fa.each_state {|s| assert_equal(s.index==0, s.initial?)}
|
179
|
+
fa.each_state {|s| assert_equal(s.index==2, s.accepting?)}
|
180
|
+
assert_equal(false, fa.accepts?('+'))
|
181
|
+
assert_equal(false, fa.accepts?('+ a'))
|
182
|
+
assert_equal(true, fa.accepts?('+ a b'))
|
183
|
+
assert_equal(true, fa.accepts?('+ a b a'))
|
184
|
+
assert_equal(false, fa.accepts?('+ a b a b'))
|
185
|
+
assert_equal(true, fa.accepts?('+ a b a a a'))
|
186
|
+
assert_equal(true, fa.accepts?('+ a b a a a b b a'))
|
187
|
+
assert_equal(true, fa.accepts?('+ a b a a a b b a a a'))
|
188
|
+
assert_equal(true, fa.accepts?('+ a b a a a b b a a a b b a'))
|
189
|
+
end
|
190
|
+
|
191
|
+
# Tests ADL::parse on the documentation example
|
192
|
+
def test_valid_adl_automaton_example
|
193
|
+
fa = nil
|
194
|
+
assert_nothing_raised(ADL::ParseError) do
|
195
|
+
here = File.dirname(__FILE__)
|
196
|
+
automaton_adl = File.join(here, '..', '..', 'example', 'adl', 'automaton.adl')
|
197
|
+
fa = ADL::parse_automaton_file(automaton_adl)
|
198
|
+
end # assert_nothing_raised
|
199
|
+
assert_equal(5, fa.state_count)
|
200
|
+
assert_equal(6, fa.edge_count)
|
201
|
+
assert_equal(true, fa.parses?('? hello w o r l d'))
|
202
|
+
assert_equal(false, fa.accepts?('? hello w o r l d'))
|
203
|
+
assert_equal(true, fa.rejects?('? hello w o r l d'))
|
204
|
+
assert_equal(true, fa.accepts?('? hello'))
|
205
|
+
assert_equal(false, fa.accepts?('? hello w'))
|
206
|
+
assert_equal(true, fa.accepts?('? hello w o'))
|
207
|
+
assert_equal(false, fa.accepts?('? hello w o r'))
|
208
|
+
assert_equal(false, fa.accepts?('? hello w o r l'))
|
209
|
+
end
|
210
|
+
|
211
|
+
# Tests ADL::parse on the documentation succint example
|
212
|
+
def test_valid_adl_automaton_succint_example
|
213
|
+
fa = nil
|
214
|
+
assert_nothing_raised do
|
215
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
216
|
+
# Some header comments: tool which has generated this automaton,
|
217
|
+
# maybe a date or other tool options ...
|
218
|
+
# here: 'this automaton accepts the a(ba)* regular language'
|
219
|
+
2 2
|
220
|
+
0 true false
|
221
|
+
1 false true
|
222
|
+
0 1 a
|
223
|
+
1 0 b
|
224
|
+
AUTOMATON
|
225
|
+
end
|
226
|
+
assert_equal(2, fa.state_count)
|
227
|
+
assert_equal(2, fa.edge_count)
|
228
|
+
assert_equal(true, fa.accepts?('? a'))
|
229
|
+
assert_equal(true, fa.accepts?('? a b a'))
|
230
|
+
assert_equal(true, fa.accepts?('? a b a b a'))
|
231
|
+
assert_equal(false, fa.accepts?('?'))
|
232
|
+
assert_equal(false, fa.accepts?('? a b'))
|
233
|
+
assert_equal(false, fa.accepts?('? a b a b'))
|
234
|
+
end
|
235
|
+
|
236
|
+
# Checks that an initial state may arruve lately
|
237
|
+
def test_parse_automaton_allows_late_initial_state
|
238
|
+
fa = nil
|
239
|
+
assert_nothing_raised do
|
240
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
241
|
+
# Some header comments: tool which has generated this automaton,
|
242
|
+
# maybe a date or other tool options ...
|
243
|
+
# here: 'this automaton accepts the a(ba)* regular language'
|
244
|
+
2 2
|
245
|
+
0 false false
|
246
|
+
1 true true
|
247
|
+
0 1 a
|
248
|
+
1 0 b
|
249
|
+
AUTOMATON
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Tests parse_automaton on an automated randomly generated using jail
|
254
|
+
def test_parse_automaton_on_jail_randdfa
|
255
|
+
fa = nil
|
256
|
+
assert_nothing_raised do
|
257
|
+
fa = ADL::parse_automaton_file(File.join(File.dirname(__FILE__),'randdfa.adl'))
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Tests an important security issue about parse_automaton
|
262
|
+
def test_parse_automaton_does_not_executes_ruby_code
|
263
|
+
begin
|
264
|
+
assert_raise ADL::ParseError do
|
265
|
+
ADL::parse_automaton <<-AUTOMATON
|
266
|
+
Kernel.exit(-1)
|
267
|
+
AUTOMATON
|
268
|
+
end
|
269
|
+
rescue SystemExit
|
270
|
+
assert false, 'SECURITY issue: ADL::parse_automaton executes ruby code'
|
271
|
+
end
|
272
|
+
begin
|
273
|
+
assert_raise ADL::ParseError do
|
274
|
+
ADL::parse_automaton_file(File.dirname(__FILE__)+'/exit.rb')
|
275
|
+
end
|
276
|
+
rescue SystemExit
|
277
|
+
assert false, 'SECURITY issue: ADL::parse_automaton executes ruby code'
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Tests ADL::parse_string
|
282
|
+
def test_parse_string
|
283
|
+
s = ADL::parse_string('?')
|
284
|
+
assert_equal(true, InputString===s)
|
285
|
+
assert_equal(false, s.positive?)
|
286
|
+
assert_equal(false, s.negative?)
|
287
|
+
assert_equal(true, s.unlabeled?)
|
288
|
+
assert_equal(nil, s.label)
|
289
|
+
assert_equal(true, s.empty?)
|
290
|
+
assert_equal([], s.symbols)
|
291
|
+
|
292
|
+
s = ADL::parse_string('+')
|
293
|
+
assert_equal(true, InputString===s)
|
294
|
+
assert_equal(true, s.positive?)
|
295
|
+
assert_equal(true, s.label)
|
296
|
+
assert_equal(true, s.empty?)
|
297
|
+
assert_equal([], s.symbols)
|
298
|
+
|
299
|
+
s = ADL::parse_string('-')
|
300
|
+
assert_equal(true, InputString===s)
|
301
|
+
assert_equal(false, s.positive?)
|
302
|
+
assert_equal(false, s.label)
|
303
|
+
assert_equal(true, s.empty?)
|
304
|
+
assert_equal([], s.symbols)
|
305
|
+
|
306
|
+
s = ADL::parse_string('? a')
|
307
|
+
assert_equal(['a'], s.symbols)
|
308
|
+
assert_equal(false, s.positive?)
|
309
|
+
assert_equal(false, s.negative?)
|
310
|
+
assert_equal(true, s.unlabeled?)
|
311
|
+
assert_equal(nil, s.label)
|
312
|
+
assert_equal(['a'], s.symbols)
|
313
|
+
|
314
|
+
s = ADL::parse_string('+ a')
|
315
|
+
assert_equal(['a'], s.symbols)
|
316
|
+
assert_equal(true, s.positive?)
|
317
|
+
|
318
|
+
s = ADL::parse_string('- a')
|
319
|
+
assert_equal(['a'], s.symbols)
|
320
|
+
assert_equal(false, s.positive?)
|
321
|
+
|
322
|
+
s = ADL::parse_string('+ a b a b ')
|
323
|
+
assert_equal(['a','b','a','b'], s.symbols)
|
324
|
+
assert_equal(true, s.positive?)
|
325
|
+
|
326
|
+
s = ADL::parse_string('- a b a c')
|
327
|
+
assert_equal(['a','b','a','c'], s.symbols)
|
328
|
+
assert_equal(false, s.positive?)
|
329
|
+
end
|
330
|
+
|
331
|
+
# Tests ADL::parse_sample
|
332
|
+
def test_parse_sample
|
333
|
+
sample = ADL::parse_sample <<-SAMPLE
|
334
|
+
+ a b a b a b
|
335
|
+
# this is a comment, next is an empty line
|
336
|
+
+
|
337
|
+
+ a b
|
338
|
+
- a a
|
339
|
+
? a b
|
340
|
+
# trailing comment allowed
|
341
|
+
SAMPLE
|
342
|
+
assert sample==Sample['+ a b a b a b', '+ a b', '- a a', '+', '? a b']
|
343
|
+
end
|
344
|
+
|
345
|
+
# Tests that ADL::parse_sample accepts the empty sample
|
346
|
+
def test_parse_sample_accepts_empty_sample
|
347
|
+
samples = [
|
348
|
+
ADL::parse_sample(""),
|
349
|
+
ADL::parse_sample("#"),
|
350
|
+
ADL::parse_sample(<<-SAMPLE
|
351
|
+
SAMPLE
|
352
|
+
),
|
353
|
+
ADL::parse_sample(<<-SAMPLE
|
354
|
+
|
355
|
+
# this is a comment, between two empty lines
|
356
|
+
|
357
|
+
SAMPLE
|
358
|
+
)
|
359
|
+
]
|
360
|
+
samples.each do |sample|
|
361
|
+
assert sample==Sample.new
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# Tests that ADL::parse_sample accepts empty strings
|
366
|
+
def test_parse_sample_accepts_empty_strings
|
367
|
+
assert Sample['+'] == ADL::parse_sample('+')
|
368
|
+
assert Sample['-'] == ADL::parse_sample('-')
|
369
|
+
assert Sample['+'] == ADL::parse_sample(<<-SAMPLE
|
370
|
+
+
|
371
|
+
SAMPLE
|
372
|
+
)
|
373
|
+
assert Sample['-'] == ADL::parse_sample(<<-SAMPLE
|
374
|
+
-
|
375
|
+
SAMPLE
|
376
|
+
)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Tests validity of sample.adl file
|
380
|
+
def test_valid_adl_sample_example
|
381
|
+
here = File.dirname(__FILE__)
|
382
|
+
sample_adl = File.join(here, '..', '..', 'example', 'adl', 'sample.adl')
|
383
|
+
sample = ADL::parse_sample_file(sample_adl)
|
384
|
+
expected = Sample.new
|
385
|
+
expected << InputString.new(['a', 'b', 'a', 'b'], true)
|
386
|
+
expected << InputString.new(['a', 'a'], false)
|
387
|
+
expected << InputString.new(['a', 'b'], nil)
|
388
|
+
expected << InputString.new([], true)
|
389
|
+
expected << InputString.new(['hello', 'world'], true)
|
390
|
+
expected << InputString.new(['h','e','l','l','o','w','o','r','l','d'], true)
|
391
|
+
expected << InputString.new(['helloworld'], true)
|
392
|
+
expected << InputString.new(['a','+','b','-','a','-','b','+a'], true)
|
393
|
+
expected << InputString.new(['#','a','#','b','a','b','#','and','all','these','words','are','symbols', 'too', '!!'],true)
|
394
|
+
expected.each do |s|
|
395
|
+
assert sample.include?(s), "|#{s}| from expected is included in sample"
|
396
|
+
end
|
397
|
+
sample.each do |s|
|
398
|
+
assert expected.include?(s), "|#{s}| from sample is included in expected"
|
399
|
+
end
|
400
|
+
assert expected == sample
|
401
|
+
end
|
402
|
+
|
403
|
+
# Tests validity of sample.adl file
|
404
|
+
def test_valid_adl_sample_succint_example
|
405
|
+
sample = ADL::parse_sample <<-SAMPLE
|
406
|
+
# Some header comments: tool which has generated this sample,
|
407
|
+
# maybe a date or other tool options ...
|
408
|
+
# here: 'this sample is caracteristic for the a(ba)* regular language'
|
409
|
+
-
|
410
|
+
+ a
|
411
|
+
- a b
|
412
|
+
+ a b a
|
413
|
+
SAMPLE
|
414
|
+
expected = Sample.new
|
415
|
+
expected << InputString.new([], false)
|
416
|
+
expected << InputString.new(['a'], true)
|
417
|
+
expected << InputString.new(['a','b'], false)
|
418
|
+
expected << InputString.new(['a','b','a'], true)
|
419
|
+
assert expected==sample
|
420
|
+
end
|
421
|
+
|
422
|
+
# Tests an important security issue about parse_automaton
|
423
|
+
def test_parse_sample_does_not_executes_ruby_code
|
424
|
+
begin
|
425
|
+
ADL::parse_sample <<-AUTOMATON
|
426
|
+
+ Kernel.exit(-1)
|
427
|
+
AUTOMATON
|
428
|
+
rescue SystemExit
|
429
|
+
assert false, 'SECURITY issue: ADL::parse_automaton executes ruby code'
|
430
|
+
end
|
431
|
+
begin
|
432
|
+
ADL::parse_sample_file(File.dirname(__FILE__)+'/exit.rb')
|
433
|
+
rescue SystemExit
|
434
|
+
assert false, 'SECURITY issue: ADL::parse_automaton executes ruby code'
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# tests that state IDs are loaded and can be used.
|
439
|
+
def test_state_names
|
440
|
+
fa = ADL::parse_automaton <<-AUTOMATON
|
441
|
+
3 4
|
442
|
+
A true false
|
443
|
+
B false false
|
444
|
+
C false true
|
445
|
+
A B a
|
446
|
+
B C b
|
447
|
+
C C a
|
448
|
+
C B b
|
449
|
+
AUTOMATON
|
450
|
+
|
451
|
+
['A','B','C'].each do |statename|
|
452
|
+
assert_equal statename,fa.get_state(statename)[:name]
|
453
|
+
end
|
454
|
+
|
455
|
+
assert_equal true,fa.get_state('A').initial?
|
456
|
+
assert_equal false,fa.get_state('B').initial?
|
457
|
+
assert_equal false,fa.get_state('C').initial?
|
458
|
+
|
459
|
+
assert_equal false,fa.get_state('A').accepting?
|
460
|
+
assert_equal false,fa.get_state('B').accepting?
|
461
|
+
assert_equal true,fa.get_state('C').accepting?
|
462
|
+
end
|
463
|
+
|
464
|
+
def test_parsing_recognizes_failures
|
465
|
+
assert_raise Stamina::ADL::ParseError do
|
466
|
+
fa = ADL::parse_sample <<-EOF
|
467
|
+
3 4
|
468
|
+
A true false
|
469
|
+
B false false
|
470
|
+
C false true
|
471
|
+
A B a
|
472
|
+
B C b
|
473
|
+
C C a
|
474
|
+
C B b
|
475
|
+
EOF
|
476
|
+
end
|
477
|
+
assert_raise Stamina::ADL::ParseError do
|
478
|
+
sample = ADL::parse_automaton <<-EOF
|
479
|
+
+ a b a b a b
|
480
|
+
# this is a comment, next is an empty line
|
481
|
+
+
|
482
|
+
+ a b
|
483
|
+
- a a
|
484
|
+
a b
|
485
|
+
# trailing comment allowed
|
486
|
+
EOF
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end # class ADLTest
|
490
|
+
|
491
|
+
end # module Stamina
|