rubyqc 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9ff877141825f4a8516b956d216de5dcae3a9ae9
4
+ data.tar.gz: 0284ea73671c159c38a5a8c6249e477159666c1f
5
+ SHA512:
6
+ metadata.gz: a0e49da081b1af4fc05e43be982395ba6fe0521013589c0e244b148873ab2ba7eb7ca3fecf9515ff6454f75c27b5ef9c14998df181c40659830bf8f0b2979273
7
+ data.tar.gz: c4427a5669c4c7d9c7085e562c6d4f1d1aa4ef833513ac893f9fbd9d3efdb7beee07bc5da82de126252c39bdd75b593ce2875c30d8dcad1ab9f2ad181cd37290
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /pkg/
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "task"]
2
+ path = task
3
+ url = git://github.com/godfat/gemgem.git
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ before_install: 'git submodule update --init'
2
+ script: 'ruby -r bundler/setup -S rake test'
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0
8
+ - rbx
9
+ - jruby
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'bacon'
8
+
9
+ platform :rbx do
10
+ gem 'rubysl-singleton' # used in rake
11
+ end
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # RubyQC [![Build Status](https://secure.travis-ci.org/godfat/rubyqc.png?branch=master)](http://travis-ci.org/godfat/rubyqc)
2
+
3
+ by Lin Jen-Shin ([godfat](http://godfat.org))
4
+
5
+ ## LINKS:
6
+
7
+ * [github](https://github.com/godfat/rubyqc)
8
+ * [rubygems](https://rubygems.org/gems/rubyqc)
9
+ * [rdoc](http://rdoc.info/github/godfat/rubyqc)
10
+
11
+ ## DESCRIPTION:
12
+
13
+ RubyQC -- A conceptual [QuickCheck][] library for Ruby.
14
+
15
+ It's not a faithful port since Hsakell is totally different than Ruby.
16
+ However it's still benefit to use some of the ideas behind QuickCheck,
17
+ and we could also use RubyQC for generating arbitrary objects.
18
+
19
+ [QuickCheck]: http://en.wikipedia.org/wiki/QuickCheck
20
+
21
+ ## WHY?
22
+
23
+ How do we make sure our programs work as expected?
24
+ Taking it to the extreme, of course we prove it formally.
25
+ We have [Agda][] for Haskell people, or [Coq][] for OCaml people.
26
+
27
+ However, in most cases we don't really care if they are
28
+ 100% correct. Do we care PRNGs are really random
29
+ in games? Some people might care, we don't. Can we
30
+ prove halting problem? Of course not, but we do need
31
+ termination check at times.
32
+
33
+ For most cases, an army of tests is far good enough.
34
+ Usually we write scenario based tests. We first assume
35
+ things, and then do things, finally verify results. This is
36
+ quite simple, but cannot really cover most of the inputs
37
+ without great effort.
38
+
39
+ QuickCheck took another approach. Instead of writing
40
+ scenario, we think about what properties do our programs,
41
+ or functions have, giving a range of inputs. Suppose
42
+ we want to test the reverse function, instead of testing
43
+ against a fixed set of lists and verify a fixed set of results,
44
+ we think about what properties does reverse have.
45
+
46
+ For example, if we reverse and reverse a list, the result
47
+ should be equal to the original list. With QuickCheck, it
48
+ could then generate arbitrary random lists to the property
49
+ function you just wrote, and verify if the property holds.
50
+ By default, it would generate 100 test cases.
51
+
52
+ This approach would force you think more about the
53
+ precondition and postcondition, eliminating unusual
54
+ corner cases you might never think of, and force you
55
+ think what are the functions we're really writing. We
56
+ could also raise the number of test cases by configuring
57
+ it and raise our level of confidence about correctness.
58
+
59
+ [Agda]: http://en.wikipedia.org/wiki/Agda_%28programming_language%29
60
+ [Coq]: http://en.wikipedia.org/wiki/Coq
61
+
62
+ ## DESIGN:
63
+
64
+ * Testing framework agnostic
65
+ * Therefore RubyQC could be treated as an arbitrary object generator library
66
+ * Think about [combinator][]
67
+ * Self hosted (Test RubyQC with RubyQC!)
68
+
69
+ [combinator]: http://en.wikipedia.org/wiki/Combinator_library
70
+
71
+ ## REQUIREMENTS:
72
+
73
+ * Tested with MRI (official CRuby) 2.0.0, 2.1.0, Rubinius and JRuby.
74
+
75
+ ## INSTALLATION:
76
+
77
+ gem install rubyqc
78
+
79
+ ## SYNOPSIS:
80
+
81
+ Here's a quick example using [Bacon][]. We check if `Array#sort` has the
82
+ property that the front elements of the result array would be `<=` than
83
+ the rear elements of the result array for all arrays.
84
+
85
+ ``` ruby
86
+ require 'bacon'
87
+ require 'rubyqc'
88
+
89
+ Bacon.summary_on_exit
90
+ include RubyQC::API
91
+
92
+ describe Array do
93
+ describe 'sort' do
94
+ should 'Any front elements should be <= any rear elements' do
95
+ check([Fixnum]*100).times(10) do |array|
96
+ array.sort.each_cons(2).each{ |x, y| x.should <= y }
97
+ end
98
+ end
99
+ end
100
+ end
101
+ ```
102
+
103
+ [Bacon]: https://github.com/chneukirchen/bacon
104
+
105
+ Basically, `RubyQC::API.check` would merely take the arguments and
106
+ generate the instances via `rubyqc` method. Here the generated array
107
+ could be viewed as `([Fixnum]*100).rubyqc`, meaning that we want an
108
+ array which contains 100 random instances of Fixnum.
109
+
110
+ As you can see, here actually `rubyqc` is an instance method of Array,
111
+ and it would recursively call `rubyqc` for all elements of the array,
112
+ and collect the results. Here's the definition of `Array#rubyqc`:
113
+
114
+ ``` ruby
115
+ class Array
116
+ def rubyqc
117
+ map(&:rubyqc)
118
+ end
119
+ end
120
+ ```
121
+
122
+ And `Fixnum.rubyqc` is a Fixnum's singleton method which is defined as
123
+ follows:
124
+
125
+ ``` ruby
126
+ class Fixnum
127
+ def self.rubyqc
128
+ rand(RubyQC::FixnumMin..RubyQC::FixnumMax)
129
+ end
130
+ end
131
+ ```
132
+
133
+ You get the idea. See next section for the list of built-in generators.
134
+
135
+ ### Kernel
136
+
137
+ The very default generator would simply return the instance itself.
138
+ So if there's no generator defined for a given class or instance, it
139
+ would merely take `self`.
140
+
141
+ ``` ruby
142
+ true.rubyqc # true
143
+ ```
144
+
145
+ ### Class
146
+
147
+ This default generator for classes would simply return a new instance via
148
+ `new` method. This could fail if the `initialize` method for the particular
149
+ class does not take zero argument.
150
+
151
+ ``` ruby
152
+ Object.rubyqc # kind_of?(Object)
153
+ ```
154
+
155
+ ### Fixnum, Bignum, and Integer
156
+
157
+ This would give you a random integer. Fixnum and Bignum would guarantee to
158
+ give you the particular class, whereas Integer would give you either a Fixnum
159
+ or Bignum.
160
+
161
+ ``` ruby
162
+ Fixnum.rubyqc # kind_of?(Fixnum)
163
+ ```
164
+
165
+ ### array
166
+
167
+ We also have instance level generator, which was used in the first example.
168
+ The array instance generator would recursively call `rubyqc` for all elements
169
+ of the array, and collect the results.
170
+
171
+ ``` ruby
172
+ [Fixnum, Fixnum].rubyqc # [kind_of?(Fixnum), kind_of?(Fixnum)]
173
+ ```
174
+
175
+ ### hash
176
+
177
+ This also applies to hashes which would do the same thing as arrays for the
178
+ values, keeping the key.
179
+
180
+ ``` ruby
181
+ {:fixnum => Fixnum}.rubyqc # {:fixnum => kind_of?(Fixnum)}
182
+ ```
183
+
184
+ ### range
185
+
186
+ Fixnum would actually give a very large or very small (negative) number in
187
+ most cases. If you want to have a number with specific range, use a range
188
+ object to specific the range.
189
+
190
+ ``` ruby
191
+ (1..6).rubyqc # within?(1..6)
192
+ ```
193
+
194
+ Granted that this is actually the same as using `rand(1..6)`, but for
195
+ combinators we need to have a unified interface.
196
+
197
+ ### Define your own generator
198
+
199
+ Just define `rubyqc` method for your classes or instances. This weird name
200
+ was simply chosen to avoid name conflicting since we don't have [typeclass][]
201
+ in Ruby, and it's quite natural to open and insert new methods into classes
202
+ in Ruby.
203
+
204
+ [typeclass]: http://learnyouahaskell.com/types-and-typeclasses
205
+
206
+ ## CONTRIBUTORS:
207
+
208
+ * Lin Jen-Shin (@godfat)
209
+
210
+ ## LICENSE:
211
+
212
+ Apache License 2.0
213
+
214
+ Copyright (c) 2014, Lin Jen-Shin (godfat)
215
+
216
+ Licensed under the Apache License, Version 2.0 (the "License");
217
+ you may not use this file except in compliance with the License.
218
+ You may obtain a copy of the License at
219
+
220
+ <http://www.apache.org/licenses/LICENSE-2.0>
221
+
222
+ Unless required by applicable law or agreed to in writing, software
223
+ distributed under the License is distributed on an "AS IS" BASIS,
224
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
225
+ See the License for the specific language governing permissions and
226
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+
2
+ begin
3
+ require "#{dir = File.dirname(__FILE__)}/task/gemgem"
4
+ rescue LoadError
5
+ sh 'git submodule update --init'
6
+ exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
7
+ end
8
+
9
+ Gemgem.init(dir) do |s|
10
+ require 'rubyqc/version'
11
+ s.name = 'rubyqc'
12
+ s.version = RubyQC::VERSION
13
+ end
data/lib/rubyqc.rb ADDED
@@ -0,0 +1,16 @@
1
+
2
+ require 'rubyqc/prelude'
3
+ require 'rubyqc/modifier'
4
+
5
+ module RubyQC
6
+ module API
7
+ module_function
8
+ def check *args, &block
9
+ RubyQC::Modifier.new(args, &block)
10
+ end
11
+
12
+ def one_of *args
13
+ args.sample
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module RubyQC
3
+ class Modifier
4
+ def initialize args, &block
5
+ @args = args
6
+ @times = 10
7
+ run(&block)
8
+ end
9
+
10
+ def times t, &block
11
+ @times = t
12
+ run(&block)
13
+ end
14
+
15
+ def run
16
+ @times.times{
17
+ yield(*@args.map(&:rubyqc))
18
+ } if block_given?
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,263 @@
1
+
2
+ module RubyQC
3
+ FixnumMax = 2 ** (0.size * 8 - 2) - 1
4
+ FixnumMin = - FixnumMax - 1
5
+ FixnumSuc = FixnumMax + 1
6
+ BignumMax = FixnumMax * 10
7
+ BignumMin = FixnumMin * 10
8
+ end
9
+
10
+ ###
11
+ ### default implementation
12
+ ###
13
+
14
+ module Kernel
15
+ def rubyqc
16
+ self
17
+ end
18
+ end
19
+
20
+ class Class
21
+ def rubyqc
22
+ new
23
+ end
24
+ end
25
+
26
+ ###
27
+ ### class level implementation
28
+ ###
29
+
30
+ class NilClass
31
+ def self.rubyqc
32
+ nil
33
+ end
34
+ end
35
+
36
+ class TrueClass
37
+ def self.rubyqc
38
+ true
39
+ end
40
+ end
41
+
42
+ class FalseClass
43
+ def self.rubyqc
44
+ false
45
+ end
46
+ end
47
+
48
+ class Proc
49
+ def self.rubyqc
50
+ lambda{ Class.rubyqc.rubyqc }
51
+ end
52
+ end
53
+
54
+ class Method
55
+ def self.rubyqc
56
+ obj = Class.rubyqc.rubyqc
57
+ obj.method(obj.methods.sample)
58
+ end
59
+ end
60
+
61
+ class UnboundMethod
62
+ def self.rubyqc
63
+ klass = Class.rubyqc
64
+ klass.instance_method(klass.instance_methods.sample)
65
+ end
66
+ end
67
+
68
+ # TODO
69
+ class Binding
70
+ def self.rubyqc
71
+ binding
72
+ end
73
+ end
74
+
75
+ # TODO
76
+ class Symbol
77
+ def self.rubyqc
78
+ String.rubyqc.to_sym
79
+ end
80
+ end
81
+
82
+ class Encoding
83
+ def self.rubyqc
84
+ list.sample
85
+ end
86
+ end
87
+
88
+ # TODO
89
+ class Dir
90
+ def self.rubyqc
91
+ Dir.open(__dir__)
92
+ end
93
+ end
94
+
95
+ class File
96
+ def self.rubyqc
97
+ File.open(__FILE__)
98
+ end
99
+ end
100
+
101
+ class IO
102
+ def self.rubyqc
103
+ pipe.sample
104
+ end
105
+ end
106
+
107
+ # TODO
108
+ class String
109
+ def self.rubyqc
110
+ /.*/.rubyqc
111
+ end
112
+ end
113
+
114
+ class Regexp
115
+ def self.rubyqc
116
+ new(String.rubyqc)
117
+ end
118
+ end
119
+
120
+ class MatchData
121
+ def self.rubyqc
122
+ String.rubyqc.match(Regexp.rubyqc)
123
+ end
124
+ end
125
+
126
+ class Class
127
+ def self.rubyqc
128
+ Object.constants.map{ |name|
129
+ unless [:Class, :BasicObject, :Data,
130
+ :Config, :RubyVM, :TracePoint].include?(name)
131
+ const_get(name)
132
+ end
133
+ }.select{ |const| const.kind_of?(Class) }.sample
134
+ end
135
+ end
136
+
137
+ # TODO: why?
138
+ # class Object
139
+ # def self.rubyqc
140
+ # Class.rubyqc.rubyqc
141
+ # end
142
+ # end
143
+
144
+ class Fixnum
145
+ def self.rubyqc
146
+ (RubyQC::FixnumMin..RubyQC::FixnumMax).rubyqc
147
+ end
148
+ end
149
+
150
+ class Bignum
151
+ def self.rubyqc
152
+ case (0..1).rubyqc
153
+ when 0
154
+ (RubyQC::BignumMin...RubyQC::FixnumMin).rubyqc
155
+ when 1
156
+ (RubyQC::FixnumSuc...RubyQC::BignumMax).rubyqc
157
+ else
158
+ raise "huh?"
159
+ end
160
+ end
161
+ end
162
+
163
+ class Integer
164
+ def self.rubyqc
165
+ [Bignum, Fixnum].sample.rubyqc
166
+ end
167
+ end
168
+
169
+ class Complex
170
+ def self.rubyqc
171
+ Fixnum.rubyqc + Fixnum.rubyqc.i
172
+ end
173
+ end
174
+
175
+ class Rational
176
+ def self.rubyqc
177
+ Rational(Fixnum.rubyqc, Fixnum.rubyqc)
178
+ end
179
+ end
180
+
181
+ class Range
182
+ def self.rubyqc
183
+ new(*[Fixnum.rubyqc, Fixnum.rubyqc].sort)
184
+ end
185
+ end
186
+
187
+ class Enumerator
188
+ def self.rubyqc
189
+ Array.rubyqc.to_enum
190
+ end
191
+ end
192
+
193
+ # TODO
194
+ class Float
195
+ def self.rubyqc
196
+ rand
197
+ end
198
+ end
199
+
200
+ class Thread::SizedQueue
201
+ def self.rubyqc
202
+ new(rand(0..100))
203
+ end
204
+ end
205
+
206
+ class Fiber
207
+ def self.rubyqc
208
+ new(&Proc.rubyqc)
209
+ end
210
+ end
211
+
212
+ class Thread
213
+ def self.rubyqc
214
+ new(&Proc.rubyqc)
215
+ end
216
+ end
217
+
218
+ class SignalException
219
+ def self.rubyqc
220
+ new(rand(0..32))
221
+ end
222
+ end
223
+
224
+ class SystemCallError
225
+ def self.rubyqc
226
+ new(rand(1..106))
227
+ end
228
+ end
229
+
230
+ class Struct
231
+ def self.rubyqc
232
+ new(Symbol.rubyqc)
233
+ end
234
+ end
235
+
236
+ ###
237
+ ### instance level implementation
238
+ ###
239
+
240
+ class Array
241
+ def rubyqc
242
+ map(&:rubyqc)
243
+ end
244
+ end
245
+
246
+ class Hash
247
+ def rubyqc
248
+ inject({}){ |r, (k, v)| r[k] = v.rubyqc; r }
249
+ end
250
+ end
251
+
252
+ class Range
253
+ def rubyqc
254
+ rand(self.begin..self.end)
255
+ end
256
+ end
257
+
258
+ # TODO
259
+ class Regexp
260
+ def rubyqc
261
+ 'rubyqc...'
262
+ end
263
+ end
@@ -0,0 +1,67 @@
1
+
2
+ require 'bacon'
3
+ require 'rubyqc'
4
+
5
+ Bacon.summary_on_exit
6
+ include RubyQC::API
7
+
8
+ module Kernel
9
+ def eq? rhs
10
+ self == rhs
11
+ end
12
+ end
13
+
14
+ class Should
15
+ def self.rubyqc
16
+ new(Class.rubyqc.rubyqc)
17
+ end
18
+ end
19
+
20
+ def verify_generated generated, spec
21
+ if spec.empty?
22
+ generated.should.eq spec
23
+ else
24
+ case spec
25
+ when Array
26
+ verify_array(generated, spec)
27
+ when Hash
28
+ verify_hash(generated, spec)
29
+ else
30
+ verify_other(generated, spec)
31
+ end
32
+ end
33
+ end
34
+
35
+ def verify_array generated, spec
36
+ generated.zip(spec).each do |(instance, expected)|
37
+ case expected
38
+ when Array
39
+ verify_array(instance, expected)
40
+ when Hash
41
+ verify_hash(instance, expected)
42
+ else
43
+ verify_other(instance, expected)
44
+ end
45
+ end
46
+ end
47
+
48
+ def verify_hash generated, spec
49
+ generated.each do |key, instance|
50
+ case expected = spec[key]
51
+ when Array
52
+ verify_array(instance, expected)
53
+ when Hash
54
+ verify_hash(instance, expected)
55
+ else
56
+ verify_other(instance, expected)
57
+ end
58
+ end
59
+ end
60
+
61
+ def verify_other generated, spec
62
+ if spec.kind_of?(Class)
63
+ generated.should.kind_of spec
64
+ else
65
+ generated.should.eq spec
66
+ end
67
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module RubyQC
3
+ VERSION = '0.0.1'
4
+ end
data/rubyqc.gemspec ADDED
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # stub: rubyqc 0.0.1 ruby lib
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rubyqc"
6
+ s.version = "0.0.1"
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib"]
10
+ s.authors = ["Lin Jen-Shin (godfat)"]
11
+ s.date = "2014-01-30"
12
+ s.description = "RubyQC -- A conceptual [QuickCheck][] library for Ruby.\n\nIt's not a faithful port since Hsakell is totally different than Ruby.\nHowever it's still benefit to use some of the ideas behind QuickCheck,\nand we could also use RubyQC for generating arbitrary objects.\n\n[QuickCheck]: http://en.wikipedia.org/wiki/QuickCheck"
13
+ s.email = ["godfat (XD) godfat.org"]
14
+ s.files = [
15
+ ".gitignore",
16
+ ".gitmodules",
17
+ ".travis.yml",
18
+ "Gemfile",
19
+ "LICENSE",
20
+ "README.md",
21
+ "Rakefile",
22
+ "lib/rubyqc.rb",
23
+ "lib/rubyqc/modifier.rb",
24
+ "lib/rubyqc/prelude.rb",
25
+ "lib/rubyqc/test.rb",
26
+ "lib/rubyqc/version.rb",
27
+ "rubyqc.gemspec",
28
+ "task/README.md",
29
+ "task/gemgem.rb",
30
+ "test/test_api.rb",
31
+ "test/test_kind_of.rb",
32
+ "test/test_readme.rb"]
33
+ s.homepage = "https://github.com/godfat/rubyqc"
34
+ s.licenses = ["Apache License 2.0"]
35
+ s.rubygems_version = "2.2.1"
36
+ s.summary = "RubyQC -- A conceptual [QuickCheck][] library for Ruby."
37
+ s.test_files = [
38
+ "test/test_api.rb",
39
+ "test/test_kind_of.rb",
40
+ "test/test_readme.rb"]
41
+ end
data/task/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Gemgem
2
+
3
+ ## DESCRIPTION:
4
+
5
+ Provided tasks:
6
+
7
+ rake clean # Remove ignored files
8
+ rake gem:build # Build gem
9
+ rake gem:install # Install gem
10
+ rake gem:release # Release gem
11
+ rake gem:spec # Generate gemspec
12
+ rake test # Run tests in memory
13
+
14
+ ## REQUIREMENTS:
15
+
16
+ * Tested with MRI (official CRuby) 1.9.3, 2.0.0, Rubinius and JRuby.
17
+
18
+ ## INSTALLATION:
19
+
20
+ git submodule add git://github.com/godfat/gemgem.git task
21
+
22
+ And in Rakefile:
23
+
24
+ ``` ruby
25
+ begin
26
+ require "#{dir = File.dirname(__FILE__)}/task/gemgem"
27
+ rescue LoadError
28
+ sh 'git submodule update --init'
29
+ exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
30
+ end
31
+
32
+ Gemgem.init(dir) do |s|
33
+ s.name = 'your-gem'
34
+ s.version = '0.1.0'
35
+ end
36
+ ```
37
+
38
+ ## LICENSE:
39
+
40
+ Apache License 2.0
41
+
42
+ Copyright (c) 2011-2013, Lin Jen-Shin (godfat)
43
+
44
+ Licensed under the Apache License, Version 2.0 (the "License");
45
+ you may not use this file except in compliance with the License.
46
+ You may obtain a copy of the License at
47
+
48
+ <http://www.apache.org/licenses/LICENSE-2.0>
49
+
50
+ Unless required by applicable law or agreed to in writing, software
51
+ distributed under the License is distributed on an "AS IS" BASIS,
52
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
53
+ See the License for the specific language governing permissions and
54
+ limitations under the License.
data/task/gemgem.rb ADDED
@@ -0,0 +1,262 @@
1
+
2
+ module Gemgem
3
+ class << self
4
+ attr_accessor :dir, :spec, :spec_create
5
+ end
6
+
7
+ module_function
8
+ def gem_tag ; "#{spec.name}-#{spec.version}" ; end
9
+ def gem_path ; "#{pkg_dir}/#{gem_tag}.gem" ; end
10
+ def spec_path ; "#{dir}/#{spec.name}.gemspec" ; end
11
+ def pkg_dir ; "#{dir}/pkg" ; end
12
+ def escaped_dir; @escaped_dir ||= Regexp.escape(dir); end
13
+
14
+ def init dir, &block
15
+ self.dir = dir
16
+ $LOAD_PATH.unshift("#{dir}/lib")
17
+ ENV['RUBYLIB'] = "#{dir}/lib:#{ENV['RUBYLIB']}"
18
+ ENV['PATH'] = "#{dir}/bin:#{ENV['PATH']}"
19
+ self.spec_create = block
20
+ end
21
+
22
+ def create
23
+ spec = Gem::Specification.new do |s|
24
+ s.authors = ['Lin Jen-Shin (godfat)']
25
+ s.email = ['godfat (XD) godfat.org']
26
+
27
+ s.description = description.join
28
+ s.summary = description.first
29
+ s.license = readme['LICENSE'].sub(/.+\n\n/, '').lines.first.strip
30
+
31
+ s.date = Time.now.strftime('%Y-%m-%d')
32
+ s.files = gem_files
33
+ s.test_files = test_files
34
+ s.executables = bin_files
35
+ end
36
+ spec_create.call(spec)
37
+ spec.homepage = "https://github.com/godfat/#{spec.name}"
38
+ self.spec = spec
39
+ end
40
+
41
+ def write
42
+ File.open(spec_path, 'w'){ |f| f << split_lines(spec.to_ruby) }
43
+ end
44
+
45
+ def split_lines ruby
46
+ ruby.gsub(/(.+?)\s*=\s*\[(.+?)\]/){ |s|
47
+ if $2.index(',')
48
+ "#{$1} = [\n #{$2.split(',').map(&:strip).join(",\n ")}]"
49
+ else
50
+ s
51
+ end
52
+ }
53
+ end
54
+
55
+ def strip_path path
56
+ strip_home_path(strip_cwd_path(path))
57
+ end
58
+
59
+ def strip_home_path path
60
+ path.sub(ENV['HOME'], '~')
61
+ end
62
+
63
+ def strip_cwd_path path
64
+ path.sub(Dir.pwd, '.')
65
+ end
66
+
67
+ def git *args
68
+ `git --git-dir=#{dir}/.git #{args.join(' ')}`
69
+ end
70
+
71
+ def sh_git *args
72
+ Rake.sh('git', "--git-dir=#{dir}/.git", *args)
73
+ end
74
+
75
+ def sh_gem *args
76
+ Rake.sh(Gem.ruby, '-S', 'gem', *args)
77
+ end
78
+
79
+ def glob path=dir
80
+ Dir.glob("#{path}/**/*", File::FNM_DOTMATCH)
81
+ end
82
+
83
+ def readme
84
+ @readme ||=
85
+ if (path = "#{Gemgem.dir}/README.md") && File.exist?(path)
86
+ ps = "##{File.read(path)}".
87
+ scan(/((#+)[^\n]+\n\n.+?(?=(\n\n\2[^#\n]+\n)|\Z))/m).map(&:first)
88
+ ps.inject('HEADER' => ps.first){ |r, s, i|
89
+ r[s[/\w+/]] = s
90
+ r
91
+ }
92
+ else
93
+ {}
94
+ end
95
+ end
96
+
97
+ def description
98
+ # JRuby String#lines is returning an enumerator
99
+ @description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a
100
+ end
101
+
102
+ def all_files
103
+ @all_files ||= fold_files(glob).sort
104
+ end
105
+
106
+ def fold_files files
107
+ files.inject([]){ |r, path|
108
+ if File.file?(path) && path !~ %r{/\.git(/|$)} &&
109
+ (rpath = path[%r{^#{escaped_dir}/(.*$)}, 1])
110
+ r << rpath
111
+ elsif File.symlink?(path) # walk into symlinks...
112
+ r.concat(fold_files(glob(File.expand_path(path,
113
+ File.readlink(path)))))
114
+ else
115
+ r
116
+ end
117
+ }
118
+ end
119
+
120
+ def gem_files
121
+ @gem_files ||= all_files.reject{ |f|
122
+ f =~ ignored_pattern && !git_files.include?(f)
123
+ }
124
+ end
125
+
126
+ def test_files
127
+ @test_files ||= gem_files.grep(%r{^test/(.+?/)*test_.+?\.rb$})
128
+ end
129
+
130
+ def bin_files
131
+ @bin_files ||= gem_files.grep(%r{^bin/}).map{ |f| File.basename(f) }
132
+ end
133
+
134
+ def git_files
135
+ @git_files ||= if File.exist?("#{dir}/.git")
136
+ git('ls-files').split("\n")
137
+ else
138
+ []
139
+ end
140
+ end
141
+
142
+ def ignored_files
143
+ @ignored_files ||= all_files.grep(ignored_pattern)
144
+ end
145
+
146
+ def ignored_pattern
147
+ @ignored_pattern ||= if gitignore.empty?
148
+ /^$/
149
+ else
150
+ Regexp.new(expand_patterns(gitignore).join('|'))
151
+ end
152
+ end
153
+
154
+ def expand_patterns pathes
155
+ # http://git-scm.com/docs/gitignore
156
+ pathes.flat_map{ |path|
157
+ # we didn't implement negative pattern for now
158
+ Regexp.escape(path).sub(%r{^/}, '^').gsub(/\\\*/, '[^/]*')
159
+ }
160
+ end
161
+
162
+ def gitignore
163
+ @gitignore ||= if File.exist?(path = "#{dir}/.gitignore")
164
+ File.read(path).lines.
165
+ reject{ |l| l == /^\s*(#|\s+$)/ }.map(&:strip)
166
+ else
167
+ []
168
+ end
169
+ end
170
+ end
171
+
172
+ namespace :gem do
173
+
174
+ desc 'Install gem'
175
+ task :install => [:build] do
176
+ Gemgem.sh_gem('install', Gemgem.gem_path)
177
+ end
178
+
179
+ desc 'Build gem'
180
+ task :build => [:spec] do
181
+ require 'fileutils'
182
+ require 'rubygems/package'
183
+ gem = nil
184
+ Dir.chdir(Gemgem.dir) do
185
+ gem = Gem::Package.build(Gem::Specification.load(Gemgem.spec_path))
186
+ FileUtils.mkdir_p(Gemgem.pkg_dir)
187
+ FileUtils.mv(gem, Gemgem.pkg_dir) # gem is relative path, but might be ok
188
+ end
189
+ puts "\e[35mGem built: \e[33m" \
190
+ "#{Gemgem.strip_path("#{Gemgem.pkg_dir}/#{gem}")}\e[0m"
191
+ end
192
+
193
+ desc 'Generate gemspec'
194
+ task :spec do
195
+ Gemgem.create
196
+ Gemgem.write
197
+ end
198
+
199
+ desc 'Release gem'
200
+ task :release => [:spec, :check, :build] do
201
+ Gemgem.module_eval do
202
+ sh_git('tag', Gemgem.gem_tag)
203
+ sh_git('push')
204
+ sh_git('push', '--tags')
205
+ sh_gem('push', Gemgem.gem_path)
206
+ end
207
+ end
208
+
209
+ task :check do
210
+ ver = Gemgem.spec.version.to_s
211
+
212
+ if ENV['VERSION'].nil?
213
+ puts("\e[35mExpected " \
214
+ "\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m")
215
+ exit(1)
216
+
217
+ elsif ENV['VERSION'] != ver
218
+ puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \
219
+ "\e[35mbut got\n " \
220
+ "\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m")
221
+ exit(2)
222
+ end
223
+ end
224
+
225
+ end # of gem namespace
226
+
227
+ desc 'Run tests'
228
+ task :test do
229
+ next if Gemgem.test_files.empty?
230
+
231
+ require 'bacon'
232
+ Bacon.extend(Bacon::TestUnitOutput)
233
+ Bacon.summary_on_exit
234
+ Gemgem.test_files.each{ |file| require "#{Gemgem.dir}/#{file[0..-4]}" }
235
+ end
236
+
237
+ desc 'Trash ignored files'
238
+ task :clean => ['gem:spec'] do
239
+ next if Gemgem.ignored_files.empty?
240
+
241
+ require 'fileutils'
242
+ trash = File.expand_path("~/.Trash/#{Gemgem.spec.name}")
243
+ puts "Move the following files into:" \
244
+ " \e[35m#{Gemgem.strip_path(trash)}\e[33m"
245
+
246
+ Gemgem.ignored_files.each do |file|
247
+ from = "#{Gemgem.dir}/#{file}"
248
+ to = "#{trash}/#{File.dirname(file)}"
249
+ puts Gemgem.strip_path(from)
250
+
251
+ FileUtils.mkdir_p(to)
252
+ FileUtils.mv(from, to)
253
+ end
254
+
255
+ print "\e[0m"
256
+ end
257
+
258
+ task :default do
259
+ # Is there a reliable way to do this in the current process?
260
+ # It failed miserably before between Rake versions...
261
+ exec "#{Gem.ruby} -S #{$PROGRAM_NAME} -f #{Rake.application.rakefile} -T"
262
+ end
data/test/test_api.rb ADDED
@@ -0,0 +1,12 @@
1
+
2
+ require 'rubyqc/test'
3
+
4
+ describe RubyQC::API do
5
+ should 'one_of' do
6
+ check([Class]*5) do |klasses|
7
+ check(one_of(*klasses)) do |obj|
8
+ klasses.find{ |klass| obj.kind_of?(klass) }.should.not.nil
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+
2
+ require 'rubyqc/test'
3
+
4
+ describe 'kind_of' do
5
+ [Fixnum, Bignum, Array, Integer, Class].each do |klass|
6
+ should klass.name do
7
+ check(klass){ |obj| obj.should.kind_of(klass) }
8
+ end
9
+ end
10
+
11
+ [[], [Fixnum], [Fixnum, Array],
12
+ {}, {:a => 0}, {'b' => 1}, {2 => Integer},
13
+ {nil => [Fixnum]}, {true => {false => [Bignum]}}
14
+ ].each do |spec|
15
+
16
+ should spec.inspect do
17
+ check(spec) do |generated|
18
+ verify_generated(generated, spec)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require 'rubyqc/test'
3
+
4
+ describe Array do
5
+ describe 'sort' do
6
+ should 'Any front elements should be <= any rear elements' do
7
+ check([Fixnum]*100).times(10) do |array|
8
+ array.sort.each_cons(2).each{ |x, y| x.should <= y }
9
+ end
10
+ end
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubyqc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Lin Jen-Shin (godfat)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-30 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ RubyQC -- A conceptual [QuickCheck][] library for Ruby.
15
+
16
+ It's not a faithful port since Hsakell is totally different than Ruby.
17
+ However it's still benefit to use some of the ideas behind QuickCheck,
18
+ and we could also use RubyQC for generating arbitrary objects.
19
+
20
+ [QuickCheck]: http://en.wikipedia.org/wiki/QuickCheck
21
+ email:
22
+ - godfat (XD) godfat.org
23
+ executables: []
24
+ extensions: []
25
+ extra_rdoc_files: []
26
+ files:
27
+ - ".gitignore"
28
+ - ".gitmodules"
29
+ - ".travis.yml"
30
+ - Gemfile
31
+ - LICENSE
32
+ - README.md
33
+ - Rakefile
34
+ - lib/rubyqc.rb
35
+ - lib/rubyqc/modifier.rb
36
+ - lib/rubyqc/prelude.rb
37
+ - lib/rubyqc/test.rb
38
+ - lib/rubyqc/version.rb
39
+ - rubyqc.gemspec
40
+ - task/README.md
41
+ - task/gemgem.rb
42
+ - test/test_api.rb
43
+ - test/test_kind_of.rb
44
+ - test/test_readme.rb
45
+ homepage: https://github.com/godfat/rubyqc
46
+ licenses:
47
+ - Apache License 2.0
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.2.1
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: RubyQC -- A conceptual [QuickCheck][] library for Ruby.
69
+ test_files:
70
+ - test/test_api.rb
71
+ - test/test_kind_of.rb
72
+ - test/test_readme.rb