From 2bfd848c26360f1744f9cccf72cd03d069d7aa5f Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Tue, 3 Jan 2023 09:33:31 -0600 Subject: [PATCH 01/52] [DOC] Enhanced RDoc for common methods (#48) --- lib/uri/common.rb | 143 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 27 deletions(-) diff --git a/lib/uri/common.rb b/lib/uri/common.rb index ca38bec..ed786b9 100644 --- a/lib/uri/common.rb +++ b/lib/uri/common.rb @@ -314,17 +314,41 @@ def self.regexp(schemes = nil) TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze - # Encodes given +str+ to URL-encoded form data. + # Returns a URL-encoded string derived from the given string +str+. # - # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP - # (ASCII space) to + and converts others to %XX. + # The returned string: # - # If +enc+ is given, convert +str+ to the encoding before percent encoding. + # - Preserves: # - # This is an implementation of - # https://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data. + # - Characters '*', '.', '-', and '_'. + # - Character in ranges 'a'..'z', 'A'..'Z', + # and '0'..'9'. + # + # Example: + # + # URI.encode_www_form_component('*.-_azAZ09') + # # => "*.-_azAZ09" + # + # - Converts: + # + # - Character ' ' to character '+'. + # - Any other character to "percent notation"; + # the percent notation for character c is '%%%X' % c.ord. + # + # Example: + # + # URI.encode_www_form_component('Here are some punctuation characters: ,;?:') + # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A" + # + # Encoding: + # + # - If +str+ has encoding Encoding::ASCII_8BIT, argument +enc+ is ignored. + # - Otherwise +str+ is converted first to Encoding::UTF_8 + # (with suitable character replacements), + # and then to encoding +enc+. + # + # In either case, the returned string has forced encoding Encoding::US_ASCII. # - # See URI.decode_www_form_component, URI.encode_www_form. def self.encode_www_form_component(str, enc=nil) _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc) end @@ -372,33 +396,98 @@ def self._decode_uri_component(regexp, str, enc) end private_class_method :_decode_uri_component - # Generates URL-encoded form data from given +enum+. + # Returns a URL-encoded string derived from the given + # {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes] + # +enum+. # - # This generates application/x-www-form-urlencoded data defined in HTML5 - # from given an Enumerable object. + # The result is suitable for use as form data + # for an \HTTP request whose Content-Type is + # 'application/x-www-form-urlencoded'. # - # This internally uses URI.encode_www_form_component(str). + # The returned string consists of the elements of +enum+, + # each converted to one or more URL-encoded strings, + # and all joined with character '&'. # - # This method doesn't convert the encoding of given items, so convert them - # before calling this method if you want to send data as other than original - # encoding or mixed encoding data. (Strings which are encoded in an HTML5 - # ASCII incompatible encoding are converted to UTF-8.) + # Simple examples: # - # This method doesn't handle files. When you send a file, use - # multipart/form-data. + # URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]]) + # # => "foo=0&bar=1&baz=2" + # URI.encode_www_form({foo: 0, bar: 1, baz: 2}) + # # => "foo=0&bar=1&baz=2" # - # This refers https://url.spec.whatwg.org/#concept-urlencoded-serializer + # When +enum+ is Array-like, each element +ele+ is converted to a field: # - # URI.encode_www_form([["q", "ruby"], ["lang", "en"]]) - # #=> "q=ruby&lang=en" - # URI.encode_www_form("q" => "ruby", "lang" => "en") - # #=> "q=ruby&lang=en" - # URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en") - # #=> "q=ruby&q=perl&lang=en" - # URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]]) - # #=> "q=ruby&q=perl&lang=en" + # - If +ele+ is an array of two or more elements, + # the field is formed from its first two elements + # (and any additional elements are ignored): + # + # name = URI.encode_www_form_component(ele[0], enc) + # value = URI.encode_www_form_component(ele[1], enc) + # "#{name}=#{value}" + # + # Examples: + # + # URI.encode_www_form([%w[foo bar], %w[baz bat bah]]) + # # => "foo=bar&baz=bat" + # URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']]) + # # => "foo=0&bar=baz" + # + # - If +ele+ is an array of one element, + # the field is formed from ele[0]: + # + # URI.encode_www_form_component(ele[0]) + # + # Example: + # + # URI.encode_www_form([['foo'], [:bar], [0]]) + # # => "foo&bar&0" + # + # - Otherwise the field is formed from +ele+: + # + # URI.encode_www_form_component(ele) + # + # Example: + # + # URI.encode_www_form(['foo', :bar, 0]) + # # => "foo&bar&0" + # + # The elements of an Array-like +enum+ may be mixture: + # + # URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat]) + # # => "foo=0&bar=1&baz&bat" + # + # When +enum+ is Hash-like, + # each +key+/+value+ pair is converted to one or more fields: + # + # - If +value+ is + # {Array-convertible}[https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html#label-Array-Convertible+Objects], + # each element +ele+ in +value+ is paired with +key+ to form a field: + # + # name = URI.encode_www_form_component(key, enc) + # value = URI.encode_www_form_component(ele, enc) + # "#{name}=#{value}" + # + # Example: + # + # URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]}) + # # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2" + # + # - Otherwise, +key+ and +value+ are paired to form a field: + # + # name = URI.encode_www_form_component(key, enc) + # value = URI.encode_www_form_component(value, enc) + # "#{name}=#{value}" + # + # Example: + # + # URI.encode_www_form({foo: 0, bar: 1, baz: 2}) + # # => "foo=0&bar=1&baz=2" + # + # The elements of a Hash-like +enum+ may be mixture: + # + # URI.encode_www_form({foo: [0, 1], bar: 2}) + # # => "foo=0&foo=1&bar=2" # - # See URI.encode_www_form_component, URI.decode_www_form. def self.encode_www_form(enum, enc=nil) enum.map do |k,v| if v.nil? From 02dfc7936626e2c3a44182cdd9238ddeade75d73 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Wed, 4 Jan 2023 13:58:59 -0600 Subject: [PATCH 02/52] [DOC] Common methods rdoc (#49) --- lib/uri/common.rb | 124 ++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 66 deletions(-) diff --git a/lib/uri/common.rb b/lib/uri/common.rb index ed786b9..c1b75c1 100644 --- a/lib/uri/common.rb +++ b/lib/uri/common.rb @@ -68,16 +68,32 @@ module Schemes end private_constant :Schemes + # Registers the given +klass+ as the class to be instantiated + # when parsing a \URI with the given +scheme+: # - # Register the given +klass+ to be instantiated when parsing URLs with the given +scheme+. - # Note that currently only schemes which after .upcase are valid constant names - # can be registered (no -/+/. allowed). + # URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic + # URI.scheme_list['MS_SEARCH'] # => URI::Generic # + # Note that after calling String#upcase on +scheme+, it must be a valid + # constant name. def self.register_scheme(scheme, klass) Schemes.const_set(scheme.to_s.upcase, klass) end - # Returns a Hash of the defined schemes. + # Returns a hash of the defined schemes: + # + # URI.scheme_list + # # => + # {"MAILTO"=>URI::MailTo, + # "LDAPS"=>URI::LDAPS, + # "WS"=>URI::WS, + # "HTTP"=>URI::HTTP, + # "HTTPS"=>URI::HTTPS, + # "LDAP"=>URI::LDAP, + # "FILE"=>URI::File, + # "FTP"=>URI::FTP} + # + # Related: URI.register_scheme. def self.scheme_list Schemes.constants.map { |name| [name.to_s.upcase, Schemes.const_get(name)] @@ -88,9 +104,21 @@ def self.scheme_list private_constant :INITIAL_SCHEMES Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor) + # Returns a new object constructed from the given +scheme+, +arguments+, + # and +default+: + # + # - The new object is an instance of URI.scheme_list[scheme.upcase]. + # - The object is initialized by calling the class initializer + # using +scheme+ and +arguments+. + # See URI::Generic.new. # - # Construct a URI instance, using the scheme to detect the appropriate class - # from +URI.scheme_list+. + # Examples: + # + # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top'] + # URI.for('https', *values) + # # => # + # URI.for('foo', *values, default: URI::HTTP) + # # => # # def self.for(scheme, *arguments, default: Generic) const_name = scheme.to_s.upcase @@ -121,73 +149,37 @@ class InvalidComponentError < Error; end # class BadURIError < Error; end - # - # == Synopsis - # - # URI::split(uri) - # - # == Args - # - # +uri+:: - # String with URI. - # - # == Description - # - # Splits the string on following parts and returns array with result: - # - # * Scheme - # * Userinfo - # * Host - # * Port - # * Registry - # * Path - # * Opaque - # * Query - # * Fragment - # - # == Usage - # - # require 'uri' - # - # URI.split("http://www.ruby-lang.org/") - # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil] + # Returns a 9-element array representing the parts of the \URI + # formed from the string +uri+; + # each array element is a string or +nil+: + # + # names = %w[scheme userinfo host port registry path opaque query fragment] + # values = URI.split('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # names.zip(values) + # # => + # [["scheme", "https"], + # ["userinfo", "john.doe"], + # ["host", "www.example.com"], + # ["port", "123"], + # ["registry", nil], + # ["path", "/forum/questions/"], + # ["opaque", nil], + # ["query", "tag=networking&order=newest"], + # ["fragment", "top"]] # def self.split(uri) RFC3986_PARSER.split(uri) end + # Returns a new \URI object constructed from the given string +uri+: # - # == Synopsis - # - # URI::parse(uri_str) - # - # == Args - # - # +uri_str+:: - # String with URI. - # - # == Description - # - # Creates one of the URI's subclasses instance from the string. - # - # == Raises - # - # URI::InvalidURIError:: - # Raised if URI given is not a correct one. - # - # == Usage - # - # require 'uri' - # - # uri = URI.parse("http://www.ruby-lang.org/") - # # => # - # uri.scheme - # # => "http" - # uri.host - # # => "www.ruby-lang.org" + # URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # # => # + # URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top') + # # => # # - # It's recommended to first ::escape the provided +uri_str+ if there are any - # invalid URI characters. + # It's recommended to first ::escape string +uri+ + # if it may contain invalid URI characters. # def self.parse(uri) RFC3986_PARSER.parse(uri) From 7ff4fb372bc59ce8dace8992cffbedcda80cedd4 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Fri, 6 Jan 2023 08:20:31 -0600 Subject: [PATCH 03/52] [DOC] Enhanced RDoc for common methods (#50) --- lib/uri/common.rb | 54 +++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/lib/uri/common.rb b/lib/uri/common.rb index c1b75c1..54c4484 100644 --- a/lib/uri/common.rb +++ b/lib/uri/common.rb @@ -185,23 +185,13 @@ def self.parse(uri) RFC3986_PARSER.parse(uri) end + # Merges the given URI strings +str+ + # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html]. # - # == Synopsis - # - # URI::join(str[, str, ...]) - # - # == Args - # - # +str+:: - # String(s) to work with, will be converted to RFC3986 URIs before merging. - # - # == Description + # Each string in +str+ is converted to an + # {RFC3986 URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged. # - # Joins URIs. - # - # == Usage - # - # require 'uri' + # Examples: # # URI.join("http://example.com/","main.rbx") # # => # @@ -246,7 +236,7 @@ def self.join(*str) # URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") # # => ["http://foo.example.com/bla", "mailto:test@example.com"] # - def self.extract(str, schemes = nil, &block) + def self.extract(str, schemes = nil, &block) # :nodoc: warn "URI.extract is obsolete", uplevel: 1 if $VERBOSE DEFAULT_PARSER.extract(str, schemes, &block) end @@ -283,7 +273,7 @@ def self.extract(str, schemes = nil, &block) # p $& # end # - def self.regexp(schemes = nil) + def self.regexp(schemes = nil)# :nodoc: warn "URI.regexp is obsolete", uplevel: 1 if $VERBOSE DEFAULT_PARSER.make_regexp(schemes) end @@ -340,16 +330,38 @@ def self.regexp(schemes = nil) # and then to encoding +enc+. # # In either case, the returned string has forced encoding Encoding::US_ASCII. - # def self.encode_www_form_component(str, enc=nil) _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc) end - # Decodes given +str+ of URL-encoded form data. + # Returns a string decoded from the given \URL-encoded string +str+. + # + # The given string is first encoded as Encoding::ASCII-8BIT (using String#b), + # then decoded (as below), and finally force-encoded to the given encoding +enc+. + # + # The returned string: + # + # - Preserves: + # + # - Characters '*', '.', '-', and '_'. + # - Character in ranges 'a'..'z', 'A'..'Z', + # and '0'..'9'. + # + # Example: + # + # URI.decode_www_form_component('*.-_azAZ09') + # # => "*.-_azAZ09" + # + # - Converts: + # + # - Character '+' to character ' '. + # - Each "percent notation" to an ASCII character. + # + # Example: # - # This decodes + to SP. + # URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A') + # # => "Here are some punctuation characters: ,;?:" # - # See URI.encode_www_form_component, URI.decode_www_form. def self.decode_www_form_component(str, enc=Encoding::UTF_8) _decode_uri_component(/\+|%\h\h/, str, enc) end From 8184bc0da189ed1709c4fcd0a86bfad90202756d Mon Sep 17 00:00:00 2001 From: Ryunosuke Sato Date: Fri, 6 Jan 2023 23:56:36 +0900 Subject: [PATCH 04/52] Add Ruby 3.2 to CI matrix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1127858..19c0ed5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: name: build (${{ matrix.ruby }} / ${{ matrix.os }}) strategy: matrix: - ruby: [ 3.1, '3.0', 2.7, 2.6, 2.5, 2.4, head, truffleruby ] + ruby: [ 3.2, 3.1, '3.0', 2.7, 2.6, 2.5, 2.4, head, truffleruby ] os: [ ubuntu-latest, macos-latest ] runs-on: ${{ matrix.os }} steps: From be8047028f06a80e3958bcc554ef22c370ca8c96 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 7 Jan 2023 13:22:31 -0600 Subject: [PATCH 05/52] [DOC] Common rdoc (#52) --- lib/uri/common.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/uri/common.rb b/lib/uri/common.rb index 54c4484..ca7b686 100644 --- a/lib/uri/common.rb +++ b/lib/uri/common.rb @@ -330,6 +330,8 @@ def self.regexp(schemes = nil)# :nodoc: # and then to encoding +enc+. # # In either case, the returned string has forced encoding Encoding::US_ASCII. + # + # Related: URI.encode_uri_component (encodes ' ' as '%20'). def self.encode_www_form_component(str, enc=nil) _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc) end @@ -362,20 +364,18 @@ def self.encode_www_form_component(str, enc=nil) # URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A') # # => "Here are some punctuation characters: ,;?:" # + # Related: URI.decode_uri_component (preserves '+'). def self.decode_www_form_component(str, enc=Encoding::UTF_8) _decode_uri_component(/\+|%\h\h/, str, enc) end - # Encodes +str+ using URL encoding - # - # This encodes SP to %20 instead of +. + # Like URI.encode_www_form_component, except that ' ' (space) + # is encoded as '%20' (instead of '+'). def self.encode_uri_component(str, enc=nil) _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc) end - # Decodes given +str+ of URL-encoded data. - # - # This does not decode + to SP. + # Like URI.decode_www_form_component, except that '+' is preserved. def self.decode_uri_component(str, enc=Encoding::UTF_8) _decode_uri_component(/%\h\h/, str, enc) end @@ -419,6 +419,12 @@ def self._decode_uri_component(regexp, str, enc) # URI.encode_www_form({foo: 0, bar: 1, baz: 2}) # # => "foo=0&bar=1&baz=2" # + # The returned string is formed using method URI.encode_www_form_component, + # which converts certain characters: + # + # URI.encode_www_form('f#o': '/', 'b-r': '$') + # # => "f%23o=%2F&b-r=%24" + # # When +enum+ is Array-like, each element +ele+ is converted to a field: # # - If +ele+ is an array of two or more elements, From ce379e612561fff1dbf80272d87c440c50c6d19c Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sun, 8 Jan 2023 11:11:07 -0600 Subject: [PATCH 06/52] [DOC] Enhanced RDoc for URI.decode_www_form (#53) --- lib/uri/common.rb | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/lib/uri/common.rb b/lib/uri/common.rb index ca7b686..04baccb 100644 --- a/lib/uri/common.rb +++ b/lib/uri/common.rb @@ -422,8 +422,8 @@ def self._decode_uri_component(regexp, str, enc) # The returned string is formed using method URI.encode_www_form_component, # which converts certain characters: # - # URI.encode_www_form('f#o': '/', 'b-r': '$') - # # => "f%23o=%2F&b-r=%24" + # URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@') + # # => "f%23o=%2F&b-r=%24&b+z=%40" # # When +enum+ is Array-like, each element +ele+ is converted to a field: # @@ -518,22 +518,39 @@ def self.encode_www_form(enum, enc=nil) end.join('&') end - # Decodes URL-encoded form data from given +str+. + # Returns name/value pairs derived from the given string +str+, + # which must be an ASCII string. # - # This decodes application/x-www-form-urlencoded data - # and returns an array of key-value arrays. + # The method may be used to decode the body of Net::HTTPResponse object +res+ + # for which res['Content-Type'] is 'application/x-www-form-urlencoded'. # - # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser, - # so this supports only &-separator, and doesn't support ;-separator. + # The returned data is an array of 2-element subarrays; + # each subarray is a name/value pair (both are strings). + # Each returned string has encoding +enc+, + # and has had invalid characters removed via + # {String#scrub}[https://docs.ruby-lang.org/en/master/String.html#method-i-scrub]. # - # ary = URI.decode_www_form("a=1&a=2&b=3") - # ary #=> [['a', '1'], ['a', '2'], ['b', '3']] - # ary.assoc('a').last #=> '1' - # ary.assoc('b').last #=> '3' - # ary.rassoc('a').last #=> '2' - # Hash[ary] #=> {"a"=>"2", "b"=>"3"} + # A simple example: + # + # URI.decode_www_form('foo=0&bar=1&baz') + # # => [["foo", "0"], ["bar", "1"], ["baz", ""]] + # + # The returned strings have certain conversions, + # similar to those performed in URI.decode_www_form_component: + # + # URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40') + # # => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]] + # + # The given string may contain consecutive separators: + # + # URI.decode_www_form('foo=0&&bar=1&&baz=2') + # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]] + # + # A different separator may be specified: + # + # URI.decode_www_form('foo=0--bar=1--baz', separator: '--') + # # => [["foo", "0"], ["bar", "1"], ["baz", ""]] # - # See URI.decode_www_form_component, URI.encode_www_form. def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false) raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only? ary = [] From 89ab4f14072ac47c5398692b6bfcd8752e41f292 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sun, 8 Jan 2023 17:14:44 -0600 Subject: [PATCH 07/52] [DOC] Enhanced RDoc for URI (#55) --- lib/uri/common.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/uri/common.rb b/lib/uri/common.rb index 04baccb..faf75f0 100644 --- a/lib/uri/common.rb +++ b/lib/uri/common.rb @@ -829,7 +829,15 @@ def self.get_encoding(label) module Kernel # - # Returns +uri+ converted to an URI object. + # Returns a \URI object derived from the given +uri+, + # which may be a \URI string or an existing \URI object: + # + # # Returns a new URI. + # uri = URI('http://github.com/ruby/ruby') + # # => # + # # Returns the given URI. + # URI(uri) + # # => # # def URI(uri) if uri.is_a?(URI::Generic) From 9bf071a387fa74371acda35e8ff221a2de3bbf74 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 26 Feb 2023 03:34:55 +0300 Subject: [PATCH 08/52] Add documentation link into README with shields.io badge https://github.com/docmeta/rubydoc.info/issues/61#issuecomment-58888729 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 06013eb..6cece4f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # URI [![CI](https://github.com/ruby/uri/actions/workflows/test.yml/badge.svg)](https://github.com/ruby/uri/actions/workflows/test.yml) +[![Yard Docs](https://img.shields.io/badge/docs-exist-blue.svg)](https://rubydoc.info/gems/uri) URI is a module providing classes to handle Uniform Resource Identifiers [RFC2396](http://tools.ietf.org/html/rfc2396). From 19ced145f4c0e3d7a944dd92160415b202b59603 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 26 Feb 2023 03:35:24 +0300 Subject: [PATCH 09/52] Add documentation link into gemspec --- uri.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/uri.gemspec b/uri.gemspec index 584a4fa..1c90083 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -19,6 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage + spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/uri" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. From 19a19ccde6fbd9fae909635c1cc599c2b4302662 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 26 Feb 2023 03:36:34 +0300 Subject: [PATCH 10/52] Remake `metadata` object in `gemspec` into one assignment --- uri.gemspec | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/uri.gemspec b/uri.gemspec index 1c90083..95c128d 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -17,9 +17,11 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.4' - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = spec.homepage - spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/uri" + spec.metadata = { + "homepage_uri" => spec.homepage, + "source_code_uri" => spec.homepage, + "documentation_uri" => "https://rubydoc.info/gems/uri" + } # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. From 1e9420b57c91cd1d0671e67940d7c5c7ea6d6c09 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 26 Feb 2023 03:37:15 +0300 Subject: [PATCH 11/52] Sort gemspec `metadata` object alphabetical --- uri.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uri.gemspec b/uri.gemspec index 95c128d..e590448 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -18,9 +18,9 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.4' spec.metadata = { + "documentation_uri" => "https://rubydoc.info/gems/uri", "homepage_uri" => spec.homepage, - "source_code_uri" => spec.homepage, - "documentation_uri" => "https://rubydoc.info/gems/uri" + "source_code_uri" => spec.homepage } # Specify which files should be added to the gem when it is released. From 31748915d0935d9e2940ef8bc055a55e767502f1 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 26 Feb 2023 03:38:51 +0300 Subject: [PATCH 12/52] Populate gemspec metadata object --- uri.gemspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uri.gemspec b/uri.gemspec index e590448..ce5dfd7 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -18,6 +18,8 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.4' spec.metadata = { + "bug_tracker_uri" => "https://github.com/ruby/uri/issues", + "changelog_uri" => "https://github.com/ruby/uri/releases", "documentation_uri" => "https://rubydoc.info/gems/uri", "homepage_uri" => spec.homepage, "source_code_uri" => spec.homepage From ca4638a4b31d3e456a1a24509de071257efd13dd Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sun, 26 Feb 2023 03:40:26 +0300 Subject: [PATCH 13/52] Take out GitHub link for gemspec metadata --- uri.gemspec | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/uri.gemspec b/uri.gemspec index ce5dfd7..6e77f10 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -12,17 +12,20 @@ Gem::Specification.new do |spec| spec.summary = %q{URI is a module providing classes to handle Uniform Resource Identifiers} spec.description = spec.summary - spec.homepage = "https://github.com/ruby/uri" + + github_link = "https://github.com/ruby/uri" + + spec.homepage = github_link spec.licenses = ["Ruby", "BSD-2-Clause"] spec.required_ruby_version = '>= 2.4' spec.metadata = { - "bug_tracker_uri" => "https://github.com/ruby/uri/issues", - "changelog_uri" => "https://github.com/ruby/uri/releases", + "bug_tracker_uri" => "#{github_link}/issues", + "changelog_uri" => "#{github_link}/releases", "documentation_uri" => "https://rubydoc.info/gems/uri", "homepage_uri" => spec.homepage, - "source_code_uri" => spec.homepage + "source_code_uri" => github_link } # Specify which files should be added to the gem when it is released. From ac59a8fbd653b4364a0e885a67b1d622f0665035 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 1 Mar 2023 14:33:10 +0900 Subject: [PATCH 14/52] Generate rdoc document by GitHub Pages Action --- .github/workflows/gh-pages.yml | 46 ++++++++++++++++++++++++++++++++++ .gitignore | 1 + Rakefile | 8 ++++++ 3 files changed, 55 insertions(+) create mode 100644 .github/workflows/gh-pages.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..1828e03 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,46 @@ +name: Deploy RDoc site to Pages + +on: + push: + branches: [ 'master' ] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Ruby + uses: ruby/setup-ruby@92aece5fc9c784ab66851c1e702b1bd5885a51f2 # v1.139.0 + with: + ruby-version: '3.2' + bundler-cache: true + - name: Setup Pages + id: pages + uses: actions/configure-pages@v3 + - name: Build with RDoc + # Outputs to the './_site' directory by default + run: bundle exec rake rdoc + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore index 4ea5798..c2ffa08 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /spec/reports/ /tmp/ Gemfile.lock +/_site diff --git a/Rakefile b/Rakefile index d81dd1f..38ddcc6 100644 --- a/Rakefile +++ b/Rakefile @@ -7,6 +7,14 @@ Rake::TestTask.new(:test) do |t| t.test_files = FileList["test/**/test_*.rb"] end +require "rdoc/task" +RDoc::Task.new do |doc| + doc.main = "README.md" + doc.title = "URI - handle Uniform Resource Identifiers" + doc.rdoc_files = FileList.new %w[lib LICENSE.txt] + doc.rdoc_dir = "_site" # for github pages +end + task :sync_tool do require 'fileutils' FileUtils.cp "../ruby/tool/lib/test/unit/core_assertions.rb", "./test/lib" From a7150f44e8315af4198a562a5b0b634e64ed7104 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 1 Mar 2023 14:46:51 +0900 Subject: [PATCH 15/52] Added missing README.md into file list of rdoc --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 38ddcc6..5a81147 100644 --- a/Rakefile +++ b/Rakefile @@ -11,7 +11,7 @@ require "rdoc/task" RDoc::Task.new do |doc| doc.main = "README.md" doc.title = "URI - handle Uniform Resource Identifiers" - doc.rdoc_files = FileList.new %w[lib LICENSE.txt] + doc.rdoc_files = FileList.new %w[lib README.md LICENSE.txt] doc.rdoc_dir = "_site" # for github pages end From c272f205f91e506231747ab50b87100ab2b022de Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 1 Mar 2023 19:18:14 +0300 Subject: [PATCH 16/52] Replace RubyDoc.info links with Ruby.GitHub.io As requested. --- README.md | 2 +- uri.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6cece4f..3775f3b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # URI [![CI](https://github.com/ruby/uri/actions/workflows/test.yml/badge.svg)](https://github.com/ruby/uri/actions/workflows/test.yml) -[![Yard Docs](https://img.shields.io/badge/docs-exist-blue.svg)](https://rubydoc.info/gems/uri) +[![Yard Docs](https://img.shields.io/badge/docs-exist-blue.svg)](https://ruby.github.io/uri/) URI is a module providing classes to handle Uniform Resource Identifiers [RFC2396](http://tools.ietf.org/html/rfc2396). diff --git a/uri.gemspec b/uri.gemspec index 6e77f10..57c3239 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| spec.metadata = { "bug_tracker_uri" => "#{github_link}/issues", "changelog_uri" => "#{github_link}/releases", - "documentation_uri" => "https://rubydoc.info/gems/uri", + "documentation_uri" => "https://ruby.github.io/uri/", "homepage_uri" => spec.homepage, "source_code_uri" => github_link } From e8ab63880512701516a7febac2d20c098263c871 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 01:21:24 +0000 Subject: [PATCH 17/52] Bump ruby/setup-ruby from 1.139.0 to 1.144.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.139.0 to 1.144.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/92aece5fc9c784ab66851c1e702b1bd5885a51f2...9669f3ee51dc3f4eda8447ab696b3ab19a90d14b) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 1828e03..0962d00 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@92aece5fc9c784ab66851c1e702b1bd5885a51f2 # v1.139.0 + uses: ruby/setup-ruby@9669f3ee51dc3f4eda8447ab696b3ab19a90d14b # v1.144.0 with: ruby-version: '3.2' bundler-cache: true From 70ba76f73a6d9808160c76ddde33d5f0a1f66e9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 01:18:24 +0000 Subject: [PATCH 18/52] Bump actions/deploy-pages from 1 to 2 Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 1 to 2. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v1...v2) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 0962d00..764b41a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -43,4 +43,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v2 From c196f992ab80322324f74f6615e47388c18b7b0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 01:18:31 +0000 Subject: [PATCH 19/52] Bump ruby/setup-ruby from 1.144.0 to 1.144.1 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.144.0 to 1.144.1. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/9669f3ee51dc3f4eda8447ab696b3ab19a90d14b...e6689b4deb1cb2062ea45315001f687c0b52111b) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 0962d00..9574824 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@9669f3ee51dc3f4eda8447ab696b3ab19a90d14b # v1.144.0 + uses: ruby/setup-ruby@e6689b4deb1cb2062ea45315001f687c0b52111b # v1.144.1 with: ruby-version: '3.2' bundler-cache: true From eedb6a68fba9aeb50145091bc951644dcd5809a7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 15 Mar 2023 17:53:00 +0900 Subject: [PATCH 20/52] Update core_assertions.rb --- test/lib/core_assertions.rb | 196 ++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 87 deletions(-) diff --git a/test/lib/core_assertions.rb b/test/lib/core_assertions.rb index 30d684e..1fdc0a3 100644 --- a/test/lib/core_assertions.rb +++ b/test/lib/core_assertions.rb @@ -3,6 +3,10 @@ module Test module Unit module Assertions + def assert_raises(*exp, &b) + raise NoMethodError, "use assert_raise", caller + end + def _assertions= n # :nodoc: @_assertions = n end @@ -16,31 +20,24 @@ def _assertions # :nodoc: def message msg = nil, ending = nil, &default proc { - msg = msg.call.chomp(".") if Proc === msg - custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? - "#{custom_message}#{default.call}#{ending || "."}" + ending ||= (ending_pattern = /(? e - bt = e.backtrace - as = e.instance_of?(MiniTest::Assertion) - if as - ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o - bt.reject! {|ln| ans =~ ln} - end - if ((args.empty? && !as) || - args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a }) - msg = message(msg) { - "Exception raised:\n<#{mu_pp(e)}>\n" + - "Backtrace:\n" + - e.backtrace.map{|frame| " #{frame}"}.join("\n") - } - raise MiniTest::Assertion, msg.call, bt - else - raise - end + rescue *(args.empty? ? Exception : args) => e + msg = message(msg) { + "Exception raised:\n<#{mu_pp(e)}>\n""Backtrace:\n" << + Test.filter_backtrace(e.backtrace).map{|frame| " #{frame}"}.join("\n") + } + raise Test::Unit::AssertionFailedError, msg.call, e.backtrace end end @@ -259,13 +244,17 @@ def assert_ruby_status(args, test_stdin="", message=nil, **opt) ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") - def separated_runner(out = nil) + def separated_runner(token, out = nil) include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) }) out = out ? IO.new(out, 'w') : STDOUT at_exit { - out.puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}" + out.puts "#{token}", [Marshal.dump($!)].pack('m'), "#{token}", "#{token}assertions=#{self._assertions}" } - Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) if defined?(Test::Unit::Runner) + if defined?(Test::Unit::Runner) + Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) + elsif defined?(Test::Unit::AutoRunner) + Test::Unit::AutoRunner.need_auto_run = false + end end def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) @@ -275,22 +264,24 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o line ||= loc.lineno end capture_stdout = true - unless /mswin|mingw/ =~ RUBY_PLATFORM + unless /mswin|mingw/ =~ RbConfig::CONFIG['host_os'] capture_stdout = false - opt[:out] = MiniTest::Unit.output if defined?(MiniTest::Unit) + opt[:out] = Test::Unit::Runner.output if defined?(Test::Unit::Runner) res_p, res_c = IO.pipe opt[:ios] = [res_c] end + token_dump, token_re = new_test_token src = <\n\K.*\n(?=#{token_re}<\/error>$)/m].unpack1("m")) rescue => marshal_error ignore_stderr = nil res = nil @@ -402,8 +393,8 @@ def assert_raise(*exp, &b) begin yield - rescue MiniTest::Skip => e - return e if exp.include? MiniTest::Skip + rescue Test::Unit::PendedError => e + return e if exp.include? Test::Unit::PendedError raise e rescue Exception => e expected = exp.any? { |ex| @@ -478,7 +469,7 @@ def assert_raise_with_message(exception, expected, msg = nil, &block) ex end - MINI_DIR = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), "minitest") #:nodoc: + TEST_DIR = File.join(__dir__, "test/unit") #:nodoc: # :call-seq: # assert(test, [failure_message]) @@ -498,7 +489,7 @@ def assert(test, *msgs) when nil msgs.shift else - bt = caller.reject { |s| s.start_with?(MINI_DIR) } + bt = caller.reject { |s| s.start_with?(TEST_DIR) } raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt end unless msgs.empty? super @@ -521,7 +512,7 @@ def assert_respond_to(obj, (meth, *priv), msg = nil) return assert obj.respond_to?(meth, *priv), msg end #get rid of overcounting - if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) + if caller_locations(1, 1)[0].path.start_with?(TEST_DIR) return if obj.respond_to?(meth) end super(obj, meth, msg) @@ -544,17 +535,17 @@ def assert_not_respond_to(obj, (meth, *priv), msg = nil) return assert !obj.respond_to?(meth, *priv), msg end #get rid of overcounting - if caller_locations(1, 1)[0].path.start_with?(MINI_DIR) + if caller_locations(1, 1)[0].path.start_with?(TEST_DIR) return unless obj.respond_to?(meth) end refute_respond_to(obj, meth, msg) end - # pattern_list is an array which contains regexp and :*. + # pattern_list is an array which contains regexp, string and :*. # :* means any sequence. # # pattern_list is anchored. - # Use [:*, regexp, :*] for non-anchored match. + # Use [:*, regexp/string, :*] for non-anchored match. def assert_pattern_list(pattern_list, actual, message=nil) rest = actual anchored = true @@ -563,11 +554,13 @@ def assert_pattern_list(pattern_list, actual, message=nil) anchored = false else if anchored - match = /\A#{pattern}/.match(rest) + match = rest.rindex(pattern, 0) else - match = pattern.match(rest) + match = rest.index(pattern) end - unless match + if match + post_match = $~ ? $~.post_match : rest[match+pattern.size..-1] + else msg = message(msg) { expect_msg = "Expected #{mu_pp pattern}\n" if /\n[^\n]/ =~ rest @@ -584,7 +577,7 @@ def assert_pattern_list(pattern_list, actual, message=nil) } assert false, msg end - rest = match.post_match + rest = post_match anchored = true end } @@ -611,19 +604,20 @@ def assert_warn(*args) def assert_deprecated_warning(mesg = /deprecated/) assert_warning(mesg) do - Warning[:deprecated] = true + Warning[:deprecated] = true if Warning.respond_to?(:[]=) yield end end def assert_deprecated_warn(mesg = /deprecated/) assert_warn(mesg) do - Warning[:deprecated] = true + Warning[:deprecated] = true if Warning.respond_to?(:[]=) yield end end class << (AssertFile = Struct.new(:failure_message).new) + include Assertions include CoreAssertions def assert_file_predicate(predicate, *args) if /\Anot_/ =~ predicate @@ -655,7 +649,7 @@ def initialize def for(key) @count += 1 - yield + yield key rescue Exception => e @failures[key] = [@count, e] end @@ -709,12 +703,12 @@ def assert_join_threads(threads, message = nil) msg = "exceptions on #{errs.length} threads:\n" + errs.map {|t, err| "#{t.inspect}:\n" + - RUBY_VERSION >= "2.5.0" ? err.full_message(highlight: false, order: :top) : err.message + (err.respond_to?(:full_message) ? err.full_message(highlight: false, order: :top) : err.message) }.join("\n---\n") if message msg = "#{message}\n#{msg}" end - raise MiniTest::Assertion, msg + raise Test::Unit::AssertionFailedError, msg end end @@ -736,21 +730,44 @@ def assert_all_assertions(msg = nil) end alias all_assertions assert_all_assertions - def message(msg = nil, *args, &default) # :nodoc: - if Proc === msg - super(nil, *args) do - ary = [msg.call, (default.call if default)].compact.reject(&:empty?) - if 1 < ary.length - ary[0...-1] = ary[0...-1].map {|str| str.sub(/(?(n) {n}) + first = seq.first + *arg = pre.call(first) + times = (0..(rehearsal || (2 * first))).map do + st = Process.clock_gettime(Process::CLOCK_MONOTONIC) + yield(*arg) + t = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st) + assert_operator 0, :<=, t + t.nonzero? + end + times.compact! + tmin, tmax = times.minmax + tmax *= tmax / tmin + tmax = 10**Math.log10(tmax).ceil + + seq.each do |i| + next if i == first + t = tmax * i.fdiv(first) + *arg = pre.call(i) + message = "[#{i}]: in #{t}s" + Timeout.timeout(t, Timeout::Error, message) do + st = Process.clock_gettime(Process::CLOCK_MONOTONIC) + yield(*arg) + assert_operator (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st), :<=, t, message end - else - super end end @@ -769,6 +786,11 @@ def diff(exp, act) end q.output end + + def new_test_token + token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m" + return token.dump, Regexp.quote(token) + end end end end From 7795a7c68149e3165fd1c692cc7a218e7f3ef47d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 24 Mar 2023 12:49:28 +0900 Subject: [PATCH 21/52] Update test libraries from https://github.com/ruby/ruby/commit/b4e438d8aabaf4bba2b27f374c787543fae07c58 --- test/lib/envutil.rb | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/test/lib/envutil.rb b/test/lib/envutil.rb index 0391b90..728ca70 100644 --- a/test/lib/envutil.rb +++ b/test/lib/envutil.rb @@ -152,7 +152,12 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = if RUBYLIB and lib = child_env["RUBYLIB"] child_env["RUBYLIB"] = [lib, RUBYLIB].join(File::PATH_SEPARATOR) end - child_env['ASAN_OPTIONS'] = ENV['ASAN_OPTIONS'] if ENV['ASAN_OPTIONS'] + + # remain env + %w(ASAN_OPTIONS RUBY_ON_BUG).each{|name| + child_env[name] = ENV[name] if ENV[name] + } + args = [args] if args.kind_of?(String) pid = spawn(child_env, *precommand, rubybin, *args, opt) in_c.close @@ -292,16 +297,24 @@ def self.diagnostic_reports(signame, pid, now) cmd = @ruby_install_name if "ruby-runner#{RbConfig::CONFIG["EXEEXT"]}" == cmd path = DIAGNOSTIC_REPORTS_PATH timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT - pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash" + pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.{crash,ips}" first = true 30.times do first ? (first = false) : sleep(0.1) Dir.glob(pat) do |name| log = File.read(name) rescue next - if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log - File.unlink(name) - File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil - return log + case name + when /\.crash\z/ + if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log + File.unlink(name) + File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil + return log + end + when /\.ips\z/ + if /^ *"pid" *: *#{pid},/ =~ log + File.unlink(name) + return log + end end end end From 17562a7ae6109065a121538280a2087161f84582 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 01:18:23 +0000 Subject: [PATCH 22/52] Bump ruby/setup-ruby from 1.144.1 to 1.144.2 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.144.1 to 1.144.2. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/e6689b4deb1cb2062ea45315001f687c0b52111b...ec02537da5712d66d4d50a0f33b7eb52773b5ed1) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 9769340..fb1f4fb 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@e6689b4deb1cb2062ea45315001f687c0b52111b # v1.144.1 + uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 # v1.144.2 with: ruby-version: '3.2' bundler-cache: true From 54abaa739b076a3f43ed4fab4367701ad1d6f88f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Jan 2022 01:12:57 +0900 Subject: [PATCH 23/52] Test for quadratic backtracking on invalid URI https://hackerone.com/reports/1444501 --- test/uri/test_common.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb index 038f483..245b3d8 100644 --- a/test/uri/test_common.rb +++ b/test/uri/test_common.rb @@ -112,6 +112,17 @@ def test_kernel_uri assert_raise(NoMethodError) { Object.new.URI("http://www.ruby-lang.org/") } end + def test_parse_timeout + pre = ->(n) { + 'https://example.com/dir/' + 'a' * (n * 100) + '/##.jpg' + } + assert_linear_performance((1..10).map {|i| i * 100}, pre: pre) do |uri| + assert_raise(URI::InvalidURIError) do + URI.parse(uri) + end + end + end + def test_encode_www_form_component assert_equal("%00+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40" \ "AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E", From 2f9585de17e919f35e16e12ab67e681d933cf570 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Jan 2022 01:12:57 +0900 Subject: [PATCH 24/52] Fix quadratic backtracking on invalid URI https://hackerone.com/reports/1444501 --- lib/uri/rfc3986_parser.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index f3816d9..dd24a40 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -2,8 +2,8 @@ module URI class RFC3986_Parser # :nodoc: # URI defined in RFC3986 - RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?\g(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ - RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ + RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*+):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?\d*+))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g)*+)?)|(?\g(?:\/\g)*+)|(?))(?:\?(?[^#]*+))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ + RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?\d*+))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g)*+)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g)*+)|(?))(?:\?(?[^#]*+))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ attr_reader :regexp def initialize From 5c4f7d04bf81f31f2767a75c52d87b27910b4ad8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 24 Mar 2023 16:44:55 +0900 Subject: [PATCH 25/52] Bump version to 0.12.1 --- lib/uri/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/uri/version.rb b/lib/uri/version.rb index a9643ef..7497a7d 100644 --- a/lib/uri/version.rb +++ b/lib/uri/version.rb @@ -1,6 +1,6 @@ module URI # :stopdoc: - VERSION_CODE = '001200'.freeze + VERSION_CODE = '001201'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end From 5037c981bb8dcb14e67538bb0d3cc0306bd43a0b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 28 Mar 2023 12:31:23 +0900 Subject: [PATCH 26/52] Switch to use ruby_versions callable workflow --- .github/workflows/test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19c0ed5..5b58a3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,11 +3,17 @@ name: CI on: [push, pull_request] jobs: + ruby-versions: + uses: ruby/actions/.github/workflows/ruby_versions.yml@master + with: + min_version: 2.4 + build: + needs: ruby-versions name: build (${{ matrix.ruby }} / ${{ matrix.os }}) strategy: matrix: - ruby: [ 3.2, 3.1, '3.0', 2.7, 2.6, 2.5, 2.4, head, truffleruby ] + ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} os: [ ubuntu-latest, macos-latest ] runs-on: ${{ matrix.os }} steps: From 09c4cc6e612ae4708c68c915b380dae53fe6a329 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 01:15:39 +0000 Subject: [PATCH 27/52] Bump ruby/setup-ruby from 1.144.2 to 1.145.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.144.2 to 1.145.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/ec02537da5712d66d4d50a0f33b7eb52773b5ed1...904f3fef85a9c80a3750cbe7d5159268fd5caa9f) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index fb1f4fb..3b9fa69 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 # v1.144.2 + uses: ruby/setup-ruby@904f3fef85a9c80a3750cbe7d5159268fd5caa9f # v1.145.0 with: ruby-version: '3.2' bundler-cache: true From dbc4154770c10b182e75215ef611769b41e0a9b6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 29 Mar 2023 09:52:33 +0900 Subject: [PATCH 28/52] [ruby/uri] Increase rehearsals --- test/uri/test_common.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb index 245b3d8..77bdab6 100644 --- a/test/uri/test_common.rb +++ b/test/uri/test_common.rb @@ -116,7 +116,7 @@ def test_parse_timeout pre = ->(n) { 'https://example.com/dir/' + 'a' * (n * 100) + '/##.jpg' } - assert_linear_performance((1..10).map {|i| i * 100}, pre: pre) do |uri| + assert_linear_performance((1..10).map {|i| i * 100}, rehearsal: 1000, pre: pre) do |uri| assert_raise(URI::InvalidURIError) do URI.parse(uri) end From 95c1a37058e07ff156dca284d5eb4dcdaa04fdbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 01:03:18 +0000 Subject: [PATCH 29/52] Bump ruby/setup-ruby from 1.145.0 to 1.146.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.145.0 to 1.146.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/904f3fef85a9c80a3750cbe7d5159268fd5caa9f...55283cc23133118229fd3f97f9336ee23a179fcf) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 3b9fa69..3fb0c68 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@904f3fef85a9c80a3750cbe7d5159268fd5caa9f # v1.145.0 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 with: ruby-version: '3.2' bundler-cache: true From 6ac566e4de789b2f67d72fc49617374c20db0c5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 01:03:13 +0000 Subject: [PATCH 30/52] Bump ruby/setup-ruby from 1.146.0 to 1.148.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.146.0 to 1.148.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/55283cc23133118229fd3f97f9336ee23a179fcf...d2b39ad0b52eca07d23f3aa14fdf2a3fcc1f411c) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 3fb0c68..a1537db 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + uses: ruby/setup-ruby@d2b39ad0b52eca07d23f3aa14fdf2a3fcc1f411c # v1.148.0 with: ruby-version: '3.2' bundler-cache: true From cfbeade935bf0d2c277f8e4f406254480a9b818a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 4 May 2023 22:46:20 +0900 Subject: [PATCH 31/52] Increase repeat orders of magnitude --- test/uri/test_common.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb index 77bdab6..1327704 100644 --- a/test/uri/test_common.rb +++ b/test/uri/test_common.rb @@ -116,7 +116,7 @@ def test_parse_timeout pre = ->(n) { 'https://example.com/dir/' + 'a' * (n * 100) + '/##.jpg' } - assert_linear_performance((1..10).map {|i| i * 100}, rehearsal: 1000, pre: pre) do |uri| + assert_linear_performance((1..3).map {|i| 10**i}, rehearsal: 1000, pre: pre) do |uri| assert_raise(URI::InvalidURIError) do URI.parse(uri) end From aaa22a24433b4b217e97ab1ef6231a4aefd8a024 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 4 May 2023 22:56:27 +0900 Subject: [PATCH 32/52] Define test cases as qualified class names --- test/uri/test_common.rb | 8 +------- test/uri/test_ftp.rb | 10 ++-------- test/uri/test_http.rb | 8 +------- test/uri/test_ldap.rb | 8 +------- test/uri/test_ws.rb | 8 +------- test/uri/test_wss.rb | 8 +------- 6 files changed, 7 insertions(+), 43 deletions(-) diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb index 1327704..1df19e6 100644 --- a/test/uri/test_common.rb +++ b/test/uri/test_common.rb @@ -3,10 +3,7 @@ require 'envutil' require 'uri' -module URI - - -class TestCommon < Test::Unit::TestCase +class URI::TestCommon < Test::Unit::TestCase def setup end @@ -292,6 +289,3 @@ def test_decode_www_form private def s(str) str.force_encoding(Encoding::Windows_31J); end end - - -end diff --git a/test/uri/test_ftp.rb b/test/uri/test_ftp.rb index 0eec984..f45bb06 100644 --- a/test/uri/test_ftp.rb +++ b/test/uri/test_ftp.rb @@ -2,10 +2,7 @@ require 'test/unit' require 'uri/ftp' -module URI - - -class TestFTP < Test::Unit::TestCase +class URI::TestFTP < Test::Unit::TestCase def setup end @@ -29,7 +26,7 @@ def test_parse end def test_parse_invalid - assert_raise(InvalidURIError){URI.parse('ftp:example')} + assert_raise(URI::InvalidURIError) {URI.parse('ftp:example')} end def test_paths @@ -62,6 +59,3 @@ def test_select end end end - - -end diff --git a/test/uri/test_http.rb b/test/uri/test_http.rb index 748e90a..e937b1a 100644 --- a/test/uri/test_http.rb +++ b/test/uri/test_http.rb @@ -3,10 +3,7 @@ require 'uri/http' require 'uri/https' -module URI - - -class TestHTTP < Test::Unit::TestCase +class URI::TestHTTP < Test::Unit::TestCase def setup end @@ -82,6 +79,3 @@ def test_origin assert_equal('https://a.b.c', URI.parse('https://a.b.c/').origin) end end - - -end diff --git a/test/uri/test_ldap.rb b/test/uri/test_ldap.rb index 2625b24..9c4506a 100644 --- a/test/uri/test_ldap.rb +++ b/test/uri/test_ldap.rb @@ -2,10 +2,7 @@ require 'test/unit' require 'uri/ldap' -module URI - - -class TestLDAP < Test::Unit::TestCase +class URI::TestLDAP < Test::Unit::TestCase def setup end @@ -100,6 +97,3 @@ def test_parse_invalid_uri assert_raise(URI::InvalidURIError) {URI.parse("ldap:https://example.com")} end end - - -end diff --git a/test/uri/test_ws.rb b/test/uri/test_ws.rb index 17acb0d..f3918f6 100644 --- a/test/uri/test_ws.rb +++ b/test/uri/test_ws.rb @@ -3,10 +3,7 @@ require 'uri/http' require 'uri/ws' -module URI - - -class TestWS < Test::Unit::TestCase +class URI::TestWS < Test::Unit::TestCase def setup end @@ -66,6 +63,3 @@ def test_select end end end - - -end diff --git a/test/uri/test_wss.rb b/test/uri/test_wss.rb index 2e8b9bc..13a2583 100644 --- a/test/uri/test_wss.rb +++ b/test/uri/test_wss.rb @@ -3,10 +3,7 @@ require 'uri/https' require 'uri/wss' -module URI - - -class TestWSS < Test::Unit::TestCase +class URI::TestWSS < Test::Unit::TestCase def setup end @@ -66,6 +63,3 @@ def test_select end end end - - -end From d962515151699cee4a1bde0abb29aec969626f19 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 5 May 2023 18:32:13 +0900 Subject: [PATCH 33/52] Redirect to `IO::NULL` for the portability --- uri.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uri.gemspec b/uri.gemspec index 57c3239..7d4b48b 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } From 9b6e382477e11538b32791035829fddd0d9de4fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 01:13:58 +0000 Subject: [PATCH 34/52] Bump ruby/setup-ruby from 1.148.0 to 1.149.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.148.0 to 1.149.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/d2b39ad0b52eca07d23f3aa14fdf2a3fcc1f411c...7d546f4868fb108ed378764d873683f920672ae2) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index a1537db..76d5f24 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@d2b39ad0b52eca07d23f3aa14fdf2a3fcc1f411c # v1.148.0 + uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # v1.149.0 with: ruby-version: '3.2' bundler-cache: true From 2539f32f9a2db870e4f73941e980c7431cc70ea1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 01:13:16 +0000 Subject: [PATCH 35/52] Bump ruby/setup-ruby from 1.149.0 to 1.150.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.149.0 to 1.150.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/7d546f4868fb108ed378764d873683f920672ae2...8a45918450651f5e4784b6031db26f4b9f76b251) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 76d5f24..f381ba0 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@7d546f4868fb108ed378764d873683f920672ae2 # v1.149.0 + uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251 # v1.150.0 with: ruby-version: '3.2' bundler-cache: true From 21baf2ba16a177fd8e63cdd5d03c369f76162a1d Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 1 Jun 2023 22:15:54 +0900 Subject: [PATCH 36/52] Drop support for 2.4 --- .github/workflows/test.yml | 2 +- uri.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b58a3b..2a6937a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ jobs: ruby-versions: uses: ruby/actions/.github/workflows/ruby_versions.yml@master with: - min_version: 2.4 + min_version: 2.5 build: needs: ruby-versions diff --git a/uri.gemspec b/uri.gemspec index 7d4b48b..9cf0a71 100644 --- a/uri.gemspec +++ b/uri.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |spec| spec.homepage = github_link spec.licenses = ["Ruby", "BSD-2-Clause"] - spec.required_ruby_version = '>= 2.4' + spec.required_ruby_version = '>= 2.5' spec.metadata = { "bug_tracker_uri" => "#{github_link}/issues", From af2c3fcc8b201d4ad2f72376fde7b30719a76cca Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 2 Jun 2023 19:02:30 +0900 Subject: [PATCH 37/52] Update test libraries from ruby/ruby 2023-06-02 From https://github.com/ruby/ruby/commit/21b61b21c29d81204ad7d6135005126dd9b5dd7e --- Rakefile | 7 ------ rakelib/sync_tool.rake | 17 ++++++++++++++ test/lib/core_assertions.rb | 45 +++++++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 rakelib/sync_tool.rake diff --git a/Rakefile b/Rakefile index 5a81147..19de93e 100644 --- a/Rakefile +++ b/Rakefile @@ -15,11 +15,4 @@ RDoc::Task.new do |doc| doc.rdoc_dir = "_site" # for github pages end -task :sync_tool do - require 'fileutils' - FileUtils.cp "../ruby/tool/lib/test/unit/core_assertions.rb", "./test/lib" - FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib" - FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib" -end - task :default => :test diff --git a/rakelib/sync_tool.rake b/rakelib/sync_tool.rake new file mode 100644 index 0000000..8ea8cb0 --- /dev/null +++ b/rakelib/sync_tool.rake @@ -0,0 +1,17 @@ +task :sync_tool, [:from] do |t, from: nil| + from ||= (File.identical?(__dir__, "rakelib") ? "../ruby/tool" : File.dirname(__dir__)) + + require 'fileutils' + + { + "rakelib/sync_tool.rake" => "rakelib", + "lib/core_assertions.rb" => "test/lib", + "lib/envutil.rb" => "test/lib", + "lib/find_executable.rb" => "test/lib", + "lib/helper.rb" => "test/lib", + }.each do |src, dest| + FileUtils.mkpath(dest) + FileUtils.cp "#{from}/#{src}", dest + rescue Errno::ENOENT + end +end diff --git a/test/lib/core_assertions.rb b/test/lib/core_assertions.rb index 1fdc0a3..4887d94 100644 --- a/test/lib/core_assertions.rb +++ b/test/lib/core_assertions.rb @@ -738,35 +738,56 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block) end alias all_assertions_foreach assert_all_assertions_foreach + %w[ + CLOCK_THREAD_CPUTIME_ID CLOCK_PROCESS_CPUTIME_ID + CLOCK_MONOTONIC + ].find do |clk| + if Process.const_defined?(clk) + [clk.to_sym, Process.const_get(clk)].find do |clk| + Process.clock_gettime(clk) + rescue + # Constants may be defined but not implemented, e.g., mingw. + else + PERFORMANCE_CLOCK = clk + end + end + end + # Expect +seq+ to respond to +first+ and +each+ methods, e.g., # Array, Range, Enumerator::ArithmeticSequence and other # Enumerable-s, and each elements should be size factors. # # :yield: each elements of +seq+. def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) + pend "No PERFORMANCE_CLOCK found" unless defined?(PERFORMANCE_CLOCK) + + # Timeout testing generally doesn't work when RJIT compilation happens. + rjit_enabled = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? + measure = proc do |arg, message| + st = Process.clock_gettime(PERFORMANCE_CLOCK) + yield(*arg) + t = (Process.clock_gettime(PERFORMANCE_CLOCK) - st) + assert_operator 0, :<=, t, message unless rjit_enabled + t + end + first = seq.first *arg = pre.call(first) times = (0..(rehearsal || (2 * first))).map do - st = Process.clock_gettime(Process::CLOCK_MONOTONIC) - yield(*arg) - t = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st) - assert_operator 0, :<=, t - t.nonzero? + measure[arg, "rehearsal"].nonzero? end times.compact! tmin, tmax = times.minmax - tmax *= tmax / tmin - tmax = 10**Math.log10(tmax).ceil + tbase = 10 ** Math.log10(tmax * ([(tmax / tmin), 2].max ** 2)).ceil + info = "(tmin: #{tmin}, tmax: #{tmax}, tbase: #{tbase})" seq.each do |i| next if i == first - t = tmax * i.fdiv(first) + t = tbase * i.fdiv(first) *arg = pre.call(i) - message = "[#{i}]: in #{t}s" + message = "[#{i}]: in #{t}s #{info}" Timeout.timeout(t, Timeout::Error, message) do - st = Process.clock_gettime(Process::CLOCK_MONOTONIC) - yield(*arg) - assert_operator (Process.clock_gettime(Process::CLOCK_MONOTONIC) - st), :<=, t, message + measure[arg, message] end end end From 26363e77b85692e6704ba82b25947690dacefc13 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 8 Jun 2023 16:20:57 +0900 Subject: [PATCH 38/52] Use released version of test-unit-ruby-core --- Gemfile | 1 + test/lib/core_assertions.rb | 817 ------------------------------------ test/lib/envutil.rb | 380 ----------------- test/lib/find_executable.rb | 22 - test/lib/helper.rb | 2 +- 5 files changed, 2 insertions(+), 1220 deletions(-) delete mode 100644 test/lib/core_assertions.rb delete mode 100644 test/lib/envutil.rb delete mode 100644 test/lib/find_executable.rb diff --git a/Gemfile b/Gemfile index 3dc2883..ba909e8 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,5 @@ group :development do gem "bundler" gem "rake" gem "test-unit" + gem "test-unit-ruby-core" end diff --git a/test/lib/core_assertions.rb b/test/lib/core_assertions.rb deleted file mode 100644 index 4887d94..0000000 --- a/test/lib/core_assertions.rb +++ /dev/null @@ -1,817 +0,0 @@ -# frozen_string_literal: true - -module Test - module Unit - module Assertions - def assert_raises(*exp, &b) - raise NoMethodError, "use assert_raise", caller - end - - def _assertions= n # :nodoc: - @_assertions = n - end - - def _assertions # :nodoc: - @_assertions ||= 0 - end - - ## - # Returns a proc that will output +msg+ along with the default message. - - def message msg = nil, ending = nil, &default - proc { - ending ||= (ending_pattern = /(? 0 and b > 0 - assert_operator(a.fdiv(b), :<, limit, message(message) {"#{n}: #{b} => #{a}"}) - end - rescue LoadError - pend - end - - # :call-seq: - # assert_nothing_raised( *args, &block ) - # - #If any exceptions are given as arguments, the assertion will - #fail if one of those exceptions are raised. Otherwise, the test fails - #if any exceptions are raised. - # - #The final argument may be a failure message. - # - # assert_nothing_raised RuntimeError do - # raise Exception #Assertion passes, Exception is not a RuntimeError - # end - # - # assert_nothing_raised do - # raise Exception #Assertion fails - # end - def assert_nothing_raised(*args) - self._assertions += 1 - if Module === args.last - msg = nil - else - msg = args.pop - end - begin - yield - rescue Test::Unit::PendedError, *(Test::Unit::AssertionFailedError if args.empty?) - raise - rescue *(args.empty? ? Exception : args) => e - msg = message(msg) { - "Exception raised:\n<#{mu_pp(e)}>\n""Backtrace:\n" << - Test.filter_backtrace(e.backtrace).map{|frame| " #{frame}"}.join("\n") - } - raise Test::Unit::AssertionFailedError, msg.call, e.backtrace - end - end - - def prepare_syntax_check(code, fname = nil, mesg = nil, verbose: nil) - fname ||= caller_locations(2, 1)[0] - mesg ||= fname.to_s - verbose, $VERBOSE = $VERBOSE, verbose - case - when Array === fname - fname, line = *fname - when defined?(fname.path) && defined?(fname.lineno) - fname, line = fname.path, fname.lineno - else - line = 1 - end - yield(code, fname, line, message(mesg) { - if code.end_with?("\n") - "```\n#{code}```\n" - else - "```\n#{code}\n```\n""no-newline" - end - }) - ensure - $VERBOSE = verbose - end - - def assert_valid_syntax(code, *args, **opt) - prepare_syntax_check(code, *args, **opt) do |src, fname, line, mesg| - yield if defined?(yield) - assert_nothing_raised(SyntaxError, mesg) do - assert_equal(:ok, syntax_check(src, fname, line), mesg) - end - end - end - - def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) - assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) - if child_env - child_env = [child_env] - else - child_env = [] - end - out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, **opt) - assert !status.signaled?, FailDesc[status, message, out] - end - - def assert_ruby_status(args, test_stdin="", message=nil, **opt) - out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) - desc = FailDesc[status, message, out] - assert(!status.signaled?, desc) - message ||= "ruby exit status is not success:" - assert(status.success?, desc) - end - - ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") - - def separated_runner(token, out = nil) - include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) }) - out = out ? IO.new(out, 'w') : STDOUT - at_exit { - out.puts "#{token}", [Marshal.dump($!)].pack('m'), "#{token}", "#{token}assertions=#{self._assertions}" - } - if defined?(Test::Unit::Runner) - Test::Unit::Runner.class_variable_set(:@@stop_auto_run, true) - elsif defined?(Test::Unit::AutoRunner) - Test::Unit::AutoRunner.need_auto_run = false - end - end - - def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) - unless file and line - loc, = caller_locations(1,1) - file ||= loc.path - line ||= loc.lineno - end - capture_stdout = true - unless /mswin|mingw/ =~ RbConfig::CONFIG['host_os'] - capture_stdout = false - opt[:out] = Test::Unit::Runner.output if defined?(Test::Unit::Runner) - res_p, res_c = IO.pipe - opt[:ios] = [res_c] - end - token_dump, token_re = new_test_token - src = <\n\K.*\n(?=#{token_re}<\/error>$)/m].unpack1("m")) - rescue => marshal_error - ignore_stderr = nil - res = nil - end - if res and !(SystemExit === res) - if bt = res.backtrace - bt.each do |l| - l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} - end - bt.concat(caller) - else - res.set_backtrace(caller) - end - raise res - end - - # really is it succeed? - unless ignore_stderr - # the body of assert_separately must not output anything to detect error - assert(stderr.empty?, FailDesc[status, "assert_separately failed with error message", stderr]) - end - assert(status.success?, FailDesc[status, "assert_separately failed", stderr]) - raise marshal_error if marshal_error - end - - # Run Ractor-related test without influencing the main test suite - def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, line: nil, ignore_stderr: nil, **opt) - return unless defined?(Ractor) - - require = "require #{require.inspect}" if require - if require_relative - dir = File.dirname(caller_locations[0,1][0].absolute_path) - full_path = File.expand_path(require_relative, dir) - require = "#{require}; require #{full_path.inspect}" - end - - assert_separately(args, file, line, <<~RUBY, ignore_stderr: ignore_stderr, **opt) - #{require} - previous_verbose = $VERBOSE - $VERBOSE = nil - Ractor.new {} # trigger initial warning - $VERBOSE = previous_verbose - #{src} - RUBY - end - - # :call-seq: - # assert_throw( tag, failure_message = nil, &block ) - # - #Fails unless the given block throws +tag+, returns the caught - #value otherwise. - # - #An optional failure message may be provided as the final argument. - # - # tag = Object.new - # assert_throw(tag, "#{tag} was not thrown!") do - # throw tag - # end - def assert_throw(tag, msg = nil) - ret = catch(tag) do - begin - yield(tag) - rescue UncaughtThrowError => e - thrown = e.tag - end - msg = message(msg) { - "Expected #{mu_pp(tag)} to have been thrown"\ - "#{%Q[, not #{thrown}] if thrown}" - } - assert(false, msg) - end - assert(true) - ret - end - - # :call-seq: - # assert_raise( *args, &block ) - # - #Tests if the given block raises an exception. Acceptable exception - #types may be given as optional arguments. If the last argument is a - #String, it will be used as the error message. - # - # assert_raise do #Fails, no Exceptions are raised - # end - # - # assert_raise NameError do - # puts x #Raises NameError, so assertion succeeds - # end - def assert_raise(*exp, &b) - case exp.last - when String, Proc - msg = exp.pop - end - - begin - yield - rescue Test::Unit::PendedError => e - return e if exp.include? Test::Unit::PendedError - raise e - rescue Exception => e - expected = exp.any? { |ex| - if ex.instance_of? Module then - e.kind_of? ex - else - e.instance_of? ex - end - } - - assert expected, proc { - flunk(message(msg) {"#{mu_pp(exp)} exception expected, not #{mu_pp(e)}"}) - } - - return e - ensure - unless e - exp = exp.first if exp.size == 1 - - flunk(message(msg) {"#{mu_pp(exp)} expected but nothing was raised"}) - end - end - end - - # :call-seq: - # assert_raise_with_message(exception, expected, msg = nil, &block) - # - #Tests if the given block raises an exception with the expected - #message. - # - # assert_raise_with_message(RuntimeError, "foo") do - # nil #Fails, no Exceptions are raised - # end - # - # assert_raise_with_message(RuntimeError, "foo") do - # raise ArgumentError, "foo" #Fails, different Exception is raised - # end - # - # assert_raise_with_message(RuntimeError, "foo") do - # raise "bar" #Fails, RuntimeError is raised but the message differs - # end - # - # assert_raise_with_message(RuntimeError, "foo") do - # raise "foo" #Raises RuntimeError with the message, so assertion succeeds - # end - def assert_raise_with_message(exception, expected, msg = nil, &block) - case expected - when String - assert = :assert_equal - when Regexp - assert = :assert_match - else - raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}" - end - - ex = m = nil - EnvUtil.with_default_internal(expected.encoding) do - ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do - yield - end - m = ex.message - end - msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} - - if assert == :assert_equal - assert_equal(expected, m, msg) - else - msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp m}" } - assert expected =~ m, msg - block.binding.eval("proc{|_|$~=_}").call($~) - end - ex - end - - TEST_DIR = File.join(__dir__, "test/unit") #:nodoc: - - # :call-seq: - # assert(test, [failure_message]) - # - #Tests if +test+ is true. - # - #+msg+ may be a String or a Proc. If +msg+ is a String, it will be used - #as the failure message. Otherwise, the result of calling +msg+ will be - #used as the message if the assertion fails. - # - #If no +msg+ is given, a default message will be used. - # - # assert(false, "This was expected to be true") - def assert(test, *msgs) - case msg = msgs.first - when String, Proc - when nil - msgs.shift - else - bt = caller.reject { |s| s.start_with?(TEST_DIR) } - raise ArgumentError, "assertion message must be String or Proc, but #{msg.class} was given.", bt - end unless msgs.empty? - super - end - - # :call-seq: - # assert_respond_to( object, method, failure_message = nil ) - # - #Tests if the given Object responds to +method+. - # - #An optional failure message may be provided as the final argument. - # - # assert_respond_to("hello", :reverse) #Succeeds - # assert_respond_to("hello", :does_not_exist) #Fails - def assert_respond_to(obj, (meth, *priv), msg = nil) - unless priv.empty? - msg = message(msg) { - "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}#{" privately" if priv[0]}" - } - return assert obj.respond_to?(meth, *priv), msg - end - #get rid of overcounting - if caller_locations(1, 1)[0].path.start_with?(TEST_DIR) - return if obj.respond_to?(meth) - end - super(obj, meth, msg) - end - - # :call-seq: - # assert_not_respond_to( object, method, failure_message = nil ) - # - #Tests if the given Object does not respond to +method+. - # - #An optional failure message may be provided as the final argument. - # - # assert_not_respond_to("hello", :reverse) #Fails - # assert_not_respond_to("hello", :does_not_exist) #Succeeds - def assert_not_respond_to(obj, (meth, *priv), msg = nil) - unless priv.empty? - msg = message(msg) { - "Expected #{mu_pp(obj)} (#{obj.class}) to not respond to ##{meth}#{" privately" if priv[0]}" - } - return assert !obj.respond_to?(meth, *priv), msg - end - #get rid of overcounting - if caller_locations(1, 1)[0].path.start_with?(TEST_DIR) - return unless obj.respond_to?(meth) - end - refute_respond_to(obj, meth, msg) - end - - # pattern_list is an array which contains regexp, string and :*. - # :* means any sequence. - # - # pattern_list is anchored. - # Use [:*, regexp/string, :*] for non-anchored match. - def assert_pattern_list(pattern_list, actual, message=nil) - rest = actual - anchored = true - pattern_list.each_with_index {|pattern, i| - if pattern == :* - anchored = false - else - if anchored - match = rest.rindex(pattern, 0) - else - match = rest.index(pattern) - end - if match - post_match = $~ ? $~.post_match : rest[match+pattern.size..-1] - else - msg = message(msg) { - expect_msg = "Expected #{mu_pp pattern}\n" - if /\n[^\n]/ =~ rest - actual_mesg = +"to match\n" - rest.scan(/.*\n+/) { - actual_mesg << ' ' << $&.inspect << "+\n" - } - actual_mesg.sub!(/\+\n\z/, '') - else - actual_mesg = "to match " + mu_pp(rest) - end - actual_mesg << "\nafter #{i} patterns with #{actual.length - rest.length} characters" - expect_msg + actual_mesg - } - assert false, msg - end - rest = post_match - anchored = true - end - } - if anchored - assert_equal("", rest) - end - end - - def assert_warning(pat, msg = nil) - result = nil - stderr = EnvUtil.with_default_internal(pat.encoding) { - EnvUtil.verbose_warning { - result = yield - } - } - msg = message(msg) {diff pat, stderr} - assert(pat === stderr, msg) - result - end - - def assert_warn(*args) - assert_warning(*args) {$VERBOSE = false; yield} - end - - def assert_deprecated_warning(mesg = /deprecated/) - assert_warning(mesg) do - Warning[:deprecated] = true if Warning.respond_to?(:[]=) - yield - end - end - - def assert_deprecated_warn(mesg = /deprecated/) - assert_warn(mesg) do - Warning[:deprecated] = true if Warning.respond_to?(:[]=) - yield - end - end - - class << (AssertFile = Struct.new(:failure_message).new) - include Assertions - include CoreAssertions - def assert_file_predicate(predicate, *args) - if /\Anot_/ =~ predicate - predicate = $' - neg = " not" - end - result = File.__send__(predicate, *args) - result = !result if neg - mesg = "Expected file ".dup << args.shift.inspect - mesg << "#{neg} to be #{predicate}" - mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? - mesg << " #{failure_message}" if failure_message - assert(result, mesg) - end - alias method_missing assert_file_predicate - - def for(message) - clone.tap {|a| a.failure_message = message} - end - end - - class AllFailures - attr_reader :failures - - def initialize - @count = 0 - @failures = {} - end - - def for(key) - @count += 1 - yield key - rescue Exception => e - @failures[key] = [@count, e] - end - - def foreach(*keys) - keys.each do |key| - @count += 1 - begin - yield key - rescue Exception => e - @failures[key] = [@count, e] - end - end - end - - def message - i = 0 - total = @count.to_s - fmt = "%#{total.size}d" - @failures.map {|k, (n, v)| - v = v.message - "\n#{i+=1}. [#{fmt%n}/#{total}] Assertion for #{k.inspect}\n#{v.b.gsub(/^/, ' | ').force_encoding(v.encoding)}" - }.join("\n") - end - - def pass? - @failures.empty? - end - end - - # threads should respond to shift method. - # Array can be used. - def assert_join_threads(threads, message = nil) - errs = [] - values = [] - while th = threads.shift - begin - values << th.value - rescue Exception - errs << [th, $!] - th = nil - end - end - values - ensure - if th&.alive? - th.raise(Timeout::Error.new) - th.join rescue errs << [th, $!] - end - if !errs.empty? - msg = "exceptions on #{errs.length} threads:\n" + - errs.map {|t, err| - "#{t.inspect}:\n" + - (err.respond_to?(:full_message) ? err.full_message(highlight: false, order: :top) : err.message) - }.join("\n---\n") - if message - msg = "#{message}\n#{msg}" - end - raise Test::Unit::AssertionFailedError, msg - end - end - - def assert_all?(obj, m = nil, &blk) - failed = [] - obj.each do |*a, &b| - unless blk.call(*a, &b) - failed << (a.size > 1 ? a : a[0]) - end - end - assert(failed.empty?, message(m) {failed.pretty_inspect}) - end - - def assert_all_assertions(msg = nil) - all = AllFailures.new - yield all - ensure - assert(all.pass?, message(msg) {all.message.chomp(".")}) - end - alias all_assertions assert_all_assertions - - def assert_all_assertions_foreach(msg = nil, *keys, &block) - all = AllFailures.new - all.foreach(*keys, &block) - ensure - assert(all.pass?, message(msg) {all.message.chomp(".")}) - end - alias all_assertions_foreach assert_all_assertions_foreach - - %w[ - CLOCK_THREAD_CPUTIME_ID CLOCK_PROCESS_CPUTIME_ID - CLOCK_MONOTONIC - ].find do |clk| - if Process.const_defined?(clk) - [clk.to_sym, Process.const_get(clk)].find do |clk| - Process.clock_gettime(clk) - rescue - # Constants may be defined but not implemented, e.g., mingw. - else - PERFORMANCE_CLOCK = clk - end - end - end - - # Expect +seq+ to respond to +first+ and +each+ methods, e.g., - # Array, Range, Enumerator::ArithmeticSequence and other - # Enumerable-s, and each elements should be size factors. - # - # :yield: each elements of +seq+. - def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) - pend "No PERFORMANCE_CLOCK found" unless defined?(PERFORMANCE_CLOCK) - - # Timeout testing generally doesn't work when RJIT compilation happens. - rjit_enabled = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? - measure = proc do |arg, message| - st = Process.clock_gettime(PERFORMANCE_CLOCK) - yield(*arg) - t = (Process.clock_gettime(PERFORMANCE_CLOCK) - st) - assert_operator 0, :<=, t, message unless rjit_enabled - t - end - - first = seq.first - *arg = pre.call(first) - times = (0..(rehearsal || (2 * first))).map do - measure[arg, "rehearsal"].nonzero? - end - times.compact! - tmin, tmax = times.minmax - tbase = 10 ** Math.log10(tmax * ([(tmax / tmin), 2].max ** 2)).ceil - info = "(tmin: #{tmin}, tmax: #{tmax}, tbase: #{tbase})" - - seq.each do |i| - next if i == first - t = tbase * i.fdiv(first) - *arg = pre.call(i) - message = "[#{i}]: in #{t}s #{info}" - Timeout.timeout(t, Timeout::Error, message) do - measure[arg, message] - end - end - end - - def diff(exp, act) - require 'pp' - q = PP.new(+"") - q.guard_inspect_key do - q.group(2, "expected: ") do - q.pp exp - end - q.text q.newline - q.group(2, "actual: ") do - q.pp act - end - q.flush - end - q.output - end - - def new_test_token - token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m" - return token.dump, Regexp.quote(token) - end - end - end -end diff --git a/test/lib/envutil.rb b/test/lib/envutil.rb deleted file mode 100644 index 728ca70..0000000 --- a/test/lib/envutil.rb +++ /dev/null @@ -1,380 +0,0 @@ -# -*- coding: us-ascii -*- -# frozen_string_literal: true -require "open3" -require "timeout" -require_relative "find_executable" -begin - require 'rbconfig' -rescue LoadError -end -begin - require "rbconfig/sizeof" -rescue LoadError -end - -module EnvUtil - def rubybin - if ruby = ENV["RUBY"] - return ruby - end - ruby = "ruby" - exeext = RbConfig::CONFIG["EXEEXT"] - rubyexe = (ruby + exeext if exeext and !exeext.empty?) - 3.times do - if File.exist? ruby and File.executable? ruby and !File.directory? ruby - return File.expand_path(ruby) - end - if rubyexe and File.exist? rubyexe and File.executable? rubyexe - return File.expand_path(rubyexe) - end - ruby = File.join("..", ruby) - end - if defined?(RbConfig.ruby) - RbConfig.ruby - else - "ruby" - end - end - module_function :rubybin - - LANG_ENVS = %w"LANG LC_ALL LC_CTYPE" - - DEFAULT_SIGNALS = Signal.list - DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM - - RUBYLIB = ENV["RUBYLIB"] - - class << self - attr_accessor :timeout_scale - attr_reader :original_internal_encoding, :original_external_encoding, - :original_verbose, :original_warning - - def capture_global_values - @original_internal_encoding = Encoding.default_internal - @original_external_encoding = Encoding.default_external - @original_verbose = $VERBOSE - @original_warning = defined?(Warning.[]) ? %i[deprecated experimental].to_h {|i| [i, Warning[i]]} : nil - end - end - - def apply_timeout_scale(t) - if scale = EnvUtil.timeout_scale - t * scale - else - t - end - end - module_function :apply_timeout_scale - - def timeout(sec, klass = nil, message = nil, &blk) - return yield(sec) if sec == nil or sec.zero? - sec = apply_timeout_scale(sec) - Timeout.timeout(sec, klass, message, &blk) - end - module_function :timeout - - def terminate(pid, signal = :TERM, pgroup = nil, reprieve = 1) - reprieve = apply_timeout_scale(reprieve) if reprieve - - signals = Array(signal).select do |sig| - DEFAULT_SIGNALS[sig.to_s] or - DEFAULT_SIGNALS[Signal.signame(sig)] rescue false - end - signals |= [:ABRT, :KILL] - case pgroup - when 0, true - pgroup = -pid - when nil, false - pgroup = pid - end - - lldb = true if /darwin/ =~ RUBY_PLATFORM - - while signal = signals.shift - - if lldb and [:ABRT, :KILL].include?(signal) - lldb = false - # sudo -n: --non-interactive - # lldb -p: attach - # -o: run command - system(*%W[sudo -n lldb -p #{pid} --batch -o bt\ all -o call\ rb_vmdebug_stack_dump_all_threads() -o quit]) - true - end - - begin - Process.kill signal, pgroup - rescue Errno::EINVAL - next - rescue Errno::ESRCH - break - end - if signals.empty? or !reprieve - Process.wait(pid) - else - begin - Timeout.timeout(reprieve) {Process.wait(pid)} - rescue Timeout::Error - else - break - end - end - end - $? - end - module_function :terminate - - def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false, - encoding: nil, timeout: 10, reprieve: 1, timeout_error: Timeout::Error, - stdout_filter: nil, stderr_filter: nil, ios: nil, - signal: :TERM, - rubybin: EnvUtil.rubybin, precommand: nil, - **opt) - timeout = apply_timeout_scale(timeout) - - in_c, in_p = IO.pipe - out_p, out_c = IO.pipe if capture_stdout - err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout - opt[:in] = in_c - opt[:out] = out_c if capture_stdout - opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr - if encoding - out_p.set_encoding(encoding) if out_p - err_p.set_encoding(encoding) if err_p - end - ios.each {|i, o = i|opt[i] = o} if ios - - c = "C" - child_env = {} - LANG_ENVS.each {|lc| child_env[lc] = c} - if Array === args and Hash === args.first - child_env.update(args.shift) - end - if RUBYLIB and lib = child_env["RUBYLIB"] - child_env["RUBYLIB"] = [lib, RUBYLIB].join(File::PATH_SEPARATOR) - end - - # remain env - %w(ASAN_OPTIONS RUBY_ON_BUG).each{|name| - child_env[name] = ENV[name] if ENV[name] - } - - args = [args] if args.kind_of?(String) - pid = spawn(child_env, *precommand, rubybin, *args, opt) - in_c.close - out_c&.close - out_c = nil - err_c&.close - err_c = nil - if block_given? - return yield in_p, out_p, err_p, pid - else - th_stdout = Thread.new { out_p.read } if capture_stdout - th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout - in_p.write stdin_data.to_str unless stdin_data.empty? - in_p.close - if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout)) - timeout_error = nil - else - status = terminate(pid, signal, opt[:pgroup], reprieve) - terminated = Time.now - end - stdout = th_stdout.value if capture_stdout - stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout - out_p.close if capture_stdout - err_p.close if capture_stderr && capture_stderr != :merge_to_stdout - status ||= Process.wait2(pid)[1] - stdout = stdout_filter.call(stdout) if stdout_filter - stderr = stderr_filter.call(stderr) if stderr_filter - if timeout_error - bt = caller_locations - msg = "execution of #{bt.shift.label} expired timeout (#{timeout} sec)" - msg = failure_description(status, terminated, msg, [stdout, stderr].join("\n")) - raise timeout_error, msg, bt.map(&:to_s) - end - return stdout, stderr, status - end - ensure - [th_stdout, th_stderr].each do |th| - th.kill if th - end - [in_c, in_p, out_c, out_p, err_c, err_p].each do |io| - io&.close - end - [th_stdout, th_stderr].each do |th| - th.join if th - end - end - module_function :invoke_ruby - - def verbose_warning - class << (stderr = "".dup) - alias write concat - def flush; end - end - stderr, $stderr = $stderr, stderr - $VERBOSE = true - yield stderr - return $stderr - ensure - stderr, $stderr = $stderr, stderr - $VERBOSE = EnvUtil.original_verbose - EnvUtil.original_warning&.each {|i, v| Warning[i] = v} - end - module_function :verbose_warning - - def default_warning - $VERBOSE = false - yield - ensure - $VERBOSE = EnvUtil.original_verbose - end - module_function :default_warning - - def suppress_warning - $VERBOSE = nil - yield - ensure - $VERBOSE = EnvUtil.original_verbose - end - module_function :suppress_warning - - def under_gc_stress(stress = true) - stress, GC.stress = GC.stress, stress - yield - ensure - GC.stress = stress - end - module_function :under_gc_stress - - def with_default_external(enc) - suppress_warning { Encoding.default_external = enc } - yield - ensure - suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } - end - module_function :with_default_external - - def with_default_internal(enc) - suppress_warning { Encoding.default_internal = enc } - yield - ensure - suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } - end - module_function :with_default_internal - - def labeled_module(name, &block) - Module.new do - singleton_class.class_eval { - define_method(:to_s) {name} - alias inspect to_s - alias name to_s - } - class_eval(&block) if block - end - end - module_function :labeled_module - - def labeled_class(name, superclass = Object, &block) - Class.new(superclass) do - singleton_class.class_eval { - define_method(:to_s) {name} - alias inspect to_s - alias name to_s - } - class_eval(&block) if block - end - end - module_function :labeled_class - - if /darwin/ =~ RUBY_PLATFORM - DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports") - DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S' - @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME'] - - def self.diagnostic_reports(signame, pid, now) - return unless %w[ABRT QUIT SEGV ILL TRAP].include?(signame) - cmd = File.basename(rubybin) - cmd = @ruby_install_name if "ruby-runner#{RbConfig::CONFIG["EXEEXT"]}" == cmd - path = DIAGNOSTIC_REPORTS_PATH - timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT - pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.{crash,ips}" - first = true - 30.times do - first ? (first = false) : sleep(0.1) - Dir.glob(pat) do |name| - log = File.read(name) rescue next - case name - when /\.crash\z/ - if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log - File.unlink(name) - File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil - return log - end - when /\.ips\z/ - if /^ *"pid" *: *#{pid},/ =~ log - File.unlink(name) - return log - end - end - end - end - nil - end - else - def self.diagnostic_reports(signame, pid, now) - end - end - - def self.failure_description(status, now, message = "", out = "") - pid = status.pid - if signo = status.termsig - signame = Signal.signame(signo) - sigdesc = "signal #{signo}" - end - log = diagnostic_reports(signame, pid, now) - if signame - sigdesc = "SIG#{signame} (#{sigdesc})" - end - if status.coredump? - sigdesc = "#{sigdesc} (core dumped)" - end - full_message = ''.dup - message = message.call if Proc === message - if message and !message.empty? - full_message << message << "\n" - end - full_message << "pid #{pid}" - full_message << " exit #{status.exitstatus}" if status.exited? - full_message << " killed by #{sigdesc}" if sigdesc - if out and !out.empty? - full_message << "\n" << out.b.gsub(/^/, '| ') - full_message.sub!(/(? Date: Thu, 8 Jun 2023 16:22:29 +0900 Subject: [PATCH 39/52] Don't use bundler-cache --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2a6937a..88a6261 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,6 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - bundler-cache: true + - run: bundle install --jobs 4 --retry 3 - name: Run test run: rake test From 3dfa19e920357385a8bc8aae67df4efb1da6af7a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 13 Oct 2022 18:01:58 +0900 Subject: [PATCH 40/52] Refactor RFC3986 regexps to make more readable --- lib/uri/rfc3986_parser.rb | 82 +++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index dd24a40..8cc51f1 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -1,9 +1,69 @@ -# frozen_string_literal: false +# frozen_string_literal: true module URI class RFC3986_Parser # :nodoc: # URI defined in RFC3986 - RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*+):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+))(?::(?\d*+))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g)*+)?)|(?\g(?:\/\g)*+)|(?))(?:\?(?[^#]*+))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ - RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*+)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h++\.[!$&-.0-;=A-Z_a-z~]++))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])++))?(?::(?\d*+))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*+))*+)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])++)(?:\/\g)*+)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])++)(?:\/\g)*+)|(?))(?:\?(?[^#]*+))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*+))?)\z/ + HOST = %r[ + (?\[(?: + (? + (?:\h{1,4}:){6} + (?\h{1,4}:\h{1,4} + | (?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d) + \.\g\.\g\.\g) + ) + | ::(?:\h{1,4}:){5}\g + | \h{1,4}?::(?:\h{1,4}:){4}\g + | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g + | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g + | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g + | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g + | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4} + | (?:(?:\h{1,4}:){,6}\h{1,4})?:: + ) + | (?v\h++\.[!$&-.0-9:;=A-Z_a-z~]++) + )\]) + | \g + | (?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+) + ]x + + USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/ + AUTHORITY = %r[ + (?:(?#{USERINFO.source})@)? + (?#{HOST.source.delete(" \n")}) + (?::(?\d*+))? + ]x + + SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source + SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source + FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source + + RFC3986_URI = %r[\A + (?#{SEG}){0} + (? + (?#{SCHEME}): + (?// + (?#{AUTHORITY}) + (?(?:/\g*+)?) + | (?/\g*+) + | (?(?!=/)\g++) + | (?) + ) + (?:\?(?[^\#]*+))? + (?:\#(?#{FRAGMENT}))? + )\z]x + + RFC3986_relative_ref = %r[\A + (?#{SEG}){0} + (? + (?// + (?#{AUTHORITY}) + (?(?:/\g*+)?) + | (?/\g*+) + | (?(?!=[:/])\g++) + | (?) + ) + (?:\?(?[^#]*+))? + (?:\#(?#{FRAGMENT}))? + )\z]x attr_reader :regexp def initialize @@ -92,14 +152,14 @@ def inspect def default_regexp # :nodoc: { - SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/, - USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/, - HOST: /\A(?:(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{,4}::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/, - ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/, - REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/, - QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/, - FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/, - OPAQUE: /\A(?:[^\/].*)?\z/, + SCHEME: %r[\A#{SCHEME}\z]o, + USERINFO: %r[\A#{USERINFO}\z]o, + HOST: %r[\A#{HOST}\z]o, + ABS_PATH: %r[\A/#{SEG}*+\z]o, + REL_PATH: %r[\A(?!=/)#{SEG}++\z]o, + QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z], + FRAGMENT: %r[\A#{FRAGMENT}\z]o, + OPAQUE: %r[\A(?:[^/].*)?\z], PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/, } end From 453313bd3843ad8a94b9477a9db2552ec731d894 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:11:59 +0000 Subject: [PATCH 41/52] Bump ruby/setup-ruby from 1.150.0 to 1.151.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.150.0 to 1.151.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/8a45918450651f5e4784b6031db26f4b9f76b251...bc1dd263b68cb5626dbb55d5c89777d79372c484) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index f381ba0..37cd126 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251 # v1.150.0 + uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # v1.151.0 with: ruby-version: '3.2' bundler-cache: true From 8e385922412e0a8ea13dceb1154049df5b040ff9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 13 Jun 2023 13:54:44 +0900 Subject: [PATCH 42/52] Fix RFC3986 regexps --- lib/uri/rfc3986_parser.rb | 9 +++++---- test/uri/test_parser.rb | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index 8cc51f1..59e2be2 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -34,6 +34,7 @@ class RFC3986_Parser # :nodoc: SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source + SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source RFC3986_URI = %r[\A @@ -43,8 +44,8 @@ class RFC3986_Parser # :nodoc: (?// (?#{AUTHORITY}) (?(?:/\g*+)?) - | (?/\g*+) - | (?(?!=/)\g++) + | (?/((?!/)\g++)?) + | (?(?!/)\g++) | (?) ) (?:\?(?[^\#]*+))? @@ -58,7 +59,7 @@ class RFC3986_Parser # :nodoc: (?#{AUTHORITY}) (?(?:/\g*+)?) | (?/\g*+) - | (?(?!=[:/])\g++) + | (?#{SEG_NC}++(?:/\g*+)?) | (?) ) (?:\?(?[^#]*+))? @@ -156,7 +157,7 @@ def default_regexp # :nodoc: USERINFO: %r[\A#{USERINFO}\z]o, HOST: %r[\A#{HOST}\z]o, ABS_PATH: %r[\A/#{SEG}*+\z]o, - REL_PATH: %r[\A(?!=/)#{SEG}++\z]o, + REL_PATH: %r[\A(?!/)#{SEG}++\z]o, QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z], FRAGMENT: %r[\A#{FRAGMENT}\z]o, OPAQUE: %r[\A(?:[^/].*)?\z], diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb index 72fb590..70d476b 100644 --- a/test/uri/test_parser.rb +++ b/test/uri/test_parser.rb @@ -78,5 +78,13 @@ def test_split assert_equal(["http", nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("http://[0::0]")) assert_equal([nil, nil, "example.com", nil, nil, "", nil, nil, nil], URI.split("//example.com")) assert_equal([nil, nil, "[0::0]", nil, nil, "", nil, nil, nil], URI.split("//[0::0]")) + + assert_equal(["a", nil, nil, nil, nil, "", nil, nil, nil], URI.split("a:")) + assert_raise(URI::InvalidURIError) do + URI.parse("::") + end + assert_raise(URI::InvalidURIError) do + URI.parse("foo@example:foo") + end end end From 051b2b552d2c3aba7e6dcc6d9186b937e4b96aa5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 01:14:33 +0000 Subject: [PATCH 43/52] Bump ruby/setup-ruby from 1.151.0 to 1.152.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.151.0 to 1.152.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Commits](https://github.com/ruby/setup-ruby/compare/bc1dd263b68cb5626dbb55d5c89777d79372c484...250fcd6a742febb1123a77a841497ccaa8b9e939) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 37cd126..df321af 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Setup Ruby - uses: ruby/setup-ruby@bc1dd263b68cb5626dbb55d5c89777d79372c484 # v1.151.0 + uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0 with: ruby-version: '3.2' bundler-cache: true From 2980f0ba02107f8db4051ddfe60e42e423df114e Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 25 Jun 2023 23:58:12 +0900 Subject: [PATCH 44/52] Fix host part in relative referece #83 In relative referece, host part can be ommitted but can not be empty. --- lib/uri/rfc3986_parser.rb | 17 ++++++++++------- test/uri/test_generic.rb | 4 ++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index 59e2be2..ffdcfa9 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -26,11 +26,6 @@ class RFC3986_Parser # :nodoc: ]x USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/ - AUTHORITY = %r[ - (?:(?#{USERINFO.source})@)? - (?#{HOST.source.delete(" \n")}) - (?::(?\d*+))? - ]x SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source @@ -42,7 +37,11 @@ class RFC3986_Parser # :nodoc: (? (?#{SCHEME}): (?// - (?#{AUTHORITY}) + (? + (?:(?#{USERINFO.source})@)? + (?#{HOST.source.delete(" \n")}) + (?::(?\d*+))? + ) (?(?:/\g*+)?) | (?/((?!/)\g++)?) | (?(?!/)\g++) @@ -56,7 +55,11 @@ class RFC3986_Parser # :nodoc: (?#{SEG}){0} (? (?// - (?#{AUTHORITY}) + (? + (?:(?#{USERINFO.source})@)? + (?#{HOST.source.delete(" \n")}(?\d*+))? + ) (?(?:/\g*+)?) | (?/\g*+) | (?#{SEG_NC}++(?:/\g*+)?) diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb index 3897c3d..e661937 100644 --- a/test/uri/test_generic.rb +++ b/test/uri/test_generic.rb @@ -977,6 +977,10 @@ def test_use_proxy_p end end + def test_split + assert_equal [nil, nil, nil, nil, nil, "", nil, nil, nil], URI.split("//") + end + class CaseInsensitiveEnv def initialize(h={}) @h = {} From 0b6ad60af6668ab9f091af4ef4bee839620f7878 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 26 Jun 2023 00:39:27 +0900 Subject: [PATCH 45/52] String literals are frozen now --- lib/uri/rfc3986_parser.rb | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index ffdcfa9..41fbea4 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -83,9 +83,9 @@ def split(uri) #:nodoc: uri.ascii_only? or raise InvalidURIError, "URI must be ascii only #{uri.dump}" if m = RFC3986_URI.match(uri) - query = m["query".freeze] - scheme = m["scheme".freeze] - opaque = m["path-rootless".freeze] + query = m["query"] + scheme = m["scheme"] + opaque = m["path-rootless"] if opaque opaque << "?#{query}" if query [ scheme, @@ -96,35 +96,35 @@ def split(uri) #:nodoc: nil, # path opaque, nil, # query - m["fragment".freeze] + m["fragment"] ] else # normal [ scheme, - m["userinfo".freeze], - m["host".freeze], - m["port".freeze], + m["userinfo"], + m["host"], + m["port"], nil, # registry - (m["path-abempty".freeze] || - m["path-absolute".freeze] || - m["path-empty".freeze]), + (m["path-abempty"] || + m["path-absolute"] || + m["path-empty"]), nil, # opaque query, - m["fragment".freeze] + m["fragment"] ] end elsif m = RFC3986_relative_ref.match(uri) [ nil, # scheme - m["userinfo".freeze], - m["host".freeze], - m["port".freeze], + m["userinfo"], + m["host"], + m["port"], nil, # registry, - (m["path-abempty".freeze] || - m["path-absolute".freeze] || - m["path-noscheme".freeze] || - m["path-empty".freeze]), + (m["path-abempty"] || + m["path-absolute"] || + m["path-noscheme"] || + m["path-empty"]), nil, # opaque - m["query".freeze], - m["fragment".freeze] + m["query"], + m["fragment"] ] else raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}" From 9010ee2536adda10a0555ae1ed6fe2f5808e6bf1 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 22 Apr 2023 20:08:32 +0900 Subject: [PATCH 46/52] Fix quadratic backtracking on invalid relative URI https://hackerone.com/reports/1958260 --- lib/uri/rfc2396_parser.rb | 4 ++-- test/uri/test_parser.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb index 76a8f99..00c66cf 100644 --- a/lib/uri/rfc2396_parser.rb +++ b/lib/uri/rfc2396_parser.rb @@ -497,8 +497,8 @@ def initialize_regexp(pattern) ret = {} # for URI::split - ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED) - ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED) + ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED) + ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED) # for URI::extract ret[:URI_REF] = Regexp.new(pattern[:URI_REF]) diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb index 70d476b..55abe2c 100644 --- a/test/uri/test_parser.rb +++ b/test/uri/test_parser.rb @@ -87,4 +87,16 @@ def test_split URI.parse("foo@example:foo") end end + + def test_rfc2822_parse_relative_uri + pre = ->(length) { + " " * length + "\0" + } + parser = URI::RFC2396_Parser.new + assert_linear_performance((1..5).map {|i| 10**i}, pre: pre) do |uri| + assert_raise(URI::InvalidURIError) do + parser.split(uri) + end + end + end end From 9d7bcef1e6ad23c9c6e4932f297fb737888144c8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 22 Apr 2023 20:09:10 +0900 Subject: [PATCH 47/52] Fix quadratic backtracking on invalid port number https://hackerone.com/reports/1958260 --- lib/uri/rfc3986_parser.rb | 2 +- test/uri/test_parser.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb index 41fbea4..092a1ac 100644 --- a/lib/uri/rfc3986_parser.rb +++ b/lib/uri/rfc3986_parser.rb @@ -164,7 +164,7 @@ def default_regexp # :nodoc: QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z], FRAGMENT: %r[\A#{FRAGMENT}\z]o, OPAQUE: %r[\A(?:[^/].*)?\z], - PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/, + PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/, } end diff --git a/test/uri/test_parser.rb b/test/uri/test_parser.rb index 55abe2c..75c02fe 100644 --- a/test/uri/test_parser.rb +++ b/test/uri/test_parser.rb @@ -99,4 +99,14 @@ def test_rfc2822_parse_relative_uri end end end + + def test_rfc3986_port_check + pre = ->(length) {"\t" * length + "a"} + uri = URI.parse("http://my.example.com") + assert_linear_performance((1..5).map {|i| 10**i}, pre: pre) do |port| + assert_raise(URI::InvalidComponentError) do + uri.port = port + end + end + end end From e18e657ea8eedb851e8ba187229c7d0b0bcef20c Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 21 Jun 2023 13:04:16 +0900 Subject: [PATCH 48/52] Bump up v0.12.2 --- lib/uri/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/uri/version.rb b/lib/uri/version.rb index 7497a7d..f0aca58 100644 --- a/lib/uri/version.rb +++ b/lib/uri/version.rb @@ -1,6 +1,6 @@ module URI # :stopdoc: - VERSION_CODE = '001201'.freeze + VERSION_CODE = '001202'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end From 5a626d6a2f9a702ebd8de4d630c408616a346412 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 00:43:21 +0000 Subject: [PATCH 49/52] Bump actions/upload-pages-artifact from 1 to 2 Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 1 to 2. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v1...v2) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index df321af..4a7d0e2 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -32,7 +32,7 @@ jobs: # Outputs to the './_site' directory by default run: bundle exec rake rdoc - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v2 deploy: environment: From b0b029ce34465766f351b511693fc573ea0a509c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 00:43:19 +0000 Subject: [PATCH 50/52] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/gh-pages.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 4a7d0e2..a4f155a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88a6261..6cf5ef7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: os: [ ubuntu-latest, macos-latest ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: From 5c17cd20930c2ac5c288c6aaeb470c7dc7547d8c Mon Sep 17 00:00:00 2001 From: Michael Chui Date: Thu, 18 Oct 2018 15:42:16 -0700 Subject: [PATCH 51/52] add #to_str to URI::Generic --- lib/uri/generic.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb index 69698c4..f3540a2 100644 --- a/lib/uri/generic.rb +++ b/lib/uri/generic.rb @@ -1376,6 +1376,7 @@ def to_s end str end + alias to_str to_s # # Compares two URIs. From b50d37f7a193991c56bda7f94e8dd6fec0bb3f7f Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 6 Nov 2023 19:08:26 +0900 Subject: [PATCH 52/52] Bump up 0.13.0 --- lib/uri/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/uri/version.rb b/lib/uri/version.rb index f0aca58..2dafa57 100644 --- a/lib/uri/version.rb +++ b/lib/uri/version.rb @@ -1,6 +1,6 @@ module URI # :stopdoc: - VERSION_CODE = '001202'.freeze + VERSION_CODE = '001300'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end