piko-sharp-gem 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
+ SHA256:
3
+ metadata.gz: 046bc9e685d69eb01fd5d44fcb6480cd8f93515b3eee08a78c77da6ccf6c814f
4
+ data.tar.gz: 80f880bb07903acc0371b8f9105b96c43becd1882488f456dd43a8b11b6fe923
5
+ SHA512:
6
+ metadata.gz: dd79cdde4fcb7edd0ee9e863aad123feb87a2cbc7280077e14c546c58e9523591874269291dc945a02a029f5e9e4d631d3fc281c9e340b887f73299739968078
7
+ data.tar.gz: 4214a28b1e9f089753fd7ae4f7118672309e518427b32a79c33ac751b20526da11ef8336b8dbb64993cb312b254c63e26eae2d8c55ccf39df97fb2bb85a36f75
@@ -0,0 +1,11 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "piko-sharp-gem"
3
+ s.version = "0.0.1"
4
+ s.summary = "Research test"
5
+ s.description = "University research based on power_assert"
6
+ s.authors = ["Andrey78"]
7
+ s.email = ["cakoc614@gmail.com"]
8
+ s.files = Dir.glob("**/*").reject { |f| f.end_with?('.gem') }
9
+ s.homepage = "https://rubygems.org/profiles/Andrey78"
10
+ s.license = "MIT"
11
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (C) 2014 Kazuki Tsujimoto
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,57 @@
1
+ Copyright (C) 2014 Kazuki Tsujimoto
2
+
3
+ You can redistribute it and/or modify it under either the terms of the
4
+ 2-clause BSDL (see the file BSDL), or the conditions below:
5
+
6
+ 1. You may make and give away verbatim copies of the source form of the
7
+ software without restriction, provided that you duplicate all of the
8
+ original copyright notices and associated disclaimers.
9
+
10
+ 2. You may modify your copy of the software in any way, provided that
11
+ you do at least ONE of the following:
12
+
13
+ a) place your modifications in the Public Domain or otherwise
14
+ make them Freely Available, such as by posting said
15
+ modifications to Usenet or an equivalent medium, or by allowing
16
+ the author to include your modifications in the software.
17
+
18
+ b) use the modified software only within your corporation or
19
+ organization.
20
+
21
+ c) give non-standard binaries non-standard names, with
22
+ instructions on where to get the original software distribution.
23
+
24
+ d) make other distribution arrangements with the author.
25
+
26
+ 3. You may distribute the software in object code or binary form,
27
+ provided that you do at least ONE of the following:
28
+
29
+ a) distribute the binaries and library files of the software,
30
+ together with instructions (in the manual page or equivalent)
31
+ on where to get the original distribution.
32
+
33
+ b) accompany the distribution with the machine-readable source of
34
+ the software.
35
+
36
+ c) give non-standard binaries non-standard names, with
37
+ instructions on where to get the original software distribution.
38
+
39
+ d) make other distribution arrangements with the author.
40
+
41
+ 4. You may modify and include the part of the software into any other
42
+ software (possibly commercial). But some files in the distribution
43
+ are not written by the author, so that they are not under these terms.
44
+
45
+ For the list of those files and their copying conditions, see the
46
+ file LEGAL.
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ # https://github.com/redmine/redmine/blob/3.0.4/Gemfile#L101
6
+ local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
7
+ if File.exist?(local_gemfile)
8
+ eval_gemfile local_gemfile
9
+ end
10
+
11
+ group :development do
12
+ gem 'test-unit'
13
+ gem 'rake'
14
+ gem 'simplecov'
15
+ gem 'irb', '>= 1.3.1'
16
+ gem 'fiddle'
17
+ gem 'benchmark-ips'
18
+ end
@@ -0,0 +1,4 @@
1
+ LEGAL NOTICE INFORMATION
2
+ ------------------------
3
+
4
+ All the files in this distribution are written by the author.
@@ -0,0 +1,108 @@
1
+ # power_assert
2
+ ## About
3
+ Power Assert shows each value of variables and method calls in the expression.
4
+ It is useful for testing, providing which value wasn't correct when the condition is not satisfied.
5
+
6
+ Failure:
7
+ assert { 3.times.to_a.include?(3) }
8
+ | | |
9
+ | | false
10
+ | [0, 1, 2]
11
+ #<Enumerator: 3:times>
12
+
13
+ ## Related Projects
14
+ In general, you don't need to use this library directly.
15
+ Use following test frameworks or extensions instead.
16
+
17
+ * [test-unit](https://github.com/test-unit/test-unit)(>= 3.0.0)
18
+ * [Document](http://test-unit.github.io/test-unit/en/Test/Unit/Assertions.html#assert-instance_method)
19
+ * [minitest-power_assert](https://github.com/hsbt/minitest-power_assert)
20
+ * [rspec-power_assert](https://github.com/joker1007/rspec-power_assert)
21
+ * [rspec-matchers-power_assert_matchers](https://github.com/kachick/rspec-matchers-power_assert_matchers)
22
+ * [pry-power_assert](https://github.com/yui-knk/pry-power_assert)
23
+ * [irb-power_assert](https://github.com/kachick/irb-power_assert)
24
+ * [power_p](https://github.com/k-tsj/power_p)
25
+
26
+ ## Requirement
27
+ * CRuby 3.1+
28
+
29
+ ## Configuration
30
+ To colorize output messages, add <code>require "power_assert/colorize"</code> to your code.
31
+ (It requires irb 1.3.1+)
32
+
33
+ ## Known Limitations
34
+ * Expressions must be on a single line. Splitting an assertion across multiple lines prevents any report from being generated, e.g.:
35
+
36
+ ```ruby
37
+ assert do
38
+ # Reported
39
+ func(foo: 0123456789, bar: "abcdefg")
40
+ end
41
+
42
+ assert do
43
+ # Not reported
44
+ func(foo: 0123456789,
45
+ bar: "abcdefg")
46
+ end
47
+ ```
48
+
49
+ * Expressions must include at least one method call. Assertions without method calls generate no report, e.g.:
50
+
51
+ ```ruby
52
+ val = false
53
+ assert do
54
+ # Reported
55
+ val == true
56
+ end
57
+
58
+ assert do
59
+ # Not reported
60
+ val
61
+ end
62
+ ```
63
+
64
+ * Return values from `method_missing` or `super` generate no report, e.g.:
65
+
66
+ ```ruby
67
+ class Foo
68
+ def method_missing(*)
69
+ :foo
70
+ end
71
+ end
72
+ foo = Foo.new
73
+
74
+ assert do
75
+ # Not reported
76
+ foo.foo
77
+ end
78
+ ```
79
+
80
+ * Avoid conditional branches inside assertions. Conditional logic may prevent a report from being generated, e.g.:
81
+
82
+ ```ruby
83
+ condition = true
84
+ expected = false
85
+ actual = true
86
+ assert do
87
+ # This fails, but nothing is reported
88
+ condition ? expected == actual : expected == actual
89
+ end
90
+ ```
91
+
92
+ * (CRuby 4.0+) `<Struct subclass>.new` generates no report. Use `<Struct subclass>.[]` instead, e.g.:
93
+
94
+ ```ruby
95
+ s = Struct.new(:a)
96
+ assert do
97
+ # Not reported
98
+ s.new(0)
99
+ end
100
+
101
+ assert do
102
+ # Reported
103
+ s[0]
104
+ end
105
+ ```
106
+
107
+ ## Reference
108
+ * [Power Assert in Ruby (at RubyKaigi 2014) // Speaker Deck](https://speakerdeck.com/k_tsj/power-assert-in-ruby)
@@ -0,0 +1,58 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task :default => :test
5
+ Rake::TestTask.new(:test) do |t|
6
+ # helper(simplecov) must be required before loading power_assert
7
+ helper_path = File.realpath("test/test_helper.rb")
8
+ t.ruby_opts = ["-w", "-r#{helper_path}"]
9
+ t.test_files = FileList["test/**/*_test.rb"].exclude do |i|
10
+ begin
11
+ RubyVM::InstructionSequence.compile(File.read(i))
12
+ false
13
+ rescue SyntaxError
14
+ true
15
+ end
16
+ end
17
+ end
18
+
19
+ # ruby/ruby:test/pathname/test_pathname.rb
20
+ def has_symlink?
21
+ begin
22
+ File.symlink("", "")
23
+ rescue NotImplementedError, Errno::EACCES
24
+ return false
25
+ rescue Errno::ENOENT
26
+ end
27
+ return true
28
+ end
29
+
30
+ SYMLINK_DIRS = ["lib", "test"]
31
+
32
+ task :before_script do
33
+ if ENV["TEST_SYMLINK"] == "yes" and has_symlink?
34
+ SYMLINK_DIRS.each do |d|
35
+ File.rename(d, ".#{d}")
36
+ File.symlink(".#{d}", d)
37
+ end
38
+ end
39
+ end
40
+
41
+ task :after_script do
42
+ SYMLINK_DIRS.each do |d|
43
+ if File.symlink?(d) and File.directory?(".#{d}")
44
+ File.unlink(d)
45
+ File.rename(".#{d}", d)
46
+ end
47
+ unless File.directory?(d)
48
+ raise "#{d} should be directory"
49
+ end
50
+ end
51
+ end
52
+
53
+ desc "Run the benchmark suite"
54
+ task :benchmark do
55
+ Dir.glob("benchmark/bm_*.rb").each do |f|
56
+ load(f)
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ require 'power_assert/configuration'
2
+
3
+ PowerAssert.configure do |c|
4
+ c.lazy_inspection = true
5
+ c.colorize_message = true
6
+ c.inspector = :pp
7
+ end
@@ -0,0 +1,46 @@
1
+ module PowerAssert
2
+ class << self
3
+ def configuration
4
+ @configuration ||= Configuration[false, true, false, :p]
5
+ end
6
+
7
+ def configure
8
+ yield configuration
9
+ end
10
+ end
11
+
12
+ class Configuration < Struct.new(:lazy_inspection, :_redefinition, :colorize_message, :inspector)
13
+ def colorize_message=(bool)
14
+ if bool
15
+ require 'irb/color'
16
+ if inspector == :pp
17
+ require 'irb/color_printer'
18
+ end
19
+ end
20
+ super
21
+ end
22
+
23
+ def lazy_inspection=(bool)
24
+ unless bool
25
+ raise 'lazy_inspection option must be enabled when using pp' if inspector == :pp
26
+ end
27
+ super
28
+ end
29
+
30
+ def inspector=(inspector)
31
+ case inspector
32
+ when :pp
33
+ raise 'lazy_inspection option must be enabled when using pp' unless lazy_inspection
34
+ require 'pp'
35
+ if colorize_message
36
+ require 'irb/color_printer'
37
+ end
38
+ when :p
39
+ else
40
+ raise ArgumentError, "unknown inspector: #{inspector}"
41
+ end
42
+ super
43
+ end
44
+ end
45
+ private_constant :Configuration
46
+ end
@@ -0,0 +1,196 @@
1
+ require 'power_assert/parser'
2
+ require 'power_assert/configuration'
3
+ require 'power_assert/enable_tracepoint_events'
4
+ require 'power_assert/inspector'
5
+
6
+ module PowerAssert
7
+ class Context
8
+ Value = Struct.new(:name, :value, :lineno, :column, :display_offset)
9
+
10
+ def initialize(assertion_proc_or_source, assertion_method, source_binding)
11
+ @fired = false
12
+ @target_thread = Thread.current
13
+
14
+ if assertion_proc_or_source.respond_to?(:to_proc)
15
+ @assertion_proc = assertion_proc_or_source.to_proc
16
+ line = nil
17
+ else
18
+ @assertion_proc = source_binding.eval "Proc.new {#{assertion_proc_or_source}}"
19
+ line = assertion_proc_or_source
20
+ end
21
+
22
+ @parser = Parser::DUMMY
23
+ @trace_call = TracePoint.new(:call, :c_call) do
24
+ if PowerAssert.app_context? and Thread.current == @target_thread
25
+ @trace_call.disable
26
+ locs = PowerAssert.app_caller_locations
27
+ path = locs.last.path
28
+ lineno = locs.last.lineno
29
+ if File.exist?(path)
30
+ line ||= File.open(path) {|fp| fp.each_line.drop(lineno - 1).first }
31
+ end
32
+ if line
33
+ @parser = Parser.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s, @assertion_proc)
34
+ end
35
+ end
36
+ end
37
+
38
+ method_id_set = nil
39
+ @return_values = []
40
+ @trace_return = TracePoint.new(:return, :c_return) do |tp|
41
+ unless method_id_set
42
+ next unless Thread.current == @target_thread
43
+ method_id_set = @parser.method_id_set
44
+ end
45
+ method_id = tp.callee_id
46
+ next if ! method_id_set[method_id]
47
+ next if tp.event == :c_return and
48
+ not (@parser.lineno == tp.lineno and @parser.path == tp.path)
49
+ locs = PowerAssert.app_caller_locations
50
+ if (tp.event == :c_return && locs.length == 1 || tp.event == :return && locs.length <= 2) and Thread.current == @target_thread
51
+ if @parser.path == locs.last.path and @parser.lineno == locs.last.lineno
52
+ val = PowerAssert.configuration.lazy_inspection ?
53
+ tp.return_value :
54
+ InspectedValue.new(SafeInspectable.new(tp.return_value).inspect)
55
+ @return_values << Value[method_id.to_s, val, locs.last.lineno, nil]
56
+ end
57
+ end
58
+ rescue Exception => e
59
+ warn "power_assert: [BUG] Failed to trace: #{e.class}: #{e.message}"
60
+ if e.respond_to?(:full_message)
61
+ warn e.full_message.gsub(/^/, 'power_assert: ')
62
+ end
63
+ end
64
+ end
65
+
66
+ def message
67
+ raise 'call #yield at first' unless fired?
68
+ @message ||= build_assertion_message(@parser, @return_values).freeze
69
+ end
70
+
71
+ def message_proc
72
+ -> { message }
73
+ end
74
+
75
+ def yield
76
+ @fired = true
77
+ invoke_yield(&@assertion_proc)
78
+ end
79
+
80
+ private
81
+
82
+ def invoke_yield
83
+ @trace_return.enable do
84
+ @trace_call.enable do
85
+ yield
86
+ end
87
+ end
88
+ end
89
+
90
+ def fired?
91
+ @fired
92
+ end
93
+
94
+ def build_assertion_message(parser, return_values)
95
+ if PowerAssert.configuration.colorize_message
96
+ line = IRB::Color.colorize_code(parser.line, ignore_error: true)
97
+ else
98
+ line = parser.line
99
+ end
100
+
101
+ path = detect_path(parser, return_values)
102
+ return line unless path
103
+
104
+ c2d = column2display_offset(parser.line)
105
+ return_values, methods_in_path = find_all_identified_calls(return_values, path)
106
+ return_values.zip(methods_in_path) do |i, j|
107
+ unless i.name == j.name
108
+ warn "power_assert: [BUG] Failed to get column: #{i.name}"
109
+ return line
110
+ end
111
+ i.display_offset = c2d[j.column]
112
+ end
113
+ refs_in_path = path.find_all {|i| i.type == :ref }
114
+ ref_values = refs_in_path.map {|i| Value[i.name, parser.binding.eval(i.name), parser.lineno, i.column, c2d[i.column]] }
115
+ vals = (return_values + ref_values).find_all(&:display_offset).sort_by(&:display_offset).reverse
116
+ return line if vals.empty?
117
+
118
+ fmt = (0..vals[0].display_offset).map do |i|
119
+ if vals.find {|v| v.display_offset == i }
120
+ "%<#{i}>s"
121
+ else
122
+ line[i] == "\t" ? "\t" : ' '
123
+ end
124
+ end.join
125
+ lines = []
126
+ lines << line.chomp
127
+ lines << sprintf(fmt, vals.each_with_object({}) {|v, h| h[:"#{v.display_offset}"] = '|' }).chomp
128
+ vals.each do |i|
129
+ inspected_val = SafeInspectable.new(Inspector.new(i.value, i.display_offset)).inspect
130
+ inspected_val.each_line do |l|
131
+ map_to = vals.each_with_object({}) do |j, h|
132
+ h[:"#{j.display_offset}"] = [l, '|', ' '][i.display_offset <=> j.display_offset]
133
+ end
134
+ lines << encoding_safe_rstrip(sprintf(fmt, map_to))
135
+ end
136
+ end
137
+ lines.join("\n")
138
+ end
139
+
140
+ def detect_path(parser, return_values)
141
+ return parser.call_paths.flatten.uniq if parser.method_id_set.empty?
142
+ all_paths = parser.call_paths
143
+ return_value_names = return_values.map(&:name)
144
+ uniq_calls = uniq_calls(all_paths)
145
+ uniq_call = return_value_names.find {|i| uniq_calls.include?(i) }
146
+ detected_paths = all_paths.find_all do |path|
147
+ method_names = path.find_all {|ident| ident.type == :method }.map(&:name)
148
+ break [path] if uniq_call and method_names.include?(uniq_call)
149
+ return_value_names == method_names
150
+ end
151
+ return nil unless detected_paths.length == 1
152
+ detected_paths[0]
153
+ end
154
+
155
+ def uniq_calls(paths)
156
+ all_calls = enum_count_by(paths.map {|path| path.find_all {|ident| ident.type == :method }.map(&:name).uniq }.flatten) {|i| i }
157
+ all_calls.find_all {|_, call_count| call_count == 1 }.map {|name, _| name }
158
+ end
159
+
160
+ def find_all_identified_calls(return_values, path)
161
+ return_value_num_of_calls = enum_count_by(return_values, &:name)
162
+ path_num_of_calls = enum_count_by(path.find_all {|ident| ident.type == :method }, &:name)
163
+ identified_calls = return_value_num_of_calls.find_all {|name, num| path_num_of_calls[name] == num }.map(&:first)
164
+ [
165
+ return_values.find_all {|val| identified_calls.include?(val.name) },
166
+ path.find_all {|ident| ident.type == :method and identified_calls.include?(ident.name) }
167
+ ]
168
+ end
169
+
170
+ def enum_count_by(enum, &blk)
171
+ Hash[enum.group_by(&blk).map{|k, v| [k, v.length] }]
172
+ end
173
+
174
+ def encoding_safe_rstrip(str)
175
+ str.rstrip
176
+ rescue ArgumentError, Encoding::CompatibilityError
177
+ enc = str.encoding
178
+ if enc.ascii_compatible?
179
+ str.b.rstrip.force_encoding(enc)
180
+ else
181
+ str
182
+ end
183
+ end
184
+
185
+ def column2display_offset(str)
186
+ display_offset = 0
187
+ str.each_char.with_object([]) do |c, r|
188
+ c.bytesize.times do
189
+ r << display_offset
190
+ end
191
+ display_offset += c.ascii_only? ? 1 : 2 # FIXME
192
+ end
193
+ end
194
+ end
195
+ private_constant :Context
196
+ end
@@ -0,0 +1,53 @@
1
+ require 'power_assert/configuration'
2
+
3
+ if PowerAssert.configuration._redefinition
4
+ module PowerAssert
5
+ # set redefined flag
6
+ basic_classes = [
7
+ Integer, Float, String, Array, Hash, Symbol, Time, Regexp, NilClass, TrueClass, FalseClass
8
+ ]
9
+
10
+ basic_operators = [
11
+ :+, :-, :*, :/, :%, :==, :===, :<, :<=, :<<, :[], :[]=, :length, :size,
12
+ :empty?, :nil?, :succ, :>, :>=, :!, :!=, :=~, :freeze, :-@, :max, :min,
13
+ # :call (it is just used for block call optimization)
14
+ :&, :|,
15
+ # :default (no specialized instruction for this)
16
+ :pack, :include?,
17
+ ]
18
+
19
+ basic_classes.each do |klass|
20
+ basic_operators.each do |bop|
21
+ if klass.public_method_defined?(bop)
22
+ refine(klass) do
23
+ define_method(bop) {}
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # bypass check_cfunc
30
+ refine BasicObject do
31
+ def !
32
+ end
33
+
34
+ def ==
35
+ end
36
+ end
37
+
38
+ refine Module do
39
+ def ==
40
+ end
41
+ end
42
+
43
+ refine Class do
44
+ def new
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # disable optimization
51
+ RubyVM::InstructionSequence.compile_option = {
52
+ specialized_instruction: false
53
+ }
@@ -0,0 +1,66 @@
1
+ require 'power_assert/configuration'
2
+ begin
3
+ require 'io/console/size'
4
+ rescue LoadError
5
+ end
6
+
7
+ module PowerAssert
8
+ class InspectedValue
9
+ def initialize(value)
10
+ @value = value
11
+ end
12
+
13
+ def inspect
14
+ @value
15
+ end
16
+ end
17
+ private_constant :InspectedValue
18
+
19
+ class SafeInspectable
20
+ def initialize(value)
21
+ @value = value
22
+ end
23
+
24
+ def inspect
25
+ inspected = @value.inspect
26
+ if Encoding.compatible?(Encoding.default_external, inspected)
27
+ inspected
28
+ else
29
+ begin
30
+ "#{inspected.encode(Encoding.default_external)}(#{inspected.encoding})"
31
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
32
+ inspected.force_encoding(Encoding.default_external)
33
+ end
34
+ end
35
+ rescue => e
36
+ "InspectionFailure: #{e.class}: #{e.message.each_line.first}"
37
+ end
38
+ end
39
+ private_constant :SafeInspectable
40
+
41
+ class Inspector
42
+ def initialize(value, indent)
43
+ @value = value
44
+ @indent = indent
45
+ end
46
+
47
+ def inspect
48
+ if PowerAssert.configuration.colorize_message
49
+ if PowerAssert.configuration.inspector == :pp
50
+ console_width = IO.respond_to?(:console_size) ? IO.console_size[1] : 80
51
+ width = [console_width - 1 - @indent, 10].max
52
+ IRB::ColorPrinter.pp(@value, +'', width)
53
+ else
54
+ IRB::Color.colorize_code(@value.to_s, ignore_error: true)
55
+ end
56
+ else
57
+ if PowerAssert.configuration.inspector == :pp
58
+ PP.pp(@value, +'')
59
+ else
60
+ @value.inspect
61
+ end
62
+ end
63
+ end
64
+ end
65
+ private_constant :Inspector
66
+ end
@@ -0,0 +1,216 @@
1
+ require 'ripper'
2
+
3
+ module PowerAssert
4
+ class Parser
5
+ Ident = Struct.new(:type, :name, :column)
6
+
7
+ attr_reader :line, :path, :lineno, :binding
8
+
9
+ def initialize(line, path, lineno, binding, assertion_method_name = nil, assertion_proc = nil)
10
+ @line = line
11
+ @line_for_parsing = (valid_syntax?(line) ? line : slice_expression(line)).b
12
+ @path = path
13
+ @lineno = lineno
14
+ @binding = binding
15
+ @proc_local_variables = binding.eval('local_variables').map(&:to_s)
16
+ @assertion_method_name = assertion_method_name
17
+ @assertion_proc = assertion_proc
18
+ end
19
+
20
+ def idents
21
+ @idents ||= extract_idents(Ripper.sexp(@line_for_parsing))
22
+ end
23
+
24
+ def call_paths
25
+ collect_paths(idents).uniq
26
+ end
27
+
28
+ def method_id_set
29
+ methods = idents.flatten.find_all {|i| i.type == :method }
30
+ @method_id_set ||= methods.map(&:name).map(&:to_sym).each_with_object({}) {|i, h| h[i] = true }
31
+ end
32
+
33
+ private
34
+
35
+ def valid_syntax?(str)
36
+ verbose, $VERBOSE = $VERBOSE, nil
37
+ RubyVM::InstructionSequence.compile(str)
38
+ true
39
+ rescue SyntaxError
40
+ false
41
+ ensure
42
+ $VERBOSE = verbose
43
+ end
44
+
45
+ def slice_expression(str)
46
+ str = str.chomp
47
+ str.sub!(/\A\s*(?:if|unless|elsif|case|while|until) /) {|i| ' ' * i.length }
48
+ str.sub!(/\A\s*(?:\}|\]|end)?\./) {|i| ' ' * i.length }
49
+ str.sub!(/[\{\.\\]\z/, '')
50
+ str.sub!(/(?:&&|\|\|)\z/, '')
51
+ str.sub!(/ (?:do|and|or)\z/, '')
52
+ str
53
+ end
54
+
55
+ class Branch < Array
56
+ end
57
+
58
+ AND_OR_OPS = %i(and or && ||)
59
+
60
+ #
61
+ # Returns idents as graph structure.
62
+ #
63
+ # +--c--b--+
64
+ # extract_idents(Ripper.sexp('a&.b(c).d')) #=> a--+ +--d
65
+ # +--------+
66
+ #
67
+ def extract_idents(sexp)
68
+ case sexp
69
+ in [:arg_paren | :assoc_splat | :fcall | :hash | :method_add_block | :string_literal | :return, s, *]
70
+ extract_idents(s)
71
+ in [:assign | :massign, _, s]
72
+ extract_idents(s)
73
+ in [:opassign, _, [_, op_name, [_, op_column]], s]
74
+ extract_idents(s) + [Ident[:method, op_name.sub(/=\z/, ''), op_column]]
75
+ in [:dyna_symbol, [Symbol, *] => s]
76
+ # s can be [:string_content, [..]] while parsing an expression like { "a": 1 }
77
+ extract_idents(s)
78
+ in [:dyna_symbol, ss]
79
+ ss.flat_map {|s| extract_idents(s) }
80
+ in [:assoclist_from_args | :bare_assoc_hash | :paren | :string_embexpr | :regexp_literal | :xstring_literal, ss, *]
81
+ ss.flat_map {|s| extract_idents(s) }
82
+ in [:command, s0, s1]
83
+ [s1, s0].flat_map {|s| extract_idents(s) }
84
+ in [:assoc_new | :dot2 | :dot3 | :string_content, *ss]
85
+ ss.flat_map {|s| extract_idents(s) }
86
+ in [:unary, mid, s]
87
+ handle_columnless_ident([], mid, extract_idents(s))
88
+ in [:binary, s0, op, s1] if AND_OR_OPS.include?(op)
89
+ extract_idents(s0) + [Branch[extract_idents(s1), []]]
90
+ in [:binary, s0, op, s1]
91
+ handle_columnless_ident(extract_idents(s0), op, extract_idents(s1))
92
+ in [:call, recv, [op_sym, op_name, _], method]
93
+ with_safe_op = ((op_sym == :@op and op_name == '&.') or op_sym == :"&.")
94
+ if method == :call
95
+ handle_columnless_ident(extract_idents(recv), :call, [], with_safe_op)
96
+ else
97
+ extract_idents(recv) + (with_safe_op ? [Branch[extract_idents(method), []]] : extract_idents(method))
98
+ end
99
+ in [:array, ss]
100
+ ss ? ss.flat_map {|s| extract_idents(s) } : []
101
+ in [:command_call, s0, _, s1, s2]
102
+ [s0, s2, s1].flat_map {|s| extract_idents(s) }
103
+ in [:aref, s0, s1]
104
+ handle_columnless_ident(extract_idents(s0), :[], extract_idents(s1))
105
+ in [:method_add_arg, s0, s1]
106
+ case extract_idents(s0)
107
+ in []
108
+ # idents(s0) may be empty(e.g. ->{}.())
109
+ extract_idents(s1)
110
+ in [*is0, Branch[is1, []]]
111
+ # Safe navigation operator is used. See :call clause also.
112
+ is0 + [Branch[extract_idents(s1) + is1, []]]
113
+ in [*is, i]
114
+ is + extract_idents(s1) + [i]
115
+ end
116
+ in [:args_add_block, [:args_add_star, ss0, *ss1], _]
117
+ (ss0 + ss1).flat_map {|s| extract_idents(s) }
118
+ in [:args_add_block, ss, _]
119
+ ss.flat_map {|s| extract_idents(s) }
120
+ in [:vcall, [:@ident, name, [_, column]]]
121
+ [Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]]
122
+ in [:vcall, _]
123
+ []
124
+ in [:program, [[:method_add_block, [:method_add_arg, [:fcall, [:@ident | :@const, ^@assertion_method_name, _]], _], [:brace_block | :do_block, _, ss]]]]
125
+ ss.flat_map {|s| extract_idents(s) }
126
+ in [:program, [s, *]]
127
+ extract_idents(s)
128
+ in [:ifop, s0, s1, s2]
129
+ [*extract_idents(s0), Branch[extract_idents(s1), extract_idents(s2)]]
130
+ in [:if | :unless, s0, ss0, [_, ss1]]
131
+ [*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, ss1.flat_map {|s| extract_idents(s) }]]
132
+ in [:if | :unless, s0, ss0, _]
133
+ [*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, []]]
134
+ in [:if_mod | :unless_mod, s0, s1]
135
+ [*extract_idents(s0), Branch[extract_idents(s1), []]]
136
+ in [:var_ref | :var_field, [:@kw, 'self', [_, column]]]
137
+ [Ident[:ref, 'self', column]]
138
+ in [:var_ref | :var_field, [:@ident | :@const | :@cvar | :@ivar | :@gvar, ref_name, [_, column]]]
139
+ [Ident[:ref, ref_name, column]]
140
+ in [:var_ref | :var_field, _]
141
+ []
142
+ in [:@ident | :@const | :@op, method_name, [_, column]]
143
+ [Ident[:method, method_name, column]]
144
+ else
145
+ []
146
+ end
147
+ end
148
+
149
+ def str_indices(str, re, offset, limit)
150
+ idx = str.index(re, offset)
151
+ if idx and idx <= limit
152
+ [idx, *str_indices(str, re, idx + 1, limit)]
153
+ else
154
+ []
155
+ end
156
+ end
157
+
158
+ MID2SRCTXT = {
159
+ :[] => '[',
160
+ :+@ => '+',
161
+ :-@ => '-',
162
+ :call => '('
163
+ }
164
+
165
+ def handle_columnless_ident(left_idents, mid, right_idents, with_safe_op = false)
166
+ left_max = left_idents.flatten.max_by(&:column)
167
+ right_min = right_idents.flatten.min_by(&:column)
168
+ bg = left_max ? left_max.column + left_max.name.length : 0
169
+ ed = right_min ? right_min.column - 1 : @line_for_parsing.length - 1
170
+ mname = mid.to_s
171
+ srctxt = MID2SRCTXT[mid] || mname
172
+ re = /
173
+ #{'\b' if /\A\w/ =~ srctxt}
174
+ #{Regexp.escape(srctxt)}
175
+ #{'\b' if /\w\z/ =~ srctxt}
176
+ /x
177
+ indices = str_indices(@line_for_parsing, re, bg, ed)
178
+ if indices.length == 1 or !(right_idents.empty? and left_idents.empty?)
179
+ ident = Ident[:method, mname, right_idents.empty? ? indices.first : indices.last]
180
+ left_idents + right_idents + (with_safe_op ? [Branch[[ident], []]] : [ident])
181
+ else
182
+ left_idents + right_idents
183
+ end
184
+ end
185
+
186
+ def collect_paths(idents, prefixes = [[]], index = 0)
187
+ if index < idents.length
188
+ node = idents[index]
189
+ if node.kind_of?(Branch)
190
+ prefixes = node.flat_map {|n| collect_paths(n, prefixes, 0) }
191
+ else
192
+ prefixes = prefixes.map {|prefix| prefix + [node] }
193
+ end
194
+ collect_paths(idents, prefixes, index + 1)
195
+ else
196
+ prefixes
197
+ end
198
+ end
199
+
200
+ class DummyParser < Parser
201
+ def initialize
202
+ super('', nil, nil, TOPLEVEL_BINDING)
203
+ end
204
+
205
+ def idents
206
+ []
207
+ end
208
+
209
+ def call_paths
210
+ []
211
+ end
212
+ end
213
+ DUMMY = DummyParser.new
214
+ end
215
+ private_constant :Parser
216
+ end
@@ -0,0 +1,3 @@
1
+ module PowerAssert
2
+ VERSION = "3.0.1"
3
+ end
@@ -0,0 +1,63 @@
1
+ # power_assert.rb
2
+ #
3
+ # Copyright (C) 2014 Kazuki Tsujimoto
4
+
5
+ begin
6
+ captured = false
7
+ target_thread = Thread.current
8
+ TracePoint.new(:return, :c_return) do |tp|
9
+ next unless Thread.current == target_thread
10
+ captured = true
11
+ unless tp.return_value and tp.callee_id
12
+ raise ''
13
+ end
14
+ end.enable { __id__ }
15
+ raise '' unless captured
16
+ rescue
17
+ raise LoadError, 'Fully compatible TracePoint API required'
18
+ end
19
+
20
+ require 'power_assert/context'
21
+ require 'power_assert/configuration'
22
+ require 'power_assert/version'
23
+
24
+ module PowerAssert
25
+ POWER_ASSERT_LIB_DIR = File.dirname(caller_locations(1, 1).first.path)
26
+ INTERNAL_LIB_DIRS = {PowerAssert => POWER_ASSERT_LIB_DIR}
27
+ private_constant :POWER_ASSERT_LIB_DIR, :INTERNAL_LIB_DIRS
28
+
29
+ class << self
30
+ def start(assertion_proc_or_source, assertion_method: nil, source_binding: TOPLEVEL_BINDING)
31
+ clear_global_method_cache
32
+ yield Context.new(assertion_proc_or_source, assertion_method, source_binding)
33
+ end
34
+
35
+ def app_caller_locations
36
+ caller_locations.drop_while {|i| internal_file?(i.path) }.take_while {|i| ! internal_file?(i.path) }
37
+ end
38
+
39
+ def app_context?
40
+ top_frame = caller_locations.drop_while {|i| i.path.start_with?(POWER_ASSERT_LIB_DIR) }.first
41
+ top_frame and ! internal_file?(top_frame.path)
42
+ end
43
+
44
+ private
45
+
46
+ def internal_file?(file)
47
+ INTERNAL_LIB_DIRS.find do |_, dir|
48
+ file.start_with?(dir)
49
+ end
50
+ end
51
+
52
+ CLEAR_CACHE_ISEQ = RubyVM::InstructionSequence.compile('using PowerAssert.const_get(:Empty)')
53
+ private_constant :CLEAR_CACHE_ISEQ
54
+
55
+ def clear_global_method_cache
56
+ CLEAR_CACHE_ISEQ.eval
57
+ end
58
+ end
59
+
60
+ module Empty
61
+ end
62
+ private_constant :Empty
63
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'power_assert/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'power_assert'
8
+ s.version = PowerAssert::VERSION
9
+ s.authors = ['Kazuki Tsujimoto']
10
+ s.email = ['kazuki@callcc.net']
11
+ s.homepage = 'https://github.com/ruby/power_assert'
12
+ s.summary = "Power Assert for Ruby"
13
+ s.description = "Power Assert shows each value of variables and method calls in the expression. It is useful for testing, providing which value wasn't correct when the condition is not satisfied."
14
+
15
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{\A(?:test|spec|features|benchmark|bin)/})
17
+ end
18
+ s.bindir = 'exe'
19
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ s.require_paths = ['lib']
21
+ s.extra_rdoc_files = ['README.md']
22
+ s.rdoc_options = ['--main', 'README.md']
23
+ s.licenses = ['BSD-2-Clause', "Ruby"]
24
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: piko-sharp-gem
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrey78
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-07-06 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: University research based on power_assert
13
+ email:
14
+ - cakoc614@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - piko-sharp-gem.gemspec
20
+ - power_assert-3.0.1/BSDL
21
+ - power_assert-3.0.1/COPYING
22
+ - power_assert-3.0.1/Gemfile
23
+ - power_assert-3.0.1/LEGAL
24
+ - power_assert-3.0.1/README.md
25
+ - power_assert-3.0.1/Rakefile
26
+ - power_assert-3.0.1/lib/power_assert.rb
27
+ - power_assert-3.0.1/lib/power_assert/colorize.rb
28
+ - power_assert-3.0.1/lib/power_assert/configuration.rb
29
+ - power_assert-3.0.1/lib/power_assert/context.rb
30
+ - power_assert-3.0.1/lib/power_assert/enable_tracepoint_events.rb
31
+ - power_assert-3.0.1/lib/power_assert/inspector.rb
32
+ - power_assert-3.0.1/lib/power_assert/parser.rb
33
+ - power_assert-3.0.1/lib/power_assert/version.rb
34
+ - power_assert-3.0.1/power_assert.gemspec
35
+ homepage: https://rubygems.org/profiles/Andrey78
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubygems_version: 3.6.2
54
+ specification_version: 4
55
+ summary: Research test
56
+ test_files: []