diff options
712 files changed, 12390 insertions, 12102 deletions
diff --git a/.github/workflows/check_sast.yml b/.github/workflows/check_sast.yml index 0b5d6ad1b6..c8db1103ed 100644 --- a/.github/workflows/check_sast.yml +++ b/.github/workflows/check_sast.yml @@ -78,14 +78,14 @@ jobs: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: ${{ matrix.language }} build-mode: none config-file: .github/codeql/codeql-config.yml - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: category: '/language:${{ matrix.language }}' upload: False @@ -127,7 +127,7 @@ jobs: continue-on-error: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: sarif_file: sarif-results/${{ matrix.language }}.sarif continue-on-error: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index fdc28c2d09..6dc4a7c6ad 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -73,6 +73,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@87557b9c84dde89fdd9b10e88954ac2f4248e463 # v4.36.1 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: sarif_file: results.sarif diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b2c84abc6d..03e75ad445 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -95,7 +95,9 @@ jobs: key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }} - name: Install libraries with vcpkg + id: build-vcpkg run: | + git -C "%VCPKG_INSTALLATION_ROOT%" pull --quiet vcpkg install working-directory: src if: ${{ ! steps.restore-vcpkg.outputs.cache-hit }} @@ -105,7 +107,13 @@ jobs: with: path: src\vcpkg_installed key: windows-${{ matrix.os }}-vcpkg-${{ hashFiles('src/vcpkg.json') }} - if: ${{ ! steps.restore-vcpkg.outputs.cache-hit && (github.ref_name == 'master' || startsWith(github.ref_name, 'ruby_')) }} + if: >- + steps.build-vcpkg.outcome == 'success' && + ( github.ref_name == 'master' + || startsWith(github.ref_name, 'ruby_') + || ( github.event.pull_request.user.login == 'dependabot[bot]' + && startsWith(github.head_ref || github.ref_name, 'dependabot/vcpkg')) + ) - name: setup env # Available Ruby versions: https://github.com/actions/runner-images/blob/main/images/windows/Windows2019-Readme.md#ruby diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index 09c7c1b6db..707e50e36b 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -93,7 +93,7 @@ jobs: rustup install ${{ matrix.rust_version }} --profile minimal rustup default ${{ matrix.rust_version }} - - uses: taiki-e/install-action@25435dc8dd3baed7417e0c96d3fe89013a5b2e09 # v2.81.3 + - uses: taiki-e/install-action@4bc351f7f2614e48088386e2a0ad917ca3a7e4ba # v2.81.5 with: tool: nextest@0.9 if: ${{ matrix.test_task == 'zjit-check' }} diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 7f5ce9322e..1c3e3f6531 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -119,7 +119,7 @@ jobs: ruby-version: '3.1' bundler: none - - uses: taiki-e/install-action@25435dc8dd3baed7417e0c96d3fe89013a5b2e09 # v2.81.3 + - uses: taiki-e/install-action@4bc351f7f2614e48088386e2a0ad917ca3a7e4ba # v2.81.5 with: tool: nextest@0.9 if: ${{ matrix.test_task == 'zjit-check' }} @@ -105,8 +105,8 @@ releases. * minitest 6.0.6 * rake 13.4.2 * 13.3.1 to [v13.4.0][rake-v13.4.0], [v13.4.1][rake-v13.4.1], [v13.4.2][rake-v13.4.2] -* test-unit 3.7.7 - * 3.7.5 to [3.7.6][test-unit-3.7.6], [3.7.7][test-unit-3.7.7] +* test-unit 3.7.8 + * 3.7.5 to [3.7.6][test-unit-3.7.6], [3.7.7][test-unit-3.7.7], [3.7.8][test-unit-3.7.8] * net-imap 0.6.4 * 0.6.2 to [v0.6.3][net-imap-v0.6.3], [v0.6.4][net-imap-v0.6.4] * rbs 4.0.2 @@ -242,6 +242,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [rake-v13.4.2]: https://github.com/ruby/rake/releases/tag/v13.4.2 [test-unit-3.7.6]: https://github.com/test-unit/test-unit/releases/tag/3.7.6 [test-unit-3.7.7]: https://github.com/test-unit/test-unit/releases/tag/3.7.7 +[test-unit-3.7.8]: https://github.com/test-unit/test-unit/releases/tag/3.7.8 [net-imap-v0.6.3]: https://github.com/ruby/net-imap/releases/tag/v0.6.3 [net-imap-v0.6.4]: https://github.com/ruby/net-imap/releases/tag/v0.6.4 [rbs-v3.10.1]: https://github.com/ruby/rbs/releases/tag/v3.10.1 diff --git a/benchmark/string_inspect.yml b/benchmark/string_inspect.yml new file mode 100644 index 0000000000..62a884e19d --- /dev/null +++ b/benchmark/string_inspect.yml @@ -0,0 +1,13 @@ +prelude: | + ascii = "Hello, World! This is a benchmark test string." * 100 + utf8 = "こんにちは世界。これはベンチマーク用のテスト文字列です。" * 100 + mixed = ("Hello World! " + "テスト" + " is great! ") * 100 + binary = ("\xE3\x81\x82" * 100).b + escapy = "\n\t\"\\\#" * 100 + +benchmark: + inspect_ascii: ascii.inspect + inspect_utf8: utf8.inspect + inspect_mixed: mixed.inspect + inspect_binary: binary.inspect + inspect_escapy: escapy.inspect diff --git a/doc/file/filename_globbing.md b/doc/file/filename_globbing.md index 7981964c5c..ce4549bffe 100644 --- a/doc/file/filename_globbing.md +++ b/doc/file/filename_globbing.md @@ -1,22 +1,20 @@ # Filename Globbing -Filename globbing is a pattern-matching feature implemented in certain Ruby methods. - -Filename-globbing methods find filesystem entries (files and directories) -that match certain patterns; -these methods are: +Filename globbing is a pattern-matching feature implemented in certain Ruby methods: - Dir.glob. - [`Dir[]`](https://docs.ruby-lang.org/en/master/Dir.html#method-c-5B-5D). - Pathname.glob. - Pathname#glob. -These methods are quite different from filename-matching methods (not discussed here), -which match patterns against string paths, and do not access the filesystem; -those methods are: +Each `glob` method finds filesystem entries (files and directories) +that match certain patterns. + +These methods are quite different +from [filename-matching](rdoc-ref:filename_matching.md) methods, +which match patterns against string paths, and do not access the filesystem. -- File.fnmatch. -- Pathname#fnmatch. +## Patterns These are the basic elements of filename-globbing patterns; see the sections below for details: diff --git a/doc/file/filename_matching.md b/doc/file/filename_matching.md index 9f13da9012..cf5b60bac2 100644 --- a/doc/file/filename_matching.md +++ b/doc/file/filename_matching.md @@ -1,4 +1,4 @@ -## Filename Matching +# Filename Matching Filename matching is a pattern-matching feature implemented in certain Ruby methods: @@ -8,14 +8,11 @@ Filename matching is a pattern-matching feature implemented in certain Ruby meth Each `fnmatch` method matches a pattern against a string _path_; these methods operate only on strings, and do not access the file system. -These are quite different from filename globbing methods (not discussed here), -which match patterns against string paths found in the actual file system: +These methods are quite different +from [filename-globbing](rdoc-ref:filename_globbing.md) methods, +which match patterns against string paths found in the actual file system. -- Dir.glob. -- Pathname.glob. -- Pathname#glob. - -### Patterns +## Patterns These are the basic elements of filename matching patterns; see the sections below for details: @@ -36,7 +33,7 @@ There are two other patterns that are disabled by default: - Alternatives (`'{ , }'`); see [`File::FNM_EXTGLOB`](#constant-filefnmextglob) below. -#### Simple \String +### Simple \String A "simple string" is one that does not contain special filename-matching patterns; see the table above. @@ -78,7 +75,7 @@ File.fnmatch('PROGRAM~1', 'Program Files') # => false It may be enabled by flag [`File::FNM_SHORTNAME`](#constant-filefnmshortname). -#### Any Sequence of Characters (`'*'`) +### Any Sequence of Characters (`'*'`) The asterisk pattern (`'*'`) matches any sequence of characters: @@ -105,7 +102,7 @@ File.fnmatch('*.rb', 'lib/test.rb') # => true That matching may be disabled by flag [`File::FNM_PATHNAME`](#constant-filefnmpathname). -#### Single Character (`'?'`) +### Single Character (`'?'`) The question-mark pattern (`'?'`) matches any single character: @@ -125,7 +122,7 @@ File.fnmatch('foo?boo', 'foo/boo') # => true That matching may be disabled by flag [`File::FNM_PATHNAME`](#constant-filefnmpathname). -#### Single Character from a Set (`'[abc]'`, `'[^abc]'`) +### Single Character from a Set (`'[abc]'`, `'[^abc]'`) Characters enclosed in square brackets define a set of characters, any of which matches a single character: @@ -145,7 +142,7 @@ File.fnmatch('[^ruby]', 'r') # => false File.fnmatch('[^ruby]', 'u') # => false ``` -#### Single Character from a \Range (`'[a-c]'`, `'[^a-c]'`) +### Single Character from a \Range (`'[a-c]'`, `'[^a-c]'`) A range of characters enclosed in square brackets defines a set of characters, any of which matches a single character: @@ -165,7 +162,7 @@ File.fnmatch('[^a-c]', 'b') # => false File.fnmatch('[^a-c]', 'd') # => true ``` -#### Escape (`'\'`) +### Escape (`'\'`) The backslash character (`'\'`) may be used to escape any of the characters that filename matching treats as special: @@ -191,7 +188,7 @@ File.fnmatch('\\\\', '\\') # => true By default escape pattern `'\'` is enabled; it may be disabled by flag [`File::FNM_NOESCAPE`](#constant-filefnmnoescape). -### Flags +## Flags Optional argument `flags` (defaults to `0`) may be the bitwise OR of the constants `File::FNM*`. @@ -210,7 +207,7 @@ see the sections below for details: | [`File::FNM_SYSCASE`](#constant-filefnmsyscase) | Make the pattern use OS's case sensitivity. | -#### Constant File::FNM_CASEFOLD +### Constant File::FNM_CASEFOLD By default, filename matching is case-sensitive; use constant [`File::FNM_CASEFOLD`](#constant-filefnmcasefold) @@ -221,7 +218,7 @@ File.fnmatch('abc', 'ABC') # => false File.fnmatch('abc', 'ABC', File::FNM_CASEFOLD) # => true ``` -#### Constant File::FNM_DOTMATCH +### Constant File::FNM_DOTMATCH By default, filename matching does not allow pattern `'*'` to match a dotfile name (i.e, a filename beginning with a dot); @@ -232,7 +229,7 @@ to enable the match: File.fnmatch('*', '.document') # => false File.fnmatch('*', '.document', File::FNM_DOTMATCH) # => true ``` -#### Constant File::FNM_EXTGLOB +### Constant File::FNM_EXTGLOB By default, filename matching has the alternative notation disabled; use constant [`File::FNM_EXTGLOB`](#constant-filefnmextglob) @@ -261,7 +258,7 @@ File.fnmatch('{*ELLO,?????}', 'hello', File::FNM_EXTGLOB) # => true File.fnmatch('R{ub,foo,bar}y', 'Ruby') # => false ``` -#### Constant File::FNM_NOESCAPE +### Constant File::FNM_NOESCAPE By default filename matching has escaping enabled; use constant [`File::FNM_NOESCAPE`](#constant-filefnmnoescape) @@ -272,7 +269,7 @@ File.fnmatch('\*\?\*\*', '*?**') # => true File.fnmatch('\*\?\*\*', '*?**', File::FNM_NOESCAPE) # => false ``` -#### Constant File::FNM_PATHNAME +### Constant File::FNM_PATHNAME Flag [`File::FNM_PATHNAME`](#constant-filefnmpathname) affects patterns `'**'`, `'*'`, and `'?'`. @@ -316,7 +313,7 @@ File.fnmatch('foo?boo', 'foo/boo') # => true File.fnmatch('foo?boo', 'foo/boo', File::FNM_PATHNAME) # => false ``` -#### Constant File::FNM_SHORTNAME +### Constant File::FNM_SHORTNAME By default, Windows shortname matching is disabled; use constant [`File::FNM_SHORTNAME`](#constant-filefnmshortname) @@ -339,7 +336,7 @@ File.fnmatch('PROGRAM~1', 'Program Files') # => false File.fnmatch('PROGRAM~1', 'Program Files', File::FNM_SHORTNAME) # => true ``` -#### Constant File::FNM_SYSCASE +### Constant File::FNM_SYSCASE By default, filename matching uses Ruby's own case-sensitivity rules; use constant [`File::FNM_SYSCASE`](#constant-filefnmsyscase) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 110b5f6b32..82853633ba 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -724,7 +724,11 @@ static void State_compact(void *ptr) static size_t State_memsize(const void *ptr) { +#ifdef HAVE_RUBY_TYPED_EMBEDDABLE + return 0; +#else return sizeof(JSON_Generator_State); +#endif } static const rb_data_type_t JSON_Generator_State_type = { diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index d0482f6861..c0631728c3 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -243,7 +243,7 @@ static void rvalue_stack_mark(void *ptr) long index; if (stack && stack->ptr) { for (index = 0; index < stack->head; index++) { - rb_gc_mark(stack->ptr[index]); + rb_gc_mark_movable(stack->ptr[index]); } } } @@ -268,7 +268,22 @@ static void rvalue_stack_free(void *ptr) static size_t rvalue_stack_memsize(const void *ptr) { const rvalue_stack *stack = (const rvalue_stack *)ptr; - return sizeof(rvalue_stack) + sizeof(VALUE) * stack->capa; + size_t memsize = sizeof(VALUE) * stack->capa; +#ifndef HAVE_RUBY_TYPED_EMBEDDABLE + memsize += sizeof(rvalue_stack); +#endif + return memsize; +} + +static void rvalue_stack_compact(void *ptr) +{ + rvalue_stack *stack = (rvalue_stack *)ptr; + long index; + if (stack && stack->ptr) { + for (index = 0; index < stack->head; index++) { + stack->ptr[index] = rb_gc_location(stack->ptr[index]); + } + } } static const rb_data_type_t JSON_Parser_rvalue_stack_type = { @@ -277,7 +292,10 @@ static const rb_data_type_t JSON_Parser_rvalue_stack_type = { .dmark = rvalue_stack_mark, .dfree = rvalue_stack_free, .dsize = rvalue_stack_memsize, + .dcompact = rvalue_stack_compact, }, + // We deliberately don't declare rvalue_stack as RUBY_TYPED_WB_PROTECTED + // because it churns a lot of values so trigering write barriers every time is very costly. .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE, }; @@ -309,31 +327,60 @@ static void rvalue_stack_eagerly_release(VALUE handle) } } -static int convert_UTF32_to_UTF8(char *buf, uint32_t ch) -{ - int len = 1; - if (ch <= 0x7F) { - buf[0] = (char) ch; - } else if (ch <= 0x07FF) { - buf[0] = (char) ((ch >> 6) | 0xC0); - buf[1] = (char) ((ch & 0x3F) | 0x80); - len++; - } else if (ch <= 0xFFFF) { - buf[0] = (char) ((ch >> 12) | 0xE0); - buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); - buf[2] = (char) ((ch & 0x3F) | 0x80); - len += 2; - } else if (ch <= 0x1fffff) { - buf[0] =(char) ((ch >> 18) | 0xF0); - buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); - buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); - buf[3] =(char) ((ch & 0x3F) | 0x80); - len += 3; - } else { - buf[0] = '?'; - } - return len; -} +/* frame stack */ + +// Iterative (non-recursive) parsing keeps an explicit stack of the containers +// currently being built, instead of relying on the C call stack. Each frame +// only needs enough bookkeeping to close its container: which kind it is, the +// rvalue_stack position where its children start (so we know how many to pop), +// and the cursor at its opening brace (used to rewind for duplicate key +// errors). Frames hold no VALUEs, so this stack needs no GC marking; it reuses +// the same stack-allocated-with-heap-spill strategy as the rvalue_stack so that +// it's freed even if parsing raises. +// +// The lifecycle helpers below (grow/push/peek/pop/spill/free/eagerly_release +// and the rb_data_type_t) deliberately mirror their rvalue_stack counterparts +// -- the element type and the absence of a mark function are the only real +// differences. Keep the two in sync: a fix to the spill/release or +// HAVE_RUBY_TYPED_EMBEDDABLE handling in one almost certainly belongs in the +// other. +#define JSON_FRAME_STACK_INITIAL_CAPA 32 + +enum json_frame_type { + JSON_FRAME_ROOT, // == JSON_PHASE_DONE + JSON_FRAME_ARRAY, // == JSON_PHASE_ARRAY_COMMA + JSON_FRAME_OBJECT, // = JSON_PHASE_OBJECT_COMMA +}; + +// Where a frame is within its container's grammar. This is the entirety of the +// parser's "what to do next" state: json_parse_any dispatches on the top +// frame's phase and holds no resume state in C locals, so a parse can stop at +// any value boundary and be resumed purely from the (persistable) frame stack. +// +// The first three phases are deliberately equal to the corresponding json_frame_type +// to simplify the transition of phase in json_value_completed. +enum json_frame_phase { + JSON_PHASE_DONE = JSON_FRAME_ROOT, // root only: the document value has been parsed + JSON_PHASE_ARRAY_COMMA = JSON_FRAME_ARRAY, // after a value: expecting ',' or the closing ']' + JSON_PHASE_OBJECT_COMMA = JSON_FRAME_OBJECT, // after a value: expecting ',' or the closing '}' + JSON_PHASE_VALUE, // expecting a value (document root, array element, or object value after ':') + JSON_PHASE_OBJECT_KEY, // expecting a '"' key (after '{' or ',') + JSON_PHASE_OBJECT_COLON, // object only: after a key, expecting ':' +}; + +typedef struct json_frame_struct { + enum json_frame_type type; + enum json_frame_phase phase; + long value_stack_head; // rvalue_stack->head when this container opened + const char *start_cursor; // object frames only (the '{'); NULL otherwise +} json_frame; + +typedef struct json_frame_stack_struct { + enum rvalue_stack_type type; // shared with rvalue_stack: is ptr stack- or heap-allocated + long capa; + long head; + json_frame *ptr; +} json_frame_stack; enum duplicate_key_action { JSON_DEPRECATED = 0, @@ -356,17 +403,148 @@ typedef struct JSON_ParserStruct { } JSON_ParserConfig; typedef struct JSON_ParserStateStruct { - VALUE *stack_handle; + VALUE *value_stack_handle; + VALUE *frame_stack_handle; const char *start; const char *cursor; const char *end; - rvalue_stack *stack; + rvalue_stack *value_stack; + json_frame_stack *frames; rvalue_cache name_cache; int in_array; int current_nesting; unsigned int emitted_deprecations; } JSON_ParserState; +static json_frame_stack *json_frame_stack_spill(json_frame_stack *old_stack, VALUE *handle, json_frame_stack **stack_ref); + +static json_frame_stack *json_frame_stack_grow(json_frame_stack *stack, VALUE *handle, json_frame_stack **stack_ref) +{ + long required = stack->capa * 2; + + if (stack->type == RVALUE_STACK_STACK_ALLOCATED) { + stack = json_frame_stack_spill(stack, handle, stack_ref); + } else { + JSON_SIZED_REALLOC_N(stack->ptr, json_frame, required, stack->capa); + stack->capa = required; + } + return stack; +} + +static json_frame *json_frame_stack_push(JSON_ParserState *state, json_frame frame) +{ + json_frame_stack *stack = state->frames; + if (RB_UNLIKELY(stack->head >= stack->capa)) { + stack = json_frame_stack_grow(stack, state->frame_stack_handle, &state->frames); + } + + json_frame *frame_ptr = &stack->ptr[stack->head++]; + *frame_ptr = frame; + return frame_ptr; +} + +static inline json_frame *json_frame_stack_peek(json_frame_stack *stack) +{ + return &stack->ptr[stack->head - 1]; +} + +static inline void json_frame_stack_pop(json_frame_stack *stack) +{ + stack->head--; +} + +static void json_frame_stack_free_buffer(json_frame_stack *stack) +{ + JSON_SIZED_FREE_N(stack->ptr, stack->capa); + stack->ptr = NULL; +} + +static void json_frame_stack_free(void *ptr) +{ + json_frame_stack *stack = (json_frame_stack *)ptr; + if (stack) { + json_frame_stack_free_buffer(stack); +#ifndef HAVE_RUBY_TYPED_EMBEDDABLE + JSON_SIZED_FREE(stack); +#endif + } +} + +static size_t json_frame_stack_memsize(const void *ptr) +{ + const json_frame_stack *stack = (const json_frame_stack *)ptr; + + size_t memsize = sizeof(json_frame) * stack->capa; +#ifndef HAVE_RUBY_TYPED_EMBEDDABLE + memsize += sizeof(json_frame_stack); +#endif + return memsize; +} + +static const rb_data_type_t JSON_Parser_frame_stack_type = { + .wrap_struct_name = "JSON::Ext::Parser/frame_stack", + .function = { + .dmark = NULL, + .dfree = json_frame_stack_free, + .dsize = json_frame_stack_memsize, + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE, +}; + +static json_frame_stack *json_frame_stack_spill(json_frame_stack *old_stack, VALUE *handle, json_frame_stack **stack_ref) +{ + json_frame_stack *stack; + *handle = TypedData_Make_Struct(0, json_frame_stack, &JSON_Parser_frame_stack_type, stack); + *stack_ref = stack; + MEMCPY(stack, old_stack, json_frame_stack, 1); + + stack->capa = old_stack->capa << 1; + stack->ptr = ALLOC_N(json_frame, stack->capa); + stack->type = RVALUE_STACK_HEAP_ALLOCATED; + MEMCPY(stack->ptr, old_stack->ptr, json_frame, old_stack->head); + return stack; +} + +static void json_frame_stack_eagerly_release(VALUE handle) +{ + if (handle) { + json_frame_stack *stack; + TypedData_Get_Struct(handle, json_frame_stack, &JSON_Parser_frame_stack_type, stack); +#ifdef HAVE_RUBY_TYPED_EMBEDDABLE + json_frame_stack_free_buffer(stack); +#else + json_frame_stack_free(stack); + RTYPEDDATA_DATA(handle) = NULL; +#endif + } +} + +static int convert_UTF32_to_UTF8(char *buf, uint32_t ch) +{ + int len = 1; + if (ch <= 0x7F) { + buf[0] = (char) ch; + } else if (ch <= 0x07FF) { + buf[0] = (char) ((ch >> 6) | 0xC0); + buf[1] = (char) ((ch & 0x3F) | 0x80); + len++; + } else if (ch <= 0xFFFF) { + buf[0] = (char) ((ch >> 12) | 0xE0); + buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80); + buf[2] = (char) ((ch & 0x3F) | 0x80); + len += 2; + } else if (ch <= 0x1fffff) { + buf[0] =(char) ((ch >> 18) | 0xF0); + buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80); + buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80); + buf[3] =(char) ((ch & 0x3F) | 0x80); + len += 3; + } else { + buf[0] = '?'; + } + return len; +} + static inline size_t rest(JSON_ParserState *state) { return state->end - state->cursor; } @@ -890,8 +1068,8 @@ static inline VALUE json_decode_float(JSON_ParserConfig *config, uint64_t mantis static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig *config, long count) { - VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count)); - rvalue_stack_pop(state->stack, count); + VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->value_stack, count)); + rvalue_stack_pop(state->value_stack, count); if (config->freeze) { RB_OBJ_FREEZE(array); @@ -941,32 +1119,39 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d rb_exc_raise(parse_error_new(message, line, column)); } +NOINLINE(static) void json_on_duplicate_key(JSON_ParserState *state, JSON_ParserConfig *config, size_t count, const VALUE *pairs) +{ + switch (config->on_duplicate_key) { + case JSON_IGNORE: + return; + + case JSON_DEPRECATED: + // Only emit the first few deprecations to avoid spamming. + if (state->emitted_deprecations < 5) { + emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs)); + state->emitted_deprecations++; + } + return; + + case JSON_RAISE: + raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs)); + return; + } + UNREACHABLE; +} + static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count) { size_t entries_count = count / 2; VALUE object = rb_hash_new_capa(entries_count); - const VALUE *pairs = rvalue_stack_peek(state->stack, count); + const VALUE *pairs = rvalue_stack_peek(state->value_stack, count); rb_hash_bulk_insert(count, pairs, object); if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) { - switch (config->on_duplicate_key) { - case JSON_IGNORE: - break; - case JSON_DEPRECATED: - // Only emit the first few deprecations to avoid spamming. - if (state->emitted_deprecations < 5) { - emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs)); - state->emitted_deprecations++; - } - - break; - case JSON_RAISE: - raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs)); - break; - } + json_on_duplicate_key(state, config, count, pairs); } - rvalue_stack_pop(state->stack, count); + rvalue_stack_pop(state->value_stack, count); if (config->freeze) { RB_OBJ_FREEZE(object); @@ -980,7 +1165,7 @@ static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig * if (RB_UNLIKELY(config->on_load_proc)) { value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil); } - rvalue_stack_push(state->stack, value, state->stack_handle, &state->stack); + rvalue_stack_push(state->value_stack, value, state->value_stack_handle, &state->value_stack); return value; } @@ -1053,7 +1238,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi case '"': { VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions); state->cursor++; - return json_push_value(state, config, string); + return string; } case '\\': { if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) { @@ -1088,12 +1273,16 @@ ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_Pars raise_parse_error("unexpected end of input, expected closing \"", state); } + VALUE string; if (RB_LIKELY(*state->cursor == '"')) { - VALUE string = json_string_fastpath(state, config, start, state->cursor, is_name); + string = json_string_fastpath(state, config, start, state->cursor, is_name); state->cursor++; - return json_push_value(state, config, string); } - return json_parse_escaped_string(state, config, is_name, start); + else { + string = json_parse_escaped_string(state, config, is_name, start); + } + + return string; } #if JSON_CPU_LITTLE_ENDIAN_64BITS @@ -1242,215 +1431,339 @@ static inline VALUE json_parse_positive_number(JSON_ParserState *state, JSON_Par static inline VALUE json_parse_negative_number(JSON_ParserState *state, JSON_ParserConfig *config) { - const char *start = state->cursor; - state->cursor++; - return json_parse_number(state, config, true, start); + return json_parse_number(state, config, true, state->cursor - 1); +} + +// How many values (array elements, or interleaved object keys+values) have been +// pushed onto the rvalue stack since this container opened. Used to size the +// bulk decode on close, and to tell the first key/colon from later ones. +static inline long json_frame_entry_count(const json_frame *frame, const rvalue_stack *value_stack) +{ + return value_stack->head - frame->value_stack_head; +} + +// A complete value now sits on top of the rvalue stack. Advance the frame that +// was waiting for it: the root document is done, or the enclosing container +// moves on to expecting a ',' or its closing bracket. The caller passes the +// frame it already has in hand -- the one that was expecting the value -- which +// after a container close is the freshly re-exposed parent. +static inline void json_value_completed(json_frame *frame) +{ + JSON_ASSERT((int)JSON_PHASE_DONE == (int)JSON_FRAME_ROOT); + JSON_ASSERT((int)JSON_PHASE_ARRAY_COMMA == (int)JSON_FRAME_ARRAY); + JSON_ASSERT((int)JSON_PHASE_OBJECT_COMMA == (int)JSON_FRAME_OBJECT); + + frame->phase = (enum json_frame_phase) frame->type; } +ALWAYS_INLINE(static) bool json_match_keyword(JSON_ParserState *state, const char *keyword, size_t offset) +{ + // It is assumed that since `keyword` is always a literal, the compiler is able to constantize this + // `strlen` and several other computations in that routine, such as eliminating the `if (resumable)` branch. + + size_t len = strlen(keyword); + + // Note: memcmp with a small power of two and a literal string compile to an integer comparison / + // That's why we sometime compare starting from the first byte and sometimes from the second. + if (rest(state) >= len && (memcmp(state->cursor + offset, keyword + offset, len - offset) == 0)) { + state->cursor += len; + return true; + } + return false; +} + +// Parse an arbitrary JSON value iteratively. This is a state machine driven +// entirely by the top frame's phase so it can stop at any value boundary and +// resume purely from the frame stack. A JSON_FRAME_ROOT frame sits at the +// bottom of the stack, so the stack is never empty mid-parse and the document +// itself is just another frame whose value, once parsed, leaves its phase DONE. static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config) { - json_eat_whitespace(state); + json_frame *frame = json_frame_stack_peek(state->frames); - switch (peek(state)) { - case 'n': - if (rest(state) >= 4 && (memcmp(state->cursor, "null", 4) == 0)) { - state->cursor += 4; - return json_push_value(state, config, Qnil); - } + switch (frame->phase) { + case JSON_PHASE_DONE: goto JSON_PHASE_DONE; + case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA; + case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA; + case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE; + case JSON_PHASE_OBJECT_KEY: goto JSON_PHASE_OBJECT_KEY; + case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON; + } + UNREACHABLE_RETURN(Qundef); - raise_parse_error("unexpected token %s", state); - break; - case 't': - if (rest(state) >= 4 && (memcmp(state->cursor, "true", 4) == 0)) { - state->cursor += 4; - return json_push_value(state, config, Qtrue); - } + JSON_PHASE_DONE: { + // The root document value is parsed; it is the lone survivor on + // the rvalue stack. + return *rvalue_stack_peek(state->value_stack, 1); + } - raise_parse_error("unexpected token %s", state); - break; - case 'f': - // Note: memcmp with a small power of two compile to an integer comparison - if (rest(state) >= 5 && (memcmp(state->cursor + 1, "alse", 4) == 0)) { - state->cursor += 5; - return json_push_value(state, config, Qfalse); - } + JSON_PHASE_VALUE: { + json_eat_whitespace(state); - raise_parse_error("unexpected token %s", state); - break; - case 'N': - // Note: memcmp with a small power of two compile to an integer comparison - if (config->allow_nan && rest(state) >= 3 && (memcmp(state->cursor + 1, "aN", 2) == 0)) { - state->cursor += 3; - return json_push_value(state, config, CNaN); - } + VALUE value; + switch (peek(state)) { + case 'n': + if (json_match_keyword(state, "null", 0)) { + value = Qnil; + break; + } + raise_parse_error("unexpected token %s", state); - raise_parse_error("unexpected token %s", state); - break; - case 'I': - if (config->allow_nan && rest(state) >= 8 && (memcmp(state->cursor, "Infinity", 8) == 0)) { - state->cursor += 8; - return json_push_value(state, config, CInfinity); - } + case 't': + if (json_match_keyword(state, "true", 0)) { + value = Qtrue; + break; + } + raise_parse_error("unexpected token %s", state); - raise_parse_error("unexpected token %s", state); - break; - case '-': { - // Note: memcmp with a small power of two compile to an integer comparison - if (rest(state) >= 9 && (memcmp(state->cursor + 1, "Infinity", 8) == 0)) { - if (config->allow_nan) { - state->cursor += 9; - return json_push_value(state, config, CMinusInfinity); + case 'f': + if (json_match_keyword(state, "false", 1)) { + value = Qfalse; + break; + } + raise_parse_error("unexpected token %s", state); + + case 'N': + // Note: memcmp with a small power of two compile to an integer comparison + if (config->allow_nan && json_match_keyword(state, "NaN", 1)) { + value = CNaN; + break; + } + raise_parse_error("unexpected token %s", state); + + case 'I': + if (config->allow_nan && json_match_keyword(state, "Infinity", 0)) { + value = CInfinity; + break; + } + raise_parse_error("unexpected token %s", state); + + case '-': { + state->cursor++; + if (config->allow_nan && json_match_keyword(state, "Infinity", 0)) { + value = CMinusInfinity; } else { - raise_parse_error("unexpected token %s", state); + value = json_parse_negative_number(state, config); } + break; } - return json_push_value(state, config, json_parse_negative_number(state, config)); - break; - } - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - return json_push_value(state, config, json_parse_positive_number(state, config)); - break; - case '"': { - // %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"} - return json_parse_string(state, config, false); - break; - } - case '[': { - state->cursor++; - json_eat_whitespace(state); - long stack_head = state->stack->head; - if (peek(state) == ']') { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + value = json_parse_positive_number(state, config); + break; + + case '"': + // %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"} + value = json_parse_string(state, config, false); + break; + + case '[': { state->cursor++; - return json_push_value(state, config, json_decode_array(state, config, 0)); - } else { + json_eat_whitespace(state); + + if (peek(state) == ']') { + state->cursor++; + value = json_decode_array(state, config, 0); + break; + } + state->current_nesting++; if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); } state->in_array++; - json_parse_any(state, config); + + // Phase stays VALUE: the next iteration reads the first element. + frame = json_frame_stack_push(state, (json_frame){ + .type = JSON_FRAME_ARRAY, + .phase = JSON_PHASE_VALUE, + .value_stack_head = state->value_stack->head, + }); + goto JSON_PHASE_VALUE; } + case '{': { + const char *object_start_cursor = state->cursor; - while (true) { + state->cursor++; json_eat_whitespace(state); - const char next_char = peek(state); - - if (RB_LIKELY(next_char == ',')) { + if (peek(state) == '}') { state->cursor++; - if (config->allow_trailing_comma) { - json_eat_whitespace(state); - if (peek(state) == ']') { - continue; - } - } - json_parse_any(state, config); - continue; + value = json_decode_object(state, config, 0); + break; } - if (next_char == ']') { - state->cursor++; - long count = state->stack->head - stack_head; - state->current_nesting--; - state->in_array--; - return json_push_value(state, config, json_decode_array(state, config, count)); + state->current_nesting++; + if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { + rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); } - raise_parse_error("expected ',' or ']' after array value", state); + // Phase KEY: the next iteration reads the first key. + frame = json_frame_stack_push(state, (json_frame){ + .type = JSON_FRAME_OBJECT, + .phase = JSON_PHASE_OBJECT_KEY, + .value_stack_head = state->value_stack->head, + .start_cursor = object_start_cursor, + }); + goto JSON_PHASE_OBJECT_KEY; } - break; + + case 0: + raise_parse_error("unexpected end of input", state); + + default: + raise_parse_error("unexpected character: %s", state); } - case '{': { - const char *object_start_cursor = state->cursor; - state->cursor++; - json_eat_whitespace(state); - long stack_head = state->stack->head; + json_push_value(state, config, value); + json_value_completed(frame); - if (peek(state) == '}') { - state->cursor++; - return json_push_value(state, config, json_decode_object(state, config, 0)); - } else { - state->current_nesting++; - if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) { - rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting); - } + switch (frame->phase) { + case JSON_PHASE_DONE: goto JSON_PHASE_DONE; + case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA; + case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA; + case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE; + case JSON_PHASE_OBJECT_KEY: UNREACHABLE_RETURN(Qundef); + case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON; + } + UNREACHABLE_RETURN(Qundef); + } - if (peek(state) != '"') { - raise_parse_error("expected object key, got %s", state); - } - json_parse_string(state, config, true); + JSON_PHASE_OBJECT_KEY: { + JSON_ASSERT(frame->type == JSON_FRAME_OBJECT); - json_eat_whitespace(state); - if (peek(state) != ':') { - raise_parse_error("expected ':' after object key", state); - } - state->cursor++; + json_eat_whitespace(state); - json_parse_any(state, config); + if (RB_LIKELY(peek(state) == '"')) { + json_push_value(state, config, json_parse_string(state, config, true)); + frame->phase = JSON_PHASE_OBJECT_COLON; + goto JSON_PHASE_OBJECT_COLON; + } else { + // The message differs for the first key vs. a key after a + // ',': the first is the only one reached with nothing pushed + // for this object yet. + if (json_frame_entry_count(frame, state->value_stack) == 0) { + raise_parse_error("expected object key, got %s", state); + } else { + raise_parse_error("expected object key, got: %s", state); } + } + UNREACHABLE_RETURN(Qundef); + } - while (true) { - json_eat_whitespace(state); + JSON_PHASE_OBJECT_COLON: { + JSON_ASSERT(frame->type == JSON_FRAME_OBJECT); - const char next_char = peek(state); - if (next_char == '}') { - state->cursor++; - state->current_nesting--; - size_t count = state->stack->head - stack_head; + json_eat_whitespace(state); - // Temporary rewind cursor in case an error is raised - const char *final_cursor = state->cursor; - state->cursor = object_start_cursor; - VALUE object = json_decode_object(state, config, count); - state->cursor = final_cursor; + if (RB_LIKELY(peek(state) == ':')) { + state->cursor++; + frame->phase = JSON_PHASE_VALUE; + goto JSON_PHASE_VALUE; + } else { + // First colon (only the first pair's key is pushed, nothing + // else) vs. a later one. + if (json_frame_entry_count(frame, state->value_stack) == 1) { + raise_parse_error("expected ':' after object key", state); + } else { + raise_parse_error("expected ':' after object key, got: %s", state); + } + } + UNREACHABLE_RETURN(Qundef); + } - return json_push_value(state, config, object); - } + JSON_PHASE_ARRAY_COMMA: { + JSON_ASSERT(frame->type == JSON_FRAME_ARRAY); - if (next_char == ',') { - state->cursor++; - json_eat_whitespace(state); + json_eat_whitespace(state); - if (config->allow_trailing_comma) { - if (peek(state) == '}') { - continue; - } - } + const char next_char = peek(state); - if (RB_UNLIKELY(peek(state) != '"')) { - raise_parse_error("expected object key, got: %s", state); - } - json_parse_string(state, config, true); + if (RB_LIKELY(next_char == ',')) { + state->cursor++; + if (config->allow_trailing_comma) { + json_eat_whitespace(state); + if (peek(state) == ']') { + // Trailing comma: stay in COMMA to close on the next iteration. + goto JSON_PHASE_ARRAY_COMMA; + } + } + frame->phase = JSON_PHASE_VALUE; + goto JSON_PHASE_VALUE; + } else if (next_char == ']') { + state->cursor++; + long count = json_frame_entry_count(frame, state->value_stack); + state->current_nesting--; + state->in_array--; + json_frame_stack_pop(state->frames); + json_push_value(state, config, json_decode_array(state, config, count)); + frame = json_frame_stack_peek(state->frames); + json_value_completed(frame); + + switch (frame->phase) { + case JSON_PHASE_DONE: goto JSON_PHASE_DONE; + case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA; + case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA; + case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE; + case JSON_PHASE_OBJECT_KEY: UNREACHABLE_RETURN(Qundef); + case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON; + } + } else { + raise_parse_error("expected ',' or ']' after array value", state); + } + UNREACHABLE_RETURN(Qundef); + } - json_eat_whitespace(state); - if (RB_UNLIKELY(peek(state) != ':')) { - raise_parse_error("expected ':' after object key, got: %s", state); - } - state->cursor++; + JSON_PHASE_OBJECT_COMMA: { + JSON_ASSERT(frame->type == JSON_FRAME_OBJECT); - json_parse_any(state, config); + json_eat_whitespace(state); + const char next_char = peek(state); + + if (RB_LIKELY(next_char == ',')) { + state->cursor++; - continue; + if (config->allow_trailing_comma) { + json_eat_whitespace(state); + if (peek(state) == '}') { + // Trailing comma: stay in COMMA to close on the next iteration. + goto JSON_PHASE_OBJECT_COMMA; } + } - raise_parse_error("expected ',' or '}' after object value, got: %s", state); + frame->phase = JSON_PHASE_OBJECT_KEY; + goto JSON_PHASE_OBJECT_KEY; + } else if (next_char == '}') { + state->cursor++; + state->current_nesting--; + size_t count = json_frame_entry_count(frame, state->value_stack); + + // Temporary rewind cursor in case an error is raised + const char *final_cursor = state->cursor; + state->cursor = frame->start_cursor; + VALUE object = json_decode_object(state, config, count); + state->cursor = final_cursor; + + json_push_value(state, config, object); + json_frame_stack_pop(state->frames); + frame = json_frame_stack_peek(state->frames); + json_value_completed(frame); + + switch (frame->phase) { + case JSON_PHASE_DONE: goto JSON_PHASE_DONE; + case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA; + case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA; + case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE; + case JSON_PHASE_OBJECT_KEY: UNREACHABLE_RETURN(Qundef); + case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON; } - break; + } else { + raise_parse_error("expected ',' or '}' after object value, got: %s", state); } - - case 0: - raise_parse_error("unexpected end of input", state); - break; - - default: - raise_parse_error("unexpected character: %s", state); - break; + UNREACHABLE_RETURN(Qundef); } - raise_parse_error("unreachable: %s", state); - return Qundef; + UNREACHABLE_RETURN(Qundef); } static void json_ensure_eof(JSON_ParserState *state) @@ -1616,24 +1929,42 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src) } VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA]; - rvalue_stack stack = { + rvalue_stack value_stack = { .type = RVALUE_STACK_STACK_ALLOCATED, .ptr = rvalue_stack_buffer, .capa = RVALUE_STACK_INITIAL_CAPA, }; + // Seed the frame stack with the root frame, establishing the invariant that + // json_parse_any always has a top frame to dispatch on (so the stack is never + // empty mid-parse). + json_frame frame_stack_buffer[JSON_FRAME_STACK_INITIAL_CAPA]; + frame_stack_buffer[0] = (json_frame){ + .type = JSON_FRAME_ROOT, + .phase = JSON_PHASE_VALUE, + }; + json_frame_stack frames = { + .type = RVALUE_STACK_STACK_ALLOCATED, + .ptr = frame_stack_buffer, + .capa = JSON_FRAME_STACK_INITIAL_CAPA, + .head = 1, + }; + long len; const char *start; RSTRING_GETMEM(Vsource, start, len); - VALUE stack_handle = 0; + VALUE value_stack_handle = 0; + VALUE frame_stack_handle = 0; JSON_ParserState _state = { .start = start, .cursor = start, .end = start + len, - .stack = &stack, - .stack_handle = &stack_handle, + .value_stack = &value_stack, + .value_stack_handle = &value_stack_handle, + .frames = &frames, + .frame_stack_handle = &frame_stack_handle, }; JSON_ParserState *state = &_state; @@ -1641,8 +1972,10 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src) // This may be skipped in case of exception, but // it won't cause a leak. - rvalue_stack_eagerly_release(stack_handle); - RB_GC_GUARD(stack_handle); + rvalue_stack_eagerly_release(value_stack_handle); + json_frame_stack_eagerly_release(frame_stack_handle); + RB_GC_GUARD(value_stack_handle); + RB_GC_GUARD(frame_stack_handle); RB_GC_GUARD(Vsource); json_ensure_eof(state); @@ -1674,21 +2007,33 @@ static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts) static void JSON_ParserConfig_mark(void *ptr) { JSON_ParserConfig *config = ptr; - rb_gc_mark(config->on_load_proc); - rb_gc_mark(config->decimal_class); + rb_gc_mark_movable(config->on_load_proc); + rb_gc_mark_movable(config->decimal_class); } static size_t JSON_ParserConfig_memsize(const void *ptr) { +#ifdef HAVE_RUBY_TYPED_EMBEDDABLE + return 0; +#else return sizeof(JSON_ParserConfig); +#endif +} + +static void JSON_ParserConfig_compact(void *ptr) +{ + JSON_ParserConfig *config = ptr; + config->on_load_proc = rb_gc_location(config->on_load_proc); + config->decimal_class = rb_gc_location(config->decimal_class); } static const rb_data_type_t JSON_ParserConfig_type = { .wrap_struct_name = "JSON::Ext::Parser/ParserConfig", .function = { - JSON_ParserConfig_mark, - RUBY_DEFAULT_FREE, - JSON_ParserConfig_memsize, + .dmark = JSON_ParserConfig_mark, + .dfree = RUBY_DEFAULT_FREE, + .dsize = JSON_ParserConfig_memsize, + .dcompact = JSON_ParserConfig_compact, }, .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE, }; diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c index 1c18bf02ee..74d793a6e2 100644 --- a/ext/objspace/object_tracing.c +++ b/ext/objspace/object_tracing.c @@ -22,7 +22,6 @@ struct traceobj_arg { int running; int keep_remains; VALUE newobj_trace; - VALUE freeobj_trace; st_table *object_table; /* obj (VALUE) -> allocation_info */ st_table *str_table; /* cstr -> refcount */ struct traceobj_arg *prev_traceobj_arg; @@ -96,13 +95,11 @@ newobj_i(VALUE tpval, void *data) st_data_t v; if (st_lookup(arg->object_table, (st_data_t)obj, &v)) { + /* keep_remains kept this slot's entry after its object was freed. The + * allocator has now reused that address, so recycle the dead entry's + * info. A living entry here would mean two live objects at one address. */ info = (struct allocation_info *)v; - if (arg->keep_remains) { - if (info->living) { - /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */ - } - } - /* reuse info */ + assert(!info->living); delete_unique_str(arg->str_table, info->path); delete_unique_str(arg->str_table, info->class_path); } @@ -121,37 +118,6 @@ newobj_i(VALUE tpval, void *data) st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info); } -static void -freeobj_i(VALUE tpval, void *data) -{ - struct traceobj_arg *arg = (struct traceobj_arg *)data; - rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval); - st_data_t obj = (st_data_t)rb_tracearg_object(tparg); - st_data_t v; - struct allocation_info *info; - - /* Modifying the st table can cause allocations, which can trigger GC. - * Since freeobj_i is called during GC, it must not trigger another GC. */ - VALUE gc_disabled = rb_gc_disable_no_rest(); - - if (arg->keep_remains) { - if (st_lookup(arg->object_table, obj, &v)) { - info = (struct allocation_info *)v; - info->living = 0; - } - } - else { - if (st_delete(arg->object_table, &obj, &v)) { - info = (struct allocation_info *)v; - delete_unique_str(arg->str_table, info->path); - delete_unique_str(arg->str_table, info->class_path); - ruby_xfree(info); - } - } - - if (gc_disabled == Qfalse) rb_gc_enable(); -} - static int free_keys_i(st_data_t key, st_data_t value, st_data_t data) { @@ -171,7 +137,6 @@ allocation_info_tracer_mark(void *ptr) { struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr; rb_gc_mark(trace_arg->newobj_trace); - rb_gc_mark(trace_arg->freeobj_trace); } static void @@ -198,14 +163,46 @@ allocation_info_tracer_memsize(const void *ptr) } static int +allocation_info_tracer_weak_reference_i(st_data_t key, st_data_t value, st_data_t data) +{ + struct traceobj_arg *arg = (struct traceobj_arg *)data; + struct allocation_info *info = (struct allocation_info *)value; + + if (rb_gc_handle_weak_references_alive_p((VALUE)key)) { + return ST_CONTINUE; + } + + /* Object was collected. keep_remains keeps the dead entry for reporting. */ + if (arg->keep_remains) { + info->living = 0; + return ST_CONTINUE; + } + else { + delete_unique_str(arg->str_table, info->path); + delete_unique_str(arg->str_table, info->class_path); + ruby_xfree(info); + return ST_DELETE; + } +} + +static void +allocation_info_tracer_weak_reference(void *ptr) +{ + struct traceobj_arg *arg = (struct traceobj_arg *)ptr; + + st_foreach(arg->object_table, allocation_info_tracer_weak_reference_i, (st_data_t)arg); +} + +static int allocation_info_tracer_compact_update_object_table_i(st_data_t key, st_data_t value, st_data_t data) { st_table *table = (st_table *)data; + struct allocation_info *info = (struct allocation_info *)value; - if (!rb_gc_pointer_to_heap_p(key)) { - struct allocation_info *info = (struct allocation_info *)value; - xfree(info); - return ST_DELETE; + /* In keep_remains mode the table keeps entries for freed objects. Their keys + * are dangling, so skip them instead of passing them to rb_gc_location. */ + if (!info->living) { + return ST_CONTINUE; } if (key != rb_gc_location(key)) { @@ -242,6 +239,7 @@ static const rb_data_type_t allocation_info_tracer_type = { allocation_info_tracer_free, /* Never called because global */ allocation_info_tracer_memsize, allocation_info_tracer_compact, + allocation_info_tracer_weak_reference, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; @@ -260,9 +258,10 @@ get_traceobj_arg(void) tmp_trace_arg->running = 0; tmp_trace_arg->keep_remains = tmp_keep_remains; tmp_trace_arg->newobj_trace = 0; - tmp_trace_arg->freeobj_trace = 0; tmp_trace_arg->object_table = st_init_numtable(); tmp_trace_arg->str_table = st_init_strtable(); + + rb_gc_declare_weak_references(obj); } return tmp_trace_arg; } @@ -284,10 +283,8 @@ trace_object_allocations_start(VALUE self) else { if (arg->newobj_trace == 0) { arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg); - arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg); } rb_tracepoint_enable(arg->newobj_trace); - rb_tracepoint_enable(arg->freeobj_trace); } return Qnil; @@ -315,9 +312,6 @@ trace_object_allocations_stop(VALUE self) if (arg->newobj_trace != 0) { rb_tracepoint_disable(arg->newobj_trace); } - if (arg->freeobj_trace != 0) { - rb_tracepoint_disable(arg->freeobj_trace); - } } return Qnil; diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c index f1e9e42524..0e17d9e873 100644 --- a/ext/socket/ancdata.c +++ b/ext/socket/ancdata.c @@ -711,6 +711,9 @@ anc_inspect_passcred_credentials(int level, int type, VALUE data, VALUE ret) static int anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret) { + long len; + const char *ptr; + if (level != SOL_SOCKET && type != SCM_CREDS) return 0; @@ -725,48 +728,59 @@ anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret) * This heuristics works well except when sc_ngroups == CMGROUP_MAX. */ + RSTRING_GETMEM(data, ptr, len); #if defined(HAVE_TYPE_STRUCT_CMSGCRED) /* FreeBSD */ - if (RSTRING_LEN(data) == sizeof(struct cmsgcred)) { + if (len == sizeof(struct cmsgcred)) { struct cmsgcred cred; - memcpy(&cred, RSTRING_PTR(data), sizeof(struct cmsgcred)); + int ngroups; + memcpy(&cred, ptr, sizeof(struct cmsgcred)); rb_str_catf(ret, " pid=%u", cred.cmcred_pid); rb_str_catf(ret, " uid=%u", cred.cmcred_uid); rb_str_catf(ret, " euid=%u", cred.cmcred_euid); rb_str_catf(ret, " gid=%u", cred.cmcred_gid); - if (cred.cmcred_ngroups) { + rb_str_catf(ret, " groups[%d]=[", cred.cmcred_ngroups); + ngroups = cred.cmcred_ngroups; + if (ngroups > 0) { int i; - const char *sep = " groups="; - for (i = 0; i < cred.cmcred_ngroups; i++) { - rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]); - sep = ","; + rb_str_catf(ret, "%u", cred.cmcred_groups[0]); + if (ngroups > CMGROUP_MAX) ngroups = CMGROUP_MAX; + for (i = 1; i < ngroups; i++) { + rb_str_catf(ret, ",%u", cred.cmcred_groups[i]); } } - rb_str_cat2(ret, " (cmsgcred)"); + rb_str_cat2(ret, "] (cmsgcred)"); return 1; } #endif #if defined(HAVE_TYPE_STRUCT_SOCKCRED) /* FreeBSD, NetBSD */ - if ((size_t)RSTRING_LEN(data) >= SOCKCREDSIZE(0)) { - struct sockcred cred0, *cred; - memcpy(&cred0, RSTRING_PTR(data), SOCKCREDSIZE(0)); - if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) { - cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups)); - memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups)); - rb_str_catf(ret, " uid=%u", cred->sc_uid); - rb_str_catf(ret, " euid=%u", cred->sc_euid); - rb_str_catf(ret, " gid=%u", cred->sc_gid); - rb_str_catf(ret, " egid=%u", cred->sc_egid); - if (cred0.sc_ngroups) { - int i; - const char *sep = " groups="; - for (i = 0; i < cred0.sc_ngroups; i++) { - rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]); - sep = ","; - } + if ((size_t)len >= SOCKCREDSIZE(0)) { + struct sockcred cred; + int ngroups; + memcpy(&cred, ptr, SOCKCREDSIZE(0)); + rb_str_catf(ret, " uid=%u", cred.sc_uid); + rb_str_catf(ret, " euid=%u", cred.sc_euid); + rb_str_catf(ret, " gid=%u", cred.sc_gid); + rb_str_catf(ret, " egid=%u", cred.sc_egid); + rb_str_catf(ret, " groups[%d]=[", cred.sc_ngroups); + ngroups = cred.sc_ngroups; + if (ngroups <= 0) { + ngroups = 0; + } + else { + size_t max = ((size_t)len - SOCKCREDSIZE(0)) / sizeof(gid_t); + if ((size_t)ngroups > max) ngroups = (int)max; + } + if (ngroups > 0) { + int i; + const void *gp = ptr + offsetof(struct sockcred, sc_groups); + const gid_t *groups = MEMCPY(ALLOCA_N(gid_t, ngroups), gp, gid_t, ngroups); + rb_str_catf(ret, "%u", groups[0]); + for (i = 1; i < ngroups; i++) { + rb_str_catf(ret, ",%u", groups[i]); } - rb_str_cat2(ret, " (sockcred)"); - return 1; } + rb_str_cat2(ret, "] (sockcred)"); + return 1; } #endif return 0; @@ -1000,6 +1014,7 @@ ancillary_inspect(VALUE self) rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(vtype)); else rb_str_catf(ret, " cmsg_type:%d", type); + RB_GC_GUARD(vtype); } else { rb_str_catf(ret, " cmsg_level:%d", level); diff --git a/ext/zlib/zlib.gemspec b/ext/zlib/zlib.gemspec index 345dc5f225..ba7114476f 100644 --- a/ext/zlib/zlib.gemspec +++ b/ext/zlib/zlib.gemspec @@ -27,5 +27,5 @@ Gem::Specification.new do |spec| spec.executables = [] spec.require_paths = ["lib"] spec.extensions = "ext/zlib/extconf.rb" - spec.required_ruby_version = ">= 2.5.0" + spec.required_ruby_version = ">= 2.7.0" end @@ -3653,11 +3653,12 @@ has_drive_letter(const char *buf) } #ifndef _WIN32 -static char* +static VALUE getcwdofdrv(int drv) { char drive[4]; - char *drvcwd, *oldcwd; + char *oldcwd; + VALUE drvcwd; drive[0] = drv; drive[1] = ':'; @@ -3669,13 +3670,13 @@ getcwdofdrv(int drv) */ oldcwd = ruby_getcwd(); if (chdir(drive) == 0) { - drvcwd = ruby_getcwd(); + drvcwd = rb_dir_getwd_ospath(); chdir(oldcwd); xfree(oldcwd); } else { /* perhaps the drive is not exist. we return only drive letter */ - drvcwd = strdup(drive); + drvcwd = rb_enc_str_new_cstr(drive, rb_filesystem_encoding()); } return drvcwd; } @@ -4045,16 +4046,19 @@ ospath_new(const char *ptr, long len, rb_encoding *fsenc) } static char * -append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encoding *fsenc) +append_fspath(VALUE result, VALUE fname, VALUE dirname, rb_encoding **enc, rb_encoding *fsenc) { - char *buf, *cwdp = dir; - VALUE dirname = Qnil; - size_t dirlen = strlen(dir), buflen = rb_str_capacity(result); + if (RB_UNLIKELY(!rb_enc_asciicompat(fsenc) || rb_enc_str_coderange(dirname) != ENC_CODERANGE_7BIT)) { + dirname = rb_str_new_shared(dirname); + rb_enc_associate(dirname, fsenc); + } + + char *buf, *cwdp; + size_t dirlen = RSTRING_LEN(dirname); + size_t buflen = rb_str_capacity(result); if (NORMALIZE_UTF8PATH || *enc != fsenc) { - dirname = ospath_new(dir, dirlen, fsenc); if (!rb_enc_compatible(fname, dirname)) { - xfree(dir); /* rb_enc_check must raise because the two encodings are not * compatible. */ rb_enc_check(fname, dirname); @@ -4063,19 +4067,15 @@ append_fspath(VALUE result, VALUE fname, char *dir, rb_encoding **enc, rb_encodi rb_encoding *direnc = fs_enc_check(fname, dirname); if (direnc != fsenc) { dirname = rb_str_conv_enc(dirname, fsenc, direnc); - RSTRING_GETMEM(dirname, cwdp, dirlen); - } - else if (NORMALIZE_UTF8PATH) { - RSTRING_GETMEM(dirname, cwdp, dirlen); } *enc = direnc; } + + RSTRING_GETMEM(dirname, cwdp, dirlen); do {buflen *= 2;} while (dirlen > buflen); rb_str_resize(result, buflen); buf = RSTRING_PTR(result); memcpy(buf, cwdp, dirlen); - xfree(dir); - if (!NIL_P(dirname)) rb_str_resize(dirname, 0); rb_enc_associate(result, *enc); return buf + dirlen; } @@ -4177,7 +4177,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na p = pend; } else { - char *e = append_fspath(result, fname, ruby_getcwd(), &enc, fsenc); + char *e = append_fspath(result, fname, rb_dir_getwd_ospath(), &enc, fsenc); BUFINIT(); p = e; } @@ -1856,10 +1856,12 @@ os_each_obj(int argc, VALUE *argv, VALUE os) /* * call-seq: - * ObjectSpace.undefine_finalizer(obj) + * ObjectSpace.undefine_finalizer(obj) -> obj * - * Removes all finalizers for <i>obj</i>. + * Removes all finalizers registered for +obj+ with + * ObjectSpace.define_finalizer, and returns +obj+. * + * Does nothing if +obj+ has no finalizers. */ static VALUE diff --git a/gems/bundled_gems b/gems/bundled_gems index 8e244af598..14f79ce9ae 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -9,7 +9,7 @@ minitest 6.0.6 https://github.com/minitest/minitest power_assert 3.0.1 https://github.com/ruby/power_assert rake 13.4.2 https://github.com/ruby/rake -test-unit 3.7.7 https://github.com/test-unit/test-unit +test-unit 3.7.8 https://github.com/test-unit/test-unit rexml 3.4.4 https://github.com/ruby/rexml rss 0.3.2 https://github.com/ruby/rss net-imap 0.6.4 https://github.com/ruby/net-imap diff --git a/internal/gc.h b/internal/gc.h index 8b136e6572..e21fb89267 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -210,9 +210,6 @@ size_t rb_gc_heap_id_for_size(size_t size); void rb_gc_mark_and_move(VALUE *ptr); -void rb_gc_declare_weak_references(VALUE obj); -bool rb_gc_handle_weak_references_alive_p(VALUE obj); - void rb_gc_ref_update_table_values_only(st_table *tbl); void rb_gc_initial_stress_set(VALUE flag); @@ -233,6 +230,8 @@ void rb_objspace_reachable_objects_from_root(void (func)(const char *category, V int rb_objspace_internal_object_p(VALUE obj); int rb_objspace_garbage_object_p(VALUE obj); bool rb_gc_pointer_to_heap_p(VALUE obj); +void rb_gc_declare_weak_references(VALUE obj); +bool rb_gc_handle_weak_references_alive_p(VALUE obj); void rb_objspace_each_objects( int (*callback)(void *start, void *end, size_t stride, void *data), diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 422b726980..753e9987d5 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -456,11 +456,27 @@ module Bundler def cooldown_excluded?(spec) return false unless spec.respond_to?(:created_at) && spec.created_at return false unless spec.respond_to?(:remote) && spec.remote + return false if pinned_by_lockfile_floor?(spec) days = spec.remote.effective_cooldown return false if days.nil? || days <= 0 (cooldown_now - spec.created_at) < (days * 86_400) end + # A spec sitting exactly at a `>= locked_version` prevent-downgrade floor is + # the version the lockfile currently pins. `bundle update` and `bundle + # outdated` install that floor so resolution never moves a gem backwards. + # Filtering it out for cooldown would then make resolution impossible + # whenever the locked version is itself inside the cooldown window, which is + # exactly what happens to a lockfile written before cooldown was enabled. + # Keep it eligible; gems being explicitly updated carry an exact `=` + # requirement instead and stay subject to the cooldown filter. + def pinned_by_lockfile_floor?(spec) + return false unless defined?(@base) && @base + requirement = base_requirements[spec.name] + return false unless requirement && !requirement.exact? + requirement.requirements.any? {|op, version| op == ">=" && version == spec.version } + end + def cooldown_now @cooldown_now ||= Time.now end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 15d6aac0fd..a6e1dc4730 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -299,7 +299,7 @@ class Gem::Installer File.chmod(dir_mode, gem_dir) if dir_mode - say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil? + say clean_text(spec.post_install_message.to_s) if options[:post_install_message] && !spec.post_install_message.nil? Gem::Specification.add_spec(spec) unless @install_dir @@ -712,6 +712,18 @@ class Gem::Installer if spec.dependencies.any? {|dep| dep.name =~ /(?:\R|[<>])/ } raise Gem::InstallError, "#{spec} has an invalid dependencies" end + + if spec.executables.any? {|name| !name.is_a?(String) || name != File.basename(name) || /\A\.\.?\z|\R/.match?(name) } + raise Gem::InstallError, "#{spec} has an invalid executable" + end + + raise Gem::InstallError, "#{spec} has an invalid bindir" unless spec.bindir.is_a?(String) + + expanded_gem_dir = File.expand_path(gem_dir) + expanded_bindir = File.expand_path(File.join(gem_dir, spec.bindir)) + unless expanded_bindir == expanded_gem_dir || expanded_bindir.start_with?("#{expanded_gem_dir}/") + raise Gem::InstallError, "#{spec} has an invalid bindir" + end end ## @@ -720,6 +732,7 @@ class Gem::Installer def app_script_text(bin_file_name) # NOTE: that the `load` lines cannot be indented, as old RG versions match # against the beginning of the line + escaped_bin_file_name = bin_file_name.gsub(/[\\']/) {|c| "\\#{c}" } <<~TEXT #{shebang bin_file_name} # @@ -743,9 +756,9 @@ class Gem::Installer end if Gem.respond_to?(:activate_and_load_bin_path) - Gem.activate_and_load_bin_path('#{spec.name}', '#{bin_file_name}', version) + Gem.activate_and_load_bin_path('#{spec.name}', '#{escaped_bin_file_name}', version) else - load Gem.activate_bin_path('#{spec.name}', '#{bin_file_name}', version) + load Gem.activate_bin_path('#{spec.name}', '#{escaped_bin_file_name}', version) end TEXT end diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb index 88d4ce59b4..0550dc473d 100644 --- a/lib/rubygems/text.rb +++ b/lib/rubygems/text.rb @@ -8,7 +8,16 @@ module Gem::Text # Remove any non-printable characters and make the text suitable for # printing. def clean_text(text) - text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".") + text = text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".") + + # Match C1 control characters (U+0080-U+009F) as codepoints. This requires + # a valid UTF-8 string so the regexp does not split a multibyte sequence; + # strings in other encodings are left unchanged. + if text.encoding == Encoding::UTF_8 && text.valid_encoding? + text = text.gsub(/[\u0080-\u009f]/, ".") + end + + text end def truncate_text(text, description, max_length = 100_000) diff --git a/spec/bundler/install/cooldown_spec.rb b/spec/bundler/install/cooldown_spec.rb index b3f57d93cc..bad7b7cf34 100644 --- a/spec/bundler/install/cooldown_spec.rb +++ b/spec/bundler/install/cooldown_spec.rb @@ -87,6 +87,26 @@ RSpec.describe "bundle install with the cooldown setting" do build_gem "ripe_gem", "2.0.0" do |s| s.date = now - (1 * 86_400) end + + # parent only resolves with the in-cooldown child 2.0.0 + build_gem "child", "1.0.0" do |s| + s.date = now - (30 * 86_400) + end + build_gem "child", "2.0.0" do |s| + s.date = now - (1 * 86_400) + end + build_gem "parent", "1.0.0" do |s| + s.add_dependency "child", ">= 2.0.0" + s.date = now - (30 * 86_400) + end + + # a cooldown-eligible version exists above the in-cooldown locked one + build_gem "upgradable", "2.0.0" do |s| + s.date = now - (1 * 86_400) + end + build_gem "upgradable", "3.0.0" do |s| + s.date = now - (30 * 86_400) + end end end @@ -268,5 +288,146 @@ RSpec.describe "bundle install with the cooldown setting" do expect(err).to match(/excluded by the cooldown setting/) expect(err).to match(/--cooldown 0/) end + + it "keeps an in-cooldown locked version on bundle update --all instead of failing" do + # Lockfile written before cooldown was enabled pins the now-in-cooldown + # latest version. A full update must not downgrade below it, and cooldown + # must not filter it out, otherwise resolution becomes impossible (#9598). + gemfile <<-G + source "https://gem.repo3" + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (2.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update --all --cooldown 7", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("ripe_gem 2.0.0") + end + + it "does not fail bundle outdated when the locked version is in cooldown" do + gemfile <<-G + source "https://gem.repo3" + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (2.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "outdated --cooldown 7", artifice: "compact_index_cooldown", raise_on_error: false + + # exit 0 means no outdated gems and, crucially, no resolution failure (exit 7) + expect(exitstatus).to eq(0) + end + + it "still applies cooldown and downgrades a gem that is updated explicitly" do + gemfile <<-G + source "https://gem.repo3" + gem "ripe_gem" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + ripe_gem (2.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ripe_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update ripe_gem --cooldown 7", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("ripe_gem 1.0.0") + end + + it "keeps an in-cooldown transitive dependency on bundle update --all" do + gemfile <<-G + source "https://gem.repo3" + gem "parent" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + child (2.0.0) + parent (1.0.0) + child (>= 2.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + parent + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update --all --cooldown 7", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("parent 1.0.0", "child 2.0.0") + end + + it "still upgrades to a cooldown-eligible version above the locked one" do + gemfile <<-G + source "https://gem.repo3" + gem "upgradable" + G + + lockfile <<-L + GEM + remote: https://gem.repo3/ + specs: + upgradable (2.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + upgradable + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update --all --cooldown 7", artifice: "compact_index_cooldown" + + expect(the_bundle).to include_gems("upgradable 3.0.0") + end end end diff --git a/spec/mspec/Gemfile b/spec/mspec/Gemfile index 617a995cad..e57acd6435 100644 --- a/spec/mspec/Gemfile +++ b/spec/mspec/Gemfile @@ -1,4 +1,4 @@ source 'https://rubygems.org' -gem "rake", "~> 12.3" +gem "rake" gem "rspec", "~> 3.0" diff --git a/spec/mspec/Gemfile.lock b/spec/mspec/Gemfile.lock index cd39906044..075337d671 100644 --- a/spec/mspec/Gemfile.lock +++ b/spec/mspec/Gemfile.lock @@ -2,7 +2,7 @@ GEM remote: https://rubygems.org/ specs: diff-lcs (1.4.4) - rake (12.3.3) + rake (13.4.2) rspec (3.10.0) rspec-core (~> 3.10.0) rspec-expectations (~> 3.10.0) @@ -22,5 +22,5 @@ PLATFORMS ruby DEPENDENCIES - rake (~> 12.3) + rake rspec (~> 3.0) diff --git a/spec/mspec/lib/mspec/matchers/base.rb b/spec/mspec/lib/mspec/matchers/base.rb index 3534520d88..4056e73a81 100644 --- a/spec/mspec/lib/mspec/matchers/base.rb +++ b/spec/mspec/lib/mspec/matchers/base.rb @@ -1,3 +1,5 @@ +require 'mspec/utils/deprecate' + module MSpecMatchers end diff --git a/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb b/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb index fdf3736ac2..230cea2e9b 100644 --- a/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb +++ b/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb @@ -21,6 +21,7 @@ end module MSpecMatchers private def be_an_instance_of(expected) + MSpec.deprecate __method__, '.should.instance_of?' BeAnInstanceOfMatcher.new(expected) end end diff --git a/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb b/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb index 05f72099e4..b428a153bf 100644 --- a/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb +++ b/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def be_ancestor_of(expected) + MSpec.deprecate __method__, '.ancestors.should.include?' BeAncestorOfMatcher.new(expected) end end diff --git a/spec/mspec/lib/mspec/matchers/be_empty.rb b/spec/mspec/lib/mspec/matchers/be_empty.rb index 5abd5c9485..aac175ffb4 100644 --- a/spec/mspec/lib/mspec/matchers/be_empty.rb +++ b/spec/mspec/lib/mspec/matchers/be_empty.rb @@ -15,6 +15,7 @@ end module MSpecMatchers private def be_empty + MSpec.deprecate __method__, '.should.empty?' BeEmptyMatcher.new end end diff --git a/spec/mspec/lib/mspec/matchers/be_false.rb b/spec/mspec/lib/mspec/matchers/be_false.rb index 9e9a2608e1..4fa0bba8a3 100644 --- a/spec/mspec/lib/mspec/matchers/be_false.rb +++ b/spec/mspec/lib/mspec/matchers/be_false.rb @@ -15,6 +15,7 @@ end module MSpecMatchers private def be_false + MSpec.deprecate __method__, '.should == false' BeFalseMatcher.new end end diff --git a/spec/mspec/lib/mspec/matchers/be_kind_of.rb b/spec/mspec/lib/mspec/matchers/be_kind_of.rb index a69906f210..d0b23086aa 100644 --- a/spec/mspec/lib/mspec/matchers/be_kind_of.rb +++ b/spec/mspec/lib/mspec/matchers/be_kind_of.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def be_kind_of(expected) + MSpec.deprecate __method__, '.should.is_a?' BeKindOfMatcher.new(expected) end end diff --git a/spec/mspec/lib/mspec/matchers/be_nan.rb b/spec/mspec/lib/mspec/matchers/be_nan.rb index b279d8f1cf..8ba2a3cafe 100644 --- a/spec/mspec/lib/mspec/matchers/be_nan.rb +++ b/spec/mspec/lib/mspec/matchers/be_nan.rb @@ -15,6 +15,7 @@ end module MSpecMatchers private def be_nan + MSpec.deprecate __method__, '.should.nan?' BeNaNMatcher.new end end diff --git a/spec/mspec/lib/mspec/matchers/be_nil.rb b/spec/mspec/lib/mspec/matchers/be_nil.rb index 049b1e3a53..c79a50a3f9 100644 --- a/spec/mspec/lib/mspec/matchers/be_nil.rb +++ b/spec/mspec/lib/mspec/matchers/be_nil.rb @@ -15,6 +15,7 @@ end module MSpecMatchers private def be_nil + MSpec.deprecate __method__, '.should == nil' BeNilMatcher.new end end diff --git a/spec/mspec/lib/mspec/matchers/be_true.rb b/spec/mspec/lib/mspec/matchers/be_true.rb index 52f5013752..91020cc3fe 100644 --- a/spec/mspec/lib/mspec/matchers/be_true.rb +++ b/spec/mspec/lib/mspec/matchers/be_true.rb @@ -15,6 +15,7 @@ end module MSpecMatchers private def be_true + MSpec.deprecate __method__, '.should == true' BeTrueMatcher.new end end diff --git a/spec/mspec/lib/mspec/matchers/eql.rb b/spec/mspec/lib/mspec/matchers/eql.rb index bcab88ebee..3b132f9ae3 100644 --- a/spec/mspec/lib/mspec/matchers/eql.rb +++ b/spec/mspec/lib/mspec/matchers/eql.rb @@ -21,6 +21,7 @@ end module MSpecMatchers private def eql(expected) + MSpec.deprecate __method__, '.should.eql?' EqlMatcher.new(expected) end end diff --git a/spec/mspec/lib/mspec/matchers/equal.rb b/spec/mspec/lib/mspec/matchers/equal.rb index 5ba4856d82..1ca5172e0b 100644 --- a/spec/mspec/lib/mspec/matchers/equal.rb +++ b/spec/mspec/lib/mspec/matchers/equal.rb @@ -21,6 +21,7 @@ end module MSpecMatchers private def equal(expected) + MSpec.deprecate __method__, '.should.equal?' EqualMatcher.new(expected) end end diff --git a/spec/mspec/lib/mspec/matchers/have_class_variable.rb b/spec/mspec/lib/mspec/matchers/have_class_variable.rb index dd43ced621..576f43793b 100644 --- a/spec/mspec/lib/mspec/matchers/have_class_variable.rb +++ b/spec/mspec/lib/mspec/matchers/have_class_variable.rb @@ -7,6 +7,7 @@ end module MSpecMatchers private def have_class_variable(variable) + MSpec.deprecate __method__, '.should.class_variable_defined?' HaveClassVariableMatcher.new(variable) end end diff --git a/spec/mspec/lib/mspec/matchers/have_constant.rb b/spec/mspec/lib/mspec/matchers/have_constant.rb index 6ec7c75b85..c796d742e0 100644 --- a/spec/mspec/lib/mspec/matchers/have_constant.rb +++ b/spec/mspec/lib/mspec/matchers/have_constant.rb @@ -7,6 +7,7 @@ end module MSpecMatchers private def have_constant(variable) + MSpec.deprecate __method__, '.should.const_defined?' HaveConstantMatcher.new(variable) end end diff --git a/spec/mspec/lib/mspec/matchers/have_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_instance_method.rb index 9a5a31aa0f..76dde482d5 100644 --- a/spec/mspec/lib/mspec/matchers/have_instance_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_instance_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_instance_method(method, include_super = true) + MSpec.deprecate __method__, '.should.method_defined?' HaveInstanceMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/have_instance_variable.rb b/spec/mspec/lib/mspec/matchers/have_instance_variable.rb index de51b3209d..f49595ff7e 100644 --- a/spec/mspec/lib/mspec/matchers/have_instance_variable.rb +++ b/spec/mspec/lib/mspec/matchers/have_instance_variable.rb @@ -7,6 +7,7 @@ end module MSpecMatchers private def have_instance_variable(variable) + MSpec.deprecate __method__, '.should.instance_variable_defined?' HaveInstanceVariableMatcher.new(variable) end end diff --git a/spec/mspec/lib/mspec/matchers/have_method.rb b/spec/mspec/lib/mspec/matchers/have_method.rb index e962e69e0a..3db01a7235 100644 --- a/spec/mspec/lib/mspec/matchers/have_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_method(method, include_super = true) + MSpec.deprecate __method__, '.should.respond_to?' HaveMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb index d32db76c6a..bae01e846e 100644 --- a/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_private_instance_method(method, include_super = true) + MSpec.deprecate __method__, '.private_instance_methods(false).should.include?' HavePrivateInstanceMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/have_private_method.rb b/spec/mspec/lib/mspec/matchers/have_private_method.rb index c74165cfc7..32efd5a155 100644 --- a/spec/mspec/lib/mspec/matchers/have_private_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_private_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_private_method(method, include_super = true) + MSpec.deprecate __method__, '.private_methods(false).should.include?' HavePrivateMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb index 1deb2f995d..9f09e8b529 100644 --- a/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_protected_instance_method(method, include_super = true) + MSpec.deprecate __method__, '.protected_instance_methods(false).should.include?' HaveProtectedInstanceMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb index 0e620532c0..69abadfafa 100644 --- a/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_public_instance_method(method, include_super = true) + MSpec.deprecate __method__, '.public_instance_methods(false).should.include?' HavePublicInstanceMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/have_singleton_method.rb b/spec/mspec/lib/mspec/matchers/have_singleton_method.rb index b60dd2536b..2d2707c528 100644 --- a/spec/mspec/lib/mspec/matchers/have_singleton_method.rb +++ b/spec/mspec/lib/mspec/matchers/have_singleton_method.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def have_singleton_method(method, include_super = true) + MSpec.deprecate __method__, '.singleton_methods(false).should.include?' HaveSingletonMethodMatcher.new method, include_super end end diff --git a/spec/mspec/lib/mspec/matchers/include.rb b/spec/mspec/lib/mspec/matchers/include.rb index 3f07f35548..05d5079f13 100644 --- a/spec/mspec/lib/mspec/matchers/include.rb +++ b/spec/mspec/lib/mspec/matchers/include.rb @@ -26,6 +26,7 @@ end # Cannot override #include at the toplevel in MRI module MSpecMatchers private def include(*expected) + MSpec.deprecate __method__, '.should.include?' IncludeMatcher.new(*expected) end end diff --git a/spec/mspec/lib/mspec/matchers/infinity.rb b/spec/mspec/lib/mspec/matchers/infinity.rb index 8bfa6dbd10..0a4c95c7cc 100644 --- a/spec/mspec/lib/mspec/matchers/infinity.rb +++ b/spec/mspec/lib/mspec/matchers/infinity.rb @@ -19,10 +19,12 @@ end module MSpecMatchers private def be_positive_infinity + MSpec.deprecate __method__, '.should.infinite? == 1' InfinityMatcher.new(1) end private def be_negative_infinity + MSpec.deprecate __method__, '.should.infinite? == -1' InfinityMatcher.new(-1) end end diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb index 8cba842ce3..17ea47148b 100644 --- a/spec/mspec/lib/mspec/matchers/raise_error.rb +++ b/spec/mspec/lib/mspec/matchers/raise_error.rb @@ -116,6 +116,7 @@ end module MSpecMatchers private def raise_error(exception = Exception, message = nil, options = nil, &block) + MSpec.deprecate __method__, '.should.raise' RaiseErrorMatcher.new(exception, message, options, &block) end diff --git a/spec/mspec/lib/mspec/matchers/respond_to.rb b/spec/mspec/lib/mspec/matchers/respond_to.rb index 6b35ae2d3c..a36bf8aee2 100644 --- a/spec/mspec/lib/mspec/matchers/respond_to.rb +++ b/spec/mspec/lib/mspec/matchers/respond_to.rb @@ -19,6 +19,7 @@ end module MSpecMatchers private def respond_to(expected) + MSpec.deprecate __method__, '.should.respond_to?' RespondToMatcher.new(expected) end end diff --git a/spec/mspec/spec/fixtures/should.rb b/spec/mspec/spec/fixtures/should.rb index f494775c5f..6eb156da25 100644 --- a/spec/mspec/spec/fixtures/should.rb +++ b/spec/mspec/spec/fixtures/should.rb @@ -40,7 +40,7 @@ MSpec.setup_env # Specs describe "MSpec expectation method #should" do it "accepts a matcher" do - :sym.should be_kind_of(Symbol) + 0.4.should be_close(0.5, 0.2) end it "causes a failure to be recorded" do @@ -59,7 +59,7 @@ end describe "MSpec expectation method #should_not" do it "accepts a matcher" do - "sym".should_not be_kind_of(Symbol) + 0.1.should_not be_close(0.5, 0.2) end it "causes a failure to be recorded" do diff --git a/spec/mspec/spec/matchers/raise_error_spec.rb b/spec/mspec/spec/matchers/raise_error_spec.rb index 3849c7dd2a..957baa087d 100644 --- a/spec/mspec/spec/matchers/raise_error_spec.rb +++ b/spec/mspec/spec/matchers/raise_error_spec.rb @@ -19,7 +19,7 @@ RSpec.describe RaiseErrorMatcher do ensure_mspec_method(-> {}.method(:should)) run = false - -> { raise ExpectedException }.should PublicMSpecMatchers.raise_error { |error| + -> { raise ExpectedException }.should.raise { |error| expect(error.class).to eq(ExpectedException) run = true } @@ -30,7 +30,7 @@ RSpec.describe RaiseErrorMatcher do ensure_mspec_method(-> {}.method(:should)) run = false - -> { raise ExpectedException }.should PublicMSpecMatchers.raise_error do |error| + -> { raise ExpectedException }.should.raise do |error| expect(error.class).to eq(ExpectedException) run = true end diff --git a/spec/mspec/spec/spec_helper.rb b/spec/mspec/spec/spec_helper.rb index 5cabfe5626..8ea38b644f 100644 --- a/spec/mspec/spec/spec_helper.rb +++ b/spec/mspec/spec/spec_helper.rb @@ -62,9 +62,4 @@ def ensure_mspec_method(method) expect(file).to start_with(File.expand_path('../../lib/mspec', __FILE__ )) end -PublicMSpecMatchers = Class.new { - include MSpecMatchers - public :raise_error -}.new - BACKTRACE_QUOTE = RUBY_VERSION >= "3.4" ? "'" : "`" diff --git a/spec/ruby/core/array/collect_spec.rb b/spec/ruby/core/array/collect_spec.rb index 43a539f805..bdee5c240a 100644 --- a/spec/ruby/core/array/collect_spec.rb +++ b/spec/ruby/core/array/collect_spec.rb @@ -1,143 +1,13 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative '../enumerable/shared/enumeratorized' -require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#collect" do - it "returns a copy of array with each element replaced by the value returned by block" do - a = ['a', 'b', 'c', 'd'] - b = a.collect { |i| i + '!' } - b.should == ["a!", "b!", "c!", "d!"] - b.should_not.equal? a + it "is an alias of Array#map" do + Array.instance_method(:collect).should == Array.instance_method(:map) end - - it "does not return subclass instance" do - ArraySpecs::MyArray[1, 2, 3].collect { |x| x + 1 }.should.instance_of?(Array) - end - - it "does not change self" do - a = ['a', 'b', 'c', 'd'] - a.collect { |i| i + '!' } - a.should == ['a', 'b', 'c', 'd'] - end - - it "returns the evaluated value of block if it broke in the block" do - a = ['a', 'b', 'c', 'd'] - b = a.collect {|i| - if i == 'c' - break 0 - else - i + '!' - end - } - b.should == 0 - end - - it "returns an Enumerator when no block given" do - a = [1, 2, 3] - a.collect.should.instance_of?(Enumerator) - end - - it "raises an ArgumentError when no block and with arguments" do - a = [1, 2, 3] - -> { - a.collect(:foo) - }.should.raise(ArgumentError) - end - - before :each do - @object = [1, 2, 3, 4] - end - it_behaves_like :enumeratorized_with_origin_size, :collect - - it_behaves_like :array_iterable_and_tolerating_size_increasing, :collect end describe "Array#collect!" do - it "replaces each element with the value returned by block" do - a = [7, 9, 3, 5] - a.collect! { |i| i - 1 }.should.equal?(a) - a.should == [6, 8, 2, 4] - end - - it "returns self" do - a = [1, 2, 3, 4, 5] - b = a.collect! {|i| i+1 } - a.should.equal? b - end - - it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do - a = ['a', 'b', 'c', 'd'] - b = a.collect! {|i| - if i == 'c' - break 0 - else - i + '!' - end - } - b.should == 0 - a.should == ['a!', 'b!', 'c', 'd'] + it "is an alias of Array#map!" do + Array.instance_method(:collect!).should == Array.instance_method(:map!) end - - it "returns an Enumerator when no block given, and the enumerator can modify the original array" do - a = [1, 2, 3] - enum = a.collect! - enum.should.instance_of?(Enumerator) - enum.each{|i| "#{i}!" } - a.should == ["1!", "2!", "3!"] - end - - describe "when frozen" do - it "raises a FrozenError" do - -> { ArraySpecs.frozen_array.collect! {} }.should.raise(FrozenError) - end - - it "raises a FrozenError when empty" do - -> { ArraySpecs.empty_frozen_array.collect! {} }.should.raise(FrozenError) - end - - it "raises a FrozenError when calling #each on the returned Enumerator" do - enumerator = ArraySpecs.frozen_array.collect! - -> { enumerator.each {|x| x } }.should.raise(FrozenError) - end - - it "raises a FrozenError when calling #each on the returned Enumerator when empty" do - enumerator = ArraySpecs.empty_frozen_array.collect! - -> { enumerator.each {|x| x } }.should.raise(FrozenError) - end - end - - it "does not truncate the array is the block raises an exception" do - a = [1, 2, 3] - begin - a.collect! { raise StandardError, 'Oops' } - rescue - end - - a.should == [1, 2, 3] - end - - it "only changes elements before error is raised, keeping the element which raised an error." do - a = [1, 2, 3, 4] - begin - a.collect! do |e| - case e - when 1 then -1 - when 2 then -2 - when 3 then raise StandardError, 'Oops' - else 0 - end - end - rescue StandardError - end - - a.should == [-1, -2, 3, 4] - end - - before :each do - @object = [1, 2, 3, 4] - end - it_behaves_like :enumeratorized_with_origin_size, :collect! - - it_behaves_like :array_iterable_and_tolerating_size_increasing, :collect! end diff --git a/spec/ruby/core/array/length_spec.rb b/spec/ruby/core/array/length_spec.rb index 74b2eb3a08..e45abb2138 100644 --- a/spec/ruby/core/array/length_spec.rb +++ b/spec/ruby/core/array/length_spec.rb @@ -1,14 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' describe "Array#length" do - it "returns the number of elements" do - [].length.should == 0 - [1, 2, 3].length.should == 3 - end - - it "properly handles recursive arrays" do - ArraySpecs.empty_recursive_array.length.should == 1 - ArraySpecs.recursive_array.length.should == 8 + it "is an alias of Array#size" do + Array.instance_method(:length).should == Array.instance_method(:size) end end diff --git a/spec/ruby/core/array/map_spec.rb b/spec/ruby/core/array/map_spec.rb index f5e88c8624..0cc394663a 100644 --- a/spec/ruby/core/array/map_spec.rb +++ b/spec/ruby/core/array/map_spec.rb @@ -1,13 +1,143 @@ require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative '../enumerable/shared/enumeratorized' +require_relative 'shared/iterable_and_tolerating_size_increasing' describe "Array#map" do - it "is an alias of Array#collect" do - Array.instance_method(:map).should == Array.instance_method(:collect) + it "returns a copy of array with each element replaced by the value returned by block" do + a = ['a', 'b', 'c', 'd'] + b = a.map { |i| i + '!' } + b.should == ["a!", "b!", "c!", "d!"] + b.should_not.equal? a end + + it "does not return subclass instance" do + ArraySpecs::MyArray[1, 2, 3].map { |x| x + 1 }.should.instance_of?(Array) + end + + it "does not change self" do + a = ['a', 'b', 'c', 'd'] + a.map { |i| i + '!' } + a.should == ['a', 'b', 'c', 'd'] + end + + it "returns the evaluated value of block if it broke in the block" do + a = ['a', 'b', 'c', 'd'] + b = a.map {|i| + if i == 'c' + break 0 + else + i + '!' + end + } + b.should == 0 + end + + it "returns an Enumerator when no block given" do + a = [1, 2, 3] + a.map.should.instance_of?(Enumerator) + end + + it "raises an ArgumentError when no block and with arguments" do + a = [1, 2, 3] + -> { + a.map(:foo) + }.should.raise(ArgumentError) + end + + before :each do + @object = [1, 2, 3, 4] + end + it_behaves_like :enumeratorized_with_origin_size, :map + + it_behaves_like :array_iterable_and_tolerating_size_increasing, :map end describe "Array#map!" do - it "is an alias of Array#collect!" do - Array.instance_method(:map!).should == Array.instance_method(:collect!) + it "replaces each element with the value returned by block" do + a = [7, 9, 3, 5] + a.map! { |i| i - 1 }.should.equal?(a) + a.should == [6, 8, 2, 4] + end + + it "returns self" do + a = [1, 2, 3, 4, 5] + b = a.map! {|i| i+1 } + a.should.equal? b + end + + it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do + a = ['a', 'b', 'c', 'd'] + b = a.map! {|i| + if i == 'c' + break 0 + else + i + '!' + end + } + b.should == 0 + a.should == ['a!', 'b!', 'c', 'd'] end + + it "returns an Enumerator when no block given, and the enumerator can modify the original array" do + a = [1, 2, 3] + enum = a.map! + enum.should.instance_of?(Enumerator) + enum.each{|i| "#{i}!" } + a.should == ["1!", "2!", "3!"] + end + + describe "when frozen" do + it "raises a FrozenError" do + -> { ArraySpecs.frozen_array.map! {} }.should.raise(FrozenError) + end + + it "raises a FrozenError when empty" do + -> { ArraySpecs.empty_frozen_array.map! {} }.should.raise(FrozenError) + end + + it "raises a FrozenError when calling #each on the returned Enumerator" do + enumerator = ArraySpecs.frozen_array.map! + -> { enumerator.each {|x| x } }.should.raise(FrozenError) + end + + it "raises a FrozenError when calling #each on the returned Enumerator when empty" do + enumerator = ArraySpecs.empty_frozen_array.map! + -> { enumerator.each {|x| x } }.should.raise(FrozenError) + end + end + + it "does not truncate the array is the block raises an exception" do + a = [1, 2, 3] + begin + a.map! { raise StandardError, 'Oops' } + rescue + end + + a.should == [1, 2, 3] + end + + it "only changes elements before error is raised, keeping the element which raised an error." do + a = [1, 2, 3, 4] + begin + a.map! do |e| + case e + when 1 then -1 + when 2 then -2 + when 3 then raise StandardError, 'Oops' + else 0 + end + end + rescue StandardError + end + + a.should == [-1, -2, 3, 4] + end + + before :each do + @object = [1, 2, 3, 4] + end + it_behaves_like :enumeratorized_with_origin_size, :map! + + it_behaves_like :array_iterable_and_tolerating_size_increasing, :map! end diff --git a/spec/ruby/core/array/size_spec.rb b/spec/ruby/core/array/size_spec.rb index 83e8969012..710754ed62 100644 --- a/spec/ruby/core/array/size_spec.rb +++ b/spec/ruby/core/array/size_spec.rb @@ -1,7 +1,14 @@ require_relative '../../spec_helper' +require_relative 'fixtures/classes' describe "Array#size" do - it "is an alias of Array#length" do - Array.instance_method(:size).should == Array.instance_method(:length) + it "returns the number of elements" do + [].size.should == 0 + [1, 2, 3].size.should == 3 + end + + it "properly handles recursive arrays" do + ArraySpecs.empty_recursive_array.size.should == 1 + ArraySpecs.recursive_array.size.should == 8 end end diff --git a/spec/ruby/core/basicobject/equal_spec.rb b/spec/ruby/core/basicobject/equal_spec.rb index c0f41dc0c0..f27f0d7aca 100644 --- a/spec/ruby/core/basicobject/equal_spec.rb +++ b/spec/ruby/core/basicobject/equal_spec.rb @@ -1,54 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/kernel/equal' describe "BasicObject#equal?" do - it "is a public instance method" do - BasicObject.public_instance_methods(false).should.include?(:equal?) - end - - it_behaves_like :object_equal, :equal? - - it "is unaffected by overriding __id__" do - o1 = mock("object") - o2 = mock("object") - suppress_warning { - def o1.__id__; 10; end - def o2.__id__; 10; end - } - o1.equal?(o2).should == false - end - - it "is unaffected by overriding object_id" do - o1 = mock("object") - o1.stub!(:object_id).and_return(10) - o2 = mock("object") - o2.stub!(:object_id).and_return(10) - o1.equal?(o2).should == false - end - - it "is unaffected by overriding ==" do - # different objects, overriding == to return true - o1 = mock("object") - o1.stub!(:==).and_return(true) - o2 = mock("object") - o1.equal?(o2).should == false - - # same objects, overriding == to return false - o3 = mock("object") - o3.stub!(:==).and_return(false) - o3.equal?(o3).should == true - end - - it "is unaffected by overriding eql?" do - # different objects, overriding eql? to return true - o1 = mock("object") - o1.stub!(:eql?).and_return(true) - o2 = mock("object") - o1.equal?(o2).should == false - - # same objects, overriding eql? to return false - o3 = mock("object") - o3.stub!(:eql?).and_return(false) - o3.equal?(o3).should == true + it "is an alias of BasicObject#==" do + BasicObject.instance_method(:equal?).should == BasicObject.instance_method(:==) end end diff --git a/spec/ruby/core/basicobject/equal_value_spec.rb b/spec/ruby/core/basicobject/equal_value_spec.rb index eb951a8305..15755a44a2 100644 --- a/spec/ruby/core/basicobject/equal_value_spec.rb +++ b/spec/ruby/core/basicobject/equal_value_spec.rb @@ -7,4 +7,48 @@ describe "BasicObject#==" do end it_behaves_like :object_equal, :== + + it "is unaffected by overriding __id__" do + o1 = mock("object") + o2 = mock("object") + suppress_warning { + def o1.__id__; 10; end + def o2.__id__; 10; end + } + (o1 == o2).should == false + end + + it "is unaffected by overriding object_id" do + o1 = mock("object") + o1.stub!(:object_id).and_return(10) + o2 = mock("object") + o2.stub!(:object_id).and_return(10) + (o1 == o2).should == false + end + + it "is unaffected by overriding equal?" do + # different objects, overriding equal? to return true + o1 = mock("object") + o1.stub!(:equal?).and_return(true) + o2 = mock("object") + (o1 == o2).should == false + + # same objects, overriding equal? to return false + o3 = mock("object") + o3.stub!(:equal?).and_return(false) + (o3 == o3).should == true + end + + it "is unaffected by overriding eql?" do + # different objects, overriding eql? to return true + o1 = mock("object") + o1.stub!(:eql?).and_return(true) + o2 = mock("object") + (o1 == o2).should == false + + # same objects, overriding eql? to return false + o3 = mock("object") + o3.stub!(:eql?).and_return(false) + (o3 == o3).should == true + end end diff --git a/spec/ruby/core/complex/rect_spec.rb b/spec/ruby/core/complex/rect_spec.rb index 72f2bf9930..bf3467c611 100644 --- a/spec/ruby/core/complex/rect_spec.rb +++ b/spec/ruby/core/complex/rect_spec.rb @@ -7,7 +7,7 @@ describe "Complex#rect" do end describe "Complex.rect" do - it "is an alias of Complex#rectangular" do + it "is an alias of Complex.rectangular" do Complex.method(:rect).should == Complex.method(:rectangular) end end diff --git a/spec/ruby/core/data/inspect_spec.rb b/spec/ruby/core/data/inspect_spec.rb index 6c97a719e3..e5b9689ca5 100644 --- a/spec/ruby/core/data/inspect_spec.rb +++ b/spec/ruby/core/data/inspect_spec.rb @@ -2,62 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Data#inspect" do - it "returns a string representation showing members and values" do - a = DataSpecs::Measure.new(42, "km") - a.inspect.should == '#<data DataSpecs::Measure amount=42, unit="km">' - end - - it "returns a string representation without the class name for anonymous structs" do - Data.define(:a).new("").inspect.should == '#<data a="">' - end - - it "returns a string representation without the class name for structs nested in anonymous classes" do - c = Class.new - c.class_eval <<~DOC - Foo = Data.define(:a) - DOC - - c::Foo.new("").inspect.should == '#<data a="">' - end - - it "returns a string representation without the class name for structs nested in anonymous modules" do - m = Module.new - m.class_eval <<~DOC - Foo = Data.define(:a) - DOC - - m::Foo.new("").inspect.should == '#<data a="">' - end - - it "does not call #name method" do - struct = DataSpecs::MeasureWithOverriddenName.new(42, "km") - struct.inspect.should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">' - end - - it "does not call #name method when struct is anonymous" do - klass = Class.new(DataSpecs::Measure) do - def self.name - "A" - end - end - struct = klass.new(42, "km") - struct.inspect.should == '#<data amount=42, unit="km">' - end - - context "recursive structure" do - it "returns string representation with recursive attribute replaced with ..." do - a = DataSpecs::Measure.allocate - a.send(:initialize, amount: 42, unit: a) - - a.inspect.should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>" - end - - it "returns string representation with recursive attribute replaced with ... when an anonymous class" do - klass = Class.new(DataSpecs::Measure) - a = klass.allocate - a.send(:initialize, amount: 42, unit: a) - - a.inspect.should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/ - end + it "is an alias of Data#to_s" do + DataSpecs::Measure.instance_method(:inspect).should == DataSpecs::Measure.instance_method(:to_s) end end diff --git a/spec/ruby/core/data/to_s_spec.rb b/spec/ruby/core/data/to_s_spec.rb index a552e4659b..e436c21109 100644 --- a/spec/ruby/core/data/to_s_spec.rb +++ b/spec/ruby/core/data/to_s_spec.rb @@ -2,8 +2,62 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Data#to_s" do - it "is an alias of Data#inspect" do + it "returns a string representation showing members and values" do a = DataSpecs::Measure.new(42, "km") - a.method(:to_s).should == a.method(:inspect) + a.to_s.should == '#<data DataSpecs::Measure amount=42, unit="km">' + end + + it "returns a string representation without the class name for anonymous structs" do + Data.define(:a).new("").to_s.should == '#<data a="">' + end + + it "returns a string representation without the class name for structs nested in anonymous classes" do + c = Class.new + c.class_eval <<~DOC + Foo = Data.define(:a) + DOC + + c::Foo.new("").to_s.should == '#<data a="">' + end + + it "returns a string representation without the class name for structs nested in anonymous modules" do + m = Module.new + m.class_eval <<~DOC + Foo = Data.define(:a) + DOC + + m::Foo.new("").to_s.should == '#<data a="">' + end + + it "does not call #name method" do + struct = DataSpecs::MeasureWithOverriddenName.new(42, "km") + struct.to_s.should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">' + end + + it "does not call #name method when struct is anonymous" do + klass = Class.new(DataSpecs::Measure) do + def self.name + "A" + end + end + struct = klass.new(42, "km") + struct.to_s.should == '#<data amount=42, unit="km">' + end + + context "recursive structure" do + it "returns string representation with recursive attribute replaced with ..." do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: 42, unit: a) + + a.to_s.should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>" + end + + it "returns string representation with recursive attribute replaced with ... when an anonymous class" do + klass = Class.new(DataSpecs::Measure) + a = klass.allocate + a.send(:initialize, amount: 42, unit: a) + + a.to_s.should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/ + end end end diff --git a/spec/ruby/core/dir/delete_spec.rb b/spec/ruby/core/dir/delete_spec.rb index a0020788ca..2dbd461c94 100644 --- a/spec/ruby/core/dir/delete_spec.rb +++ b/spec/ruby/core/dir/delete_spec.rb @@ -1,6 +1,5 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/delete' describe "Dir.delete" do before :all do @@ -11,5 +10,55 @@ describe "Dir.delete" do DirSpecs.delete_mock_dirs end - it_behaves_like :dir_delete, :delete + before :each do + DirSpecs.rmdir_dirs true + end + + after :each do + DirSpecs.rmdir_dirs false + end + + it "removes empty directories" do + Dir.delete(DirSpecs.mock_rmdir("empty")).should == 0 + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_rmdir("empty")) + Dir.delete(p) + end + + it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do + -> do + Dir.delete DirSpecs.mock_rmdir("nonempty") + end.should.raise(Errno::ENOTEMPTY) + end + + it "raises an Errno::ENOENT when trying to remove a non-existing directory" do + -> do + Dir.delete DirSpecs.nonexistent + end.should.raise(Errno::ENOENT) + end + + it "raises an Errno::ENOTDIR when trying to remove a non-directory" do + file = DirSpecs.mock_rmdir("nonempty/regular") + touch(file) + -> do + Dir.delete file + end.should.raise(Errno::ENOTDIR) + end + + # this won't work on Windows, since chmod(0000) does not remove all permissions + platform_is_not :windows do + as_user do + it "raises an Errno::EACCES if lacking adequate permissions to remove the directory" do + parent = DirSpecs.mock_rmdir("noperm") + child = DirSpecs.mock_rmdir("noperm", "child") + File.chmod(0000, parent) + -> do + Dir.delete child + end.should.raise(Errno::EACCES) + end + end + end end diff --git a/spec/ruby/core/dir/path_spec.rb b/spec/ruby/core/dir/path_spec.rb index 02ddd2cd42..e506db1222 100644 --- a/spec/ruby/core/dir/path_spec.rb +++ b/spec/ruby/core/dir/path_spec.rb @@ -1,37 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/common' describe "Dir#path" do - before :all do - DirSpecs.create_mock_dirs - end - - after :all do - DirSpecs.delete_mock_dirs - end - - it "returns the path that was supplied to .new or .open" do - dir = Dir.open DirSpecs.mock_dir - begin - dir.path.should == DirSpecs.mock_dir - ensure - dir.close rescue nil - end - end - - it "returns the path even when called on a closed Dir instance" do - dir = Dir.open DirSpecs.mock_dir - dir.close - dir.path.should == DirSpecs.mock_dir - end - - it "returns a String with the same encoding as the argument to .open" do - path = DirSpecs.mock_dir.force_encoding Encoding::IBM866 - dir = Dir.open path - begin - dir.path.encoding.should.equal?(Encoding::IBM866) - ensure - dir.close - end + it "is an alias of Dir#to_path" do + Dir.instance_method(:path).should == Dir.instance_method(:to_path) end end diff --git a/spec/ruby/core/dir/pos_spec.rb b/spec/ruby/core/dir/pos_spec.rb index f8ca06d778..1e364fbe3c 100644 --- a/spec/ruby/core/dir/pos_spec.rb +++ b/spec/ruby/core/dir/pos_spec.rb @@ -1,10 +1,42 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' +require_relative 'shared/closed' require_relative 'shared/pos' describe "Dir#pos" do - it "is an alias of Dir#tell" do - Dir.instance_method(:pos).should == Dir.instance_method(:tell) + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_closed, :pos + + before :each do + @dir = Dir.open DirSpecs.mock_dir + end + + after :each do + @dir.close rescue nil + end + + it "returns an Integer representing the current position in the directory" do + @dir.pos.should.is_a?(Integer) + @dir.pos.should.is_a?(Integer) + @dir.pos.should.is_a?(Integer) + end + + it "returns a different Integer if moved from previous position" do + a = @dir.pos + @dir.read + b = @dir.pos + + a.should.is_a?(Integer) + b.should.is_a?(Integer) + + a.should_not == b end end diff --git a/spec/ruby/core/dir/rmdir_spec.rb b/spec/ruby/core/dir/rmdir_spec.rb index 08cd1a5bc6..c31067ca29 100644 --- a/spec/ruby/core/dir/rmdir_spec.rb +++ b/spec/ruby/core/dir/rmdir_spec.rb @@ -1,15 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/common' -require_relative 'shared/delete' describe "Dir.rmdir" do - before :all do - DirSpecs.create_mock_dirs + it "is an alias of Dir.delete" do + Dir.method(:rmdir).should == Dir.method(:delete) end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_delete, :rmdir end diff --git a/spec/ruby/core/dir/shared/delete.rb b/spec/ruby/core/dir/shared/delete.rb deleted file mode 100644 index ba013e8615..0000000000 --- a/spec/ruby/core/dir/shared/delete.rb +++ /dev/null @@ -1,53 +0,0 @@ -describe :dir_delete, shared: true do - before :each do - DirSpecs.rmdir_dirs true - end - - after :each do - DirSpecs.rmdir_dirs false - end - - it "removes empty directories" do - Dir.send(@method, DirSpecs.mock_rmdir("empty")).should == 0 - end - - it "calls #to_path on non-String arguments" do - p = mock('path') - p.should_receive(:to_path).and_return(DirSpecs.mock_rmdir("empty")) - Dir.send(@method, p) - end - - it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do - -> do - Dir.send @method, DirSpecs.mock_rmdir("nonempty") - end.should.raise(Errno::ENOTEMPTY) - end - - it "raises an Errno::ENOENT when trying to remove a non-existing directory" do - -> do - Dir.send @method, DirSpecs.nonexistent - end.should.raise(Errno::ENOENT) - end - - it "raises an Errno::ENOTDIR when trying to remove a non-directory" do - file = DirSpecs.mock_rmdir("nonempty/regular") - touch(file) - -> do - Dir.send @method, file - end.should.raise(Errno::ENOTDIR) - end - - # this won't work on Windows, since chmod(0000) does not remove all permissions - platform_is_not :windows do - as_user do - it "raises an Errno::EACCES if lacking adequate permissions to remove the directory" do - parent = DirSpecs.mock_rmdir("noperm") - child = DirSpecs.mock_rmdir("noperm", "child") - File.chmod(0000, parent) - -> do - Dir.send @method, child - end.should.raise(Errno::EACCES) - end - end - end -end diff --git a/spec/ruby/core/dir/tell_spec.rb b/spec/ruby/core/dir/tell_spec.rb index dcbb40438f..04f92a8ade 100644 --- a/spec/ruby/core/dir/tell_spec.rb +++ b/spec/ruby/core/dir/tell_spec.rb @@ -1,41 +1,9 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -require_relative 'shared/closed' require_relative 'shared/pos' describe "Dir#tell" do - before :all do - DirSpecs.create_mock_dirs - end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_closed, :tell - - before :each do - @dir = Dir.open DirSpecs.mock_dir - end - - after :each do - @dir.close rescue nil - end - - it "returns an Integer representing the current position in the directory" do - @dir.tell.should.is_a?(Integer) - @dir.tell.should.is_a?(Integer) - @dir.tell.should.is_a?(Integer) - end - - it "returns a different Integer if moved from previous position" do - a = @dir.tell - @dir.read - b = @dir.tell - - a.should.is_a?(Integer) - b.should.is_a?(Integer) - - a.should_not == b + it "is an alias of Dir#pos" do + Dir.instance_method(:tell).should == Dir.instance_method(:pos) end end diff --git a/spec/ruby/core/dir/to_path_spec.rb b/spec/ruby/core/dir/to_path_spec.rb index 2ed533e757..43e349c50e 100644 --- a/spec/ruby/core/dir/to_path_spec.rb +++ b/spec/ruby/core/dir/to_path_spec.rb @@ -1,7 +1,37 @@ require_relative '../../spec_helper' +require_relative 'fixtures/common' describe "Dir#to_path" do - it "is an alias of Dir#path" do - Dir.instance_method(:to_path).should == Dir.instance_method(:path) + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it "returns the to_path that was supplied to .new or .open" do + dir = Dir.open DirSpecs.mock_dir + begin + dir.to_path.should == DirSpecs.mock_dir + ensure + dir.close rescue nil + end + end + + it "returns the to_path even when called on a closed Dir instance" do + dir = Dir.open DirSpecs.mock_dir + dir.close + dir.to_path.should == DirSpecs.mock_dir + end + + it "returns a String with the same encoding as the argument to .open" do + to_path = DirSpecs.mock_dir.force_encoding Encoding::IBM866 + dir = Dir.open to_path + begin + dir.to_path.encoding.should.equal?(Encoding::IBM866) + ensure + dir.close + end end end diff --git a/spec/ruby/core/dir/unlink_spec.rb b/spec/ruby/core/dir/unlink_spec.rb index 79027e020c..d9cd1b1a87 100644 --- a/spec/ruby/core/dir/unlink_spec.rb +++ b/spec/ruby/core/dir/unlink_spec.rb @@ -1,15 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/common' -require_relative 'shared/delete' describe "Dir.unlink" do - before :all do - DirSpecs.create_mock_dirs + it "is an alias of Dir.delete" do + Dir.method(:unlink).should == Dir.method(:delete) end - - after :all do - DirSpecs.delete_mock_dirs - end - - it_behaves_like :dir_delete, :unlink end diff --git a/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb b/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb index 07c7a88356..7fa867a57e 100644 --- a/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb +++ b/spec/ruby/core/encoding/converter/asciicompat_encoding_spec.rb @@ -1,11 +1,6 @@ require_relative '../../../spec_helper' describe "Encoding::Converter.asciicompat_encoding" do - it "accepts an encoding name as a String argument" do - -> { Encoding::Converter.asciicompat_encoding('UTF-8') }. - should_not raise_error - end - it "coerces non-String/Encoding objects with #to_str" do str = mock('string') str.should_receive(:to_str).at_least(1).times.and_return('string') @@ -13,9 +8,8 @@ describe "Encoding::Converter.asciicompat_encoding" do end it "accepts an Encoding object as an argument" do - Encoding::Converter. - asciicompat_encoding(Encoding.find("ISO-2022-JP")). - should == Encoding::Converter.asciicompat_encoding("ISO-2022-JP") + Encoding::Converter.asciicompat_encoding(Encoding.find("ISO-2022-JP")).should == + Encoding::Converter.asciicompat_encoding("ISO-2022-JP") end it "returns a corresponding ASCII compatible encoding for ASCII-incompatible encodings" do diff --git a/spec/ruby/core/enumerable/collect_concat_spec.rb b/spec/ruby/core/enumerable/collect_concat_spec.rb index 59317cfe34..5024aaddab 100644 --- a/spec/ruby/core/enumerable/collect_concat_spec.rb +++ b/spec/ruby/core/enumerable/collect_concat_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/collect_concat' describe "Enumerable#collect_concat" do - it_behaves_like :enumerable_collect_concat, :collect_concat + it "is an alias of Enumerable#flat_map" do + Enumerable.instance_method(:collect_concat).should == Enumerable.instance_method(:flat_map) + end end diff --git a/spec/ruby/core/enumerable/collect_spec.rb b/spec/ruby/core/enumerable/collect_spec.rb index cfa2895cce..319b1b263d 100644 --- a/spec/ruby/core/enumerable/collect_spec.rb +++ b/spec/ruby/core/enumerable/collect_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/collect' describe "Enumerable#collect" do - it_behaves_like :enumerable_collect, :collect + it "is an alias of Enumerable#map" do + Enumerable.instance_method(:collect).should == Enumerable.instance_method(:map) + end end diff --git a/spec/ruby/core/enumerable/detect_spec.rb b/spec/ruby/core/enumerable/detect_spec.rb index 6959aadc44..0669c50c58 100644 --- a/spec/ruby/core/enumerable/detect_spec.rb +++ b/spec/ruby/core/enumerable/detect_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/find' describe "Enumerable#detect" do - it_behaves_like :enumerable_find, :detect + it "is an alias of Enumerable#find" do + Enumerable.instance_method(:detect).should == Enumerable.instance_method(:find) + end end diff --git a/spec/ruby/core/enumerable/entries_spec.rb b/spec/ruby/core/enumerable/entries_spec.rb index 2de4fc756a..8cb29b7b47 100644 --- a/spec/ruby/core/enumerable/entries_spec.rb +++ b/spec/ruby/core/enumerable/entries_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/entries' describe "Enumerable#entries" do - it_behaves_like :enumerable_entries, :entries + it "is an alias of Enumerable#to_a" do + Enumerable.instance_method(:entries).should == Enumerable.instance_method(:to_a) + end end diff --git a/spec/ruby/core/enumerable/filter_spec.rb b/spec/ruby/core/enumerable/filter_spec.rb index 1c3a7e9ff5..d075b39396 100644 --- a/spec/ruby/core/enumerable/filter_spec.rb +++ b/spec/ruby/core/enumerable/filter_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/find_all' describe "Enumerable#filter" do - it_behaves_like :enumerable_find_all, :filter + it "is an alias of Enumerable#select" do + Enumerable.instance_method(:filter).should == Enumerable.instance_method(:select) + end end diff --git a/spec/ruby/core/enumerable/find_all_spec.rb b/spec/ruby/core/enumerable/find_all_spec.rb index 9cd635f247..1095a19569 100644 --- a/spec/ruby/core/enumerable/find_all_spec.rb +++ b/spec/ruby/core/enumerable/find_all_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/find_all' describe "Enumerable#find_all" do - it_behaves_like :enumerable_find_all, :find_all + it "is an alias of Enumerable#select" do + Enumerable.instance_method(:find_all).should == Enumerable.instance_method(:select) + end end diff --git a/spec/ruby/core/enumerable/find_spec.rb b/spec/ruby/core/enumerable/find_spec.rb index 5ddebc05f8..4ac4b75c47 100644 --- a/spec/ruby/core/enumerable/find_spec.rb +++ b/spec/ruby/core/enumerable/find_spec.rb @@ -1,7 +1,78 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/find' +require_relative 'shared/enumerable_enumeratorized' describe "Enumerable#find" do - it_behaves_like :enumerable_find, :find + before :each do + ScratchPad.record [] + @elements = [2, 4, 6, 8, 10] + @numerous = EnumerableSpecs::Numerous.new(*@elements) + @empty = [] + end + + it "passes each entry in enum to block while block when block is false" do + visited_elements = [] + @numerous.find do |element| + visited_elements << element + false + end + visited_elements.should == @elements + end + + it "returns nil when the block is false and there is no ifnone proc given" do + @numerous.find {|e| false }.should == nil + end + + it "returns the first element for which the block is not false" do + @elements.each do |element| + @numerous.find {|e| e > element - 1 }.should == element + end + end + + it "returns the value of the ifnone proc if the block is false" do + fail_proc = -> { "cheeseburgers" } + @numerous.find(fail_proc) {|e| false }.should == "cheeseburgers" + end + + it "doesn't call the ifnone proc if an element is found" do + fail_proc = -> { raise "This shouldn't have been called" } + @numerous.find(fail_proc) {|e| e == @elements.first }.should == 2 + end + + it "calls the ifnone proc only once when the block is false" do + times = 0 + fail_proc = -> { times += 1; raise if times > 1; "cheeseburgers" } + @numerous.find(fail_proc) {|e| false }.should == "cheeseburgers" + end + + it "calls the ifnone proc when there are no elements" do + fail_proc = -> { "yay" } + @empty.find(fail_proc) {|e| true}.should == "yay" + end + + it "ignores the ifnone argument when nil" do + @numerous.find(nil) {|e| false }.should == nil + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.find { |x, i| ScratchPad << [x, i]; nil } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "returns an enumerator when no block given" do + @numerous.find.should.instance_of?(Enumerator) + end + + it "passes the ifnone proc to the enumerator" do + times = 0 + fail_proc = -> { times += 1; raise if times > 1; "cheeseburgers" } + @numerous.find(fail_proc).each {|e| false }.should == "cheeseburgers" + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.find {|e| e == [1, 2] }.should == [1, 2] + end + + it_behaves_like :enumerable_enumeratorized_with_unknown_size, :find end diff --git a/spec/ruby/core/enumerable/flat_map_spec.rb b/spec/ruby/core/enumerable/flat_map_spec.rb index bd07eab6c5..ef50cb2696 100644 --- a/spec/ruby/core/enumerable/flat_map_spec.rb +++ b/spec/ruby/core/enumerable/flat_map_spec.rb @@ -1,7 +1,56 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/collect_concat' +require_relative 'shared/enumerable_enumeratorized' describe "Enumerable#flat_map" do - it_behaves_like :enumerable_collect_concat, :flat_map + it "yields elements to the block and flattens one level" do + numerous = EnumerableSpecs::Numerous.new(1, [2, 3], [4, [5, 6]], {foo: :bar}) + numerous.flat_map { |i| i }.should == [1, 2, 3, 4, [5, 6], {foo: :bar}] + end + + it "appends non-Array elements that do not define #to_ary" do + obj = mock("to_ary undefined") + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.flat_map { |i| i }.should == [1, obj, 2] + end + + it "concatenates the result of calling #to_ary if it returns an Array" do + obj = mock("to_ary defined") + obj.should_receive(:to_ary).and_return([:a, :b]) + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.flat_map { |i| i }.should == [1, :a, :b, 2] + end + + it "does not call #to_a" do + obj = mock("to_ary undefined") + obj.should_not_receive(:to_a) + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.flat_map { |i| i }.should == [1, obj, 2] + end + + it "appends an element that defines #to_ary that returns nil" do + obj = mock("to_ary defined") + obj.should_receive(:to_ary).and_return(nil) + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.flat_map { |i| i }.should == [1, obj, 2] + end + + it "raises a TypeError if an element defining #to_ary does not return an Array or nil" do + obj = mock("to_ary defined") + obj.should_receive(:to_ary).and_return("array") + + -> { [1, obj, 3].flat_map { |i| i } }.should.raise(TypeError) + end + + it "returns an enumerator when no block given" do + enum = EnumerableSpecs::Numerous.new(1, 2).flat_map + enum.should.instance_of?(Enumerator) + enum.each{ |i| [i] * i }.should == [1, 2, 2] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :flat_map end diff --git a/spec/ruby/core/enumerable/include_spec.rb b/spec/ruby/core/enumerable/include_spec.rb index dab1b04451..d59b351486 100644 --- a/spec/ruby/core/enumerable/include_spec.rb +++ b/spec/ruby/core/enumerable/include_spec.rb @@ -1,7 +1,36 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/include' describe "Enumerable#include?" do - it_behaves_like :enumerable_include, :include? + it "returns true if any element == argument for numbers" do + class EnumerableSpecIncludeP; def ==(obj) obj == 5; end; end + + elements = (0..5).to_a + EnumerableSpecs::Numerous.new(*elements).include?(5).should == true + EnumerableSpecs::Numerous.new(*elements).include?(10).should == false + EnumerableSpecs::Numerous.new(*elements).include?(EnumerableSpecIncludeP.new).should == true + end + + it "returns true if any element == argument for other objects" do + class EnumerableSpecIncludeP11; def ==(obj); obj == '11'; end; end + + elements = ('0'..'5').to_a + [EnumerableSpecIncludeP11.new] + EnumerableSpecs::Numerous.new(*elements).include?('5').should == true + EnumerableSpecs::Numerous.new(*elements).include?('10').should == false + EnumerableSpecs::Numerous.new(*elements).include?(EnumerableSpecIncludeP11.new).should == true + EnumerableSpecs::Numerous.new(*elements).include?('11').should == true + end + + + it "returns true if any member of enum equals obj when == compare different classes (legacy rubycon)" do + # equality is tested with == + EnumerableSpecs::Numerous.new(2,4,6,8,10).include?(2.0).should == true + EnumerableSpecs::Numerous.new(2,4,[6,8],10).include?([6, 8]).should == true + EnumerableSpecs::Numerous.new(2,4,[6,8],10).include?([6.0, 8.0]).should == true + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.include?([1,2]).should == true + end end diff --git a/spec/ruby/core/enumerable/inject_spec.rb b/spec/ruby/core/enumerable/inject_spec.rb index e1fe216144..10de321395 100644 --- a/spec/ruby/core/enumerable/inject_spec.rb +++ b/spec/ruby/core/enumerable/inject_spec.rb @@ -1,7 +1,144 @@ require_relative '../../spec_helper' +require_relative '../array/shared/iterable_and_tolerating_size_increasing' require_relative 'fixtures/classes' -require_relative 'shared/inject' describe "Enumerable#inject" do - it_behaves_like :enumerable_inject, :inject + it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do + a = [] + EnumerableSpecs::Numerous.new.inject(0) { |memo, i| a << [memo, i]; i } + a.should == [[0, 2], [2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] + EnumerableSpecs::EachDefiner.new(true, true, true).inject(nil) {|result, i| i && result}.should == nil + end + + it "produces an array of the accumulator and the argument when given a block with a *arg" do + a = [] + [1,2].inject(0) {|*args| a << args; args[0] + args[1]} + a.should == [[0, 1], [1, 2]] + end + + it "can take two argument" do + EnumerableSpecs::Numerous.new(1, 2, 3).inject(10, :-).should == 4 + EnumerableSpecs::Numerous.new(1, 2, 3).inject(10, "-").should == 4 + + [1, 2, 3].inject(10, :-).should == 4 + [1, 2, 3].inject(10, "-").should == 4 + end + + it "converts non-Symbol method name argument to String with #to_str if two arguments" do + name = Object.new + def name.to_str; "-"; end + + EnumerableSpecs::Numerous.new(1, 2, 3).inject(10, name).should == 4 + [1, 2, 3].inject(10, name).should == 4 + end + + it "raises TypeError when the second argument is not Symbol or String and it cannot be converted to String if two arguments" do + -> { EnumerableSpecs::Numerous.new(1, 2, 3).inject(10, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) + -> { [1, 2, 3].inject(10, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) + end + + it "ignores the block if two arguments" do + -> { + EnumerableSpecs::Numerous.new(1, 2, 3).inject(10, :-) { raise "we never get here"}.should == 4 + }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true) + + -> { + [1, 2, 3].inject(10, :-) { raise "we never get here"}.should == 4 + }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true) + end + + it "does not warn when given a Symbol with $VERBOSE true" do + -> { + [1, 2].inject(0, :+) + [1, 2].inject(:+) + EnumerableSpecs::Numerous.new(1, 2).inject(0, :+) + EnumerableSpecs::Numerous.new(1, 2).inject(:+) + }.should_not complain(verbose: true) + end + + it "can take a symbol argument" do + EnumerableSpecs::Numerous.new(10, 1, 2, 3).inject(:-).should == 4 + [10, 1, 2, 3].inject(:-).should == 4 + end + + it "can take a String argument" do + EnumerableSpecs::Numerous.new(10, 1, 2, 3).inject("-").should == 4 + [10, 1, 2, 3].inject("-").should == 4 + end + + it "converts non-Symbol method name argument to String with #to_str" do + name = Object.new + def name.to_str; "-"; end + + EnumerableSpecs::Numerous.new(10, 1, 2, 3).inject(name).should == 4 + [10, 1, 2, 3].inject(name).should == 4 + end + + it "raises TypeError when passed not Symbol or String method name argument and it cannot be converted to String" do + -> { EnumerableSpecs::Numerous.new(10, 1, 2, 3).inject(Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) + -> { [10, 1, 2, 3].inject(Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) + end + + it "without argument takes a block with an accumulator (with first element as initial value) and the current element. Value of block becomes new accumulator" do + a = [] + EnumerableSpecs::Numerous.new.inject { |memo, i| a << [memo, i]; i } + a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.inject([]) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] + end + + it "with inject arguments(legacy rubycon)" do + # with inject argument + EnumerableSpecs::EachDefiner.new().inject(1) {|acc,x| 999 }.should == 1 + EnumerableSpecs::EachDefiner.new(2).inject(1) {|acc,x| 999 }.should == 999 + EnumerableSpecs::EachDefiner.new(2).inject(1) {|acc,x| acc }.should == 1 + EnumerableSpecs::EachDefiner.new(2).inject(1) {|acc,x| x }.should == 2 + + EnumerableSpecs::EachDefiner.new(1,2,3,4).inject(100) {|acc,x| acc + x }.should == 110 + EnumerableSpecs::EachDefiner.new(1,2,3,4).inject(100) {|acc,x| acc * x }.should == 2400 + + EnumerableSpecs::EachDefiner.new('a','b','c').inject("z") {|result, i| i+result}.should == "cbaz" + end + + it "without inject arguments(legacy rubycon)" do + # no inject argument + EnumerableSpecs::EachDefiner.new(2).inject {|acc,x| 999 }.should == 2 + EnumerableSpecs::EachDefiner.new(2).inject {|acc,x| acc }.should == 2 + EnumerableSpecs::EachDefiner.new(2).inject {|acc,x| x }.should == 2 + + EnumerableSpecs::EachDefiner.new(1,2,3,4).inject {|acc,x| acc + x }.should == 10 + EnumerableSpecs::EachDefiner.new(1,2,3,4).inject {|acc,x| acc * x }.should == 24 + + EnumerableSpecs::EachDefiner.new('a','b','c').inject {|result, i| i+result}.should == "cba" + EnumerableSpecs::EachDefiner.new(3, 4, 5).inject {|result, i| result*i}.should == 60 + EnumerableSpecs::EachDefiner.new([1], 2, 'a','b').inject {|r,i| r<<i}.should == [1, 2, 'a', 'b'] + end + + it "returns nil when fails(legacy rubycon)" do + EnumerableSpecs::EachDefiner.new().inject {|acc,x| 999 }.should == nil + end + + it "tolerates increasing a collection size during iterating Array" do + array = [:a, :b, :c] + ScratchPad.record [] + i = 0 + + array.inject(nil) do |_, e| + ScratchPad << e + array << i if i < 100 + i += 1 + end + + actual = ScratchPad.recorded + expected = [:a, :b, :c] + (0..99).to_a + actual.sort_by(&:to_s).should == expected.sort_by(&:to_s) + end + + it "raises an ArgumentError when no parameters or block is given" do + -> { [1,2].inject }.should.raise(ArgumentError) + -> { {one: 1, two: 2}.inject }.should.raise(ArgumentError) + end end diff --git a/spec/ruby/core/enumerable/map_spec.rb b/spec/ruby/core/enumerable/map_spec.rb index 98a70781cf..e6447f5c23 100644 --- a/spec/ruby/core/enumerable/map_spec.rb +++ b/spec/ruby/core/enumerable/map_spec.rb @@ -1,7 +1,109 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/collect' +require_relative 'shared/enumerable_enumeratorized' describe "Enumerable#map" do - it_behaves_like :enumerable_collect, :map + before :each do + ScratchPad.record [] + end + + it "returns a new array with the results of passing each element to block" do + entries = [0, 1, 3, 4, 5, 6] + numerous = EnumerableSpecs::Numerous.new(*entries) + numerous.map { |i| i % 2 }.should == [0, 1, 1, 0, 1, 0] + numerous.map { |i| i }.should == entries + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.map { |x, i| ScratchPad << [x, i]; nil } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "gathers initial args as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.map {|e| e}.should == [1,3,6] + end + + it "only yields increasing values for a Range" do + (1..0).map { |x| x }.should == [] + (1..1).map { |x| x }.should == [1] + (1..2).map { |x| x }.should == [1, 2] + end + + it "returns an enumerator when no block given" do + enum = EnumerableSpecs::Numerous.new.map + enum.should.instance_of?(Enumerator) + enum.each { |i| -i }.should == [-2, -5, -3, -6, -1, -4] + end + + it "reports the same arity as the given block" do + entries = [0, 1, 3, 4, 5, 6] + numerous = EnumerableSpecs::Numerous.new(*entries) + + def numerous.each(&block) + ScratchPad << block.arity + super + end + + numerous.map { |a, b| a % 2 }.should == [0, 1, 1, 0, 1, 0] + ScratchPad.recorded.should == [2] + ScratchPad.clear + ScratchPad.record [] + numerous.map { |i| i }.should == entries + ScratchPad.recorded.should == [1] + end + + it "yields an Array of 2 elements for a Hash when block arity is 1" do + c = Class.new do + def register(a) + ScratchPad << a + end + end + m = c.new.method(:register) + + ScratchPad.record [] + { 1 => 'a', 2 => 'b' }.map(&m) + ScratchPad.recorded.should == [[1, 'a'], [2, 'b']] + end + + it "yields 2 arguments for a Hash when block arity is 2" do + c = Class.new do + def register(a, b) + ScratchPad << [a, b] + end + end + m = c.new.method(:register) + + ScratchPad.record [] + { 1 => 'a', 2 => 'b' }.map(&m) + ScratchPad.recorded.should == [[1, 'a'], [2, 'b']] + end + + it "raises an error for a Hash when an arity enforcing block of arity >2 is passed in" do + c = Class.new do + def register(a, b, c) + end + end + m = c.new.method(:register) + + -> do + { 1 => 'a', 2 => 'b' }.map(&m) + end.should.raise(ArgumentError) + end + + it "calls the each method on sub-classes" do + c = Class.new(Hash) do + def each + ScratchPad << 'in each' + super + end + end + h = c.new + h[1] = 'a' + ScratchPad.record [] + h.map { |k,v| v } + ScratchPad.recorded.should == ['in each'] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :map end diff --git a/spec/ruby/core/enumerable/member_spec.rb b/spec/ruby/core/enumerable/member_spec.rb index 1fe3cebd28..be06880ebb 100644 --- a/spec/ruby/core/enumerable/member_spec.rb +++ b/spec/ruby/core/enumerable/member_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/include' describe "Enumerable#member?" do - it_behaves_like :enumerable_include, :member? + it "is an alias of Enumerable#include?" do + Enumerable.instance_method(:member?).should == Enumerable.instance_method(:include?) + end end diff --git a/spec/ruby/core/enumerable/reduce_spec.rb b/spec/ruby/core/enumerable/reduce_spec.rb index bc8691c1b0..40452b66a1 100644 --- a/spec/ruby/core/enumerable/reduce_spec.rb +++ b/spec/ruby/core/enumerable/reduce_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/inject' describe "Enumerable#reduce" do - it_behaves_like :enumerable_inject, :reduce + it "is an alias of Enumerable#inject" do + Enumerable.instance_method(:reduce).should == Enumerable.instance_method(:inject) + end end diff --git a/spec/ruby/core/enumerable/select_spec.rb b/spec/ruby/core/enumerable/select_spec.rb index 7fc61926f9..a53c228a44 100644 --- a/spec/ruby/core/enumerable/select_spec.rb +++ b/spec/ruby/core/enumerable/select_spec.rb @@ -1,7 +1,33 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/find_all' +require_relative 'shared/enumerable_enumeratorized' describe "Enumerable#select" do - it_behaves_like :enumerable_find_all, :select + before :each do + ScratchPad.record [] + @elements = (1..10).to_a + @numerous = EnumerableSpecs::Numerous.new(*@elements) + end + + it "returns all elements for which the block is not false" do + @numerous.select {|i| i % 3 == 0 }.should == [3, 6, 9] + @numerous.select {|i| true }.should == @elements + @numerous.select {|i| false }.should == [] + end + + it "returns an enumerator when no block given" do + @numerous.select.should.instance_of?(Enumerator) + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.select { |x, i| ScratchPad << [x, i] } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.select {|e| e == [3, 4, 5] }.should == [[3, 4, 5]] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :select end diff --git a/spec/ruby/core/enumerable/shared/collect.rb b/spec/ruby/core/enumerable/shared/collect.rb deleted file mode 100644 index 4696d32454..0000000000 --- a/spec/ruby/core/enumerable/shared/collect.rb +++ /dev/null @@ -1,107 +0,0 @@ -require_relative 'enumerable_enumeratorized' - -describe :enumerable_collect, shared: true do - before :each do - ScratchPad.record [] - end - - it "returns a new array with the results of passing each element to block" do - entries = [0, 1, 3, 4, 5, 6] - numerous = EnumerableSpecs::Numerous.new(*entries) - numerous.send(@method) { |i| i % 2 }.should == [0, 1, 1, 0, 1, 0] - numerous.send(@method) { |i| i }.should == entries - end - - it "passes through the values yielded by #each_with_index" do - [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i]; nil } - ScratchPad.recorded.should == [[:a, 0], [:b, 1]] - end - - it "gathers initial args as elements when each yields multiple" do - multi = EnumerableSpecs::YieldsMulti.new - multi.send(@method) {|e| e}.should == [1,3,6] - end - - it "only yields increasing values for a Range" do - (1..0).send(@method) { |x| x }.should == [] - (1..1).send(@method) { |x| x }.should == [1] - (1..2).send(@method) { |x| x }.should == [1, 2] - end - - it "returns an enumerator when no block given" do - enum = EnumerableSpecs::Numerous.new.send(@method) - enum.should.instance_of?(Enumerator) - enum.each { |i| -i }.should == [-2, -5, -3, -6, -1, -4] - end - - it "reports the same arity as the given block" do - entries = [0, 1, 3, 4, 5, 6] - numerous = EnumerableSpecs::Numerous.new(*entries) - - def numerous.each(&block) - ScratchPad << block.arity - super - end - - numerous.send(@method) { |a, b| a % 2 }.should == [0, 1, 1, 0, 1, 0] - ScratchPad.recorded.should == [2] - ScratchPad.clear - ScratchPad.record [] - numerous.send(@method) { |i| i }.should == entries - ScratchPad.recorded.should == [1] - end - - it "yields an Array of 2 elements for a Hash when block arity is 1" do - c = Class.new do - def register(a) - ScratchPad << a - end - end - m = c.new.method(:register) - - ScratchPad.record [] - { 1 => 'a', 2 => 'b' }.map(&m) - ScratchPad.recorded.should == [[1, 'a'], [2, 'b']] - end - - it "yields 2 arguments for a Hash when block arity is 2" do - c = Class.new do - def register(a, b) - ScratchPad << [a, b] - end - end - m = c.new.method(:register) - - ScratchPad.record [] - { 1 => 'a', 2 => 'b' }.map(&m) - ScratchPad.recorded.should == [[1, 'a'], [2, 'b']] - end - - it "raises an error for a Hash when an arity enforcing block of arity >2 is passed in" do - c = Class.new do - def register(a, b, c) - end - end - m = c.new.method(:register) - - -> do - { 1 => 'a', 2 => 'b' }.map(&m) - end.should.raise(ArgumentError) - end - - it "calls the each method on sub-classes" do - c = Class.new(Hash) do - def each - ScratchPad << 'in each' - super - end - end - h = c.new - h[1] = 'a' - ScratchPad.record [] - h.send(@method) { |k,v| v } - ScratchPad.recorded.should == ['in each'] - end - - it_should_behave_like :enumerable_enumeratorized_with_origin_size -end diff --git a/spec/ruby/core/enumerable/shared/collect_concat.rb b/spec/ruby/core/enumerable/shared/collect_concat.rb deleted file mode 100644 index 1694e3fdce..0000000000 --- a/spec/ruby/core/enumerable/shared/collect_concat.rb +++ /dev/null @@ -1,54 +0,0 @@ -require_relative 'enumerable_enumeratorized' - -describe :enumerable_collect_concat, shared: true do - it "yields elements to the block and flattens one level" do - numerous = EnumerableSpecs::Numerous.new(1, [2, 3], [4, [5, 6]], {foo: :bar}) - numerous.send(@method) { |i| i }.should == [1, 2, 3, 4, [5, 6], {foo: :bar}] - end - - it "appends non-Array elements that do not define #to_ary" do - obj = mock("to_ary undefined") - - numerous = EnumerableSpecs::Numerous.new(1, obj, 2) - numerous.send(@method) { |i| i }.should == [1, obj, 2] - end - - it "concatenates the result of calling #to_ary if it returns an Array" do - obj = mock("to_ary defined") - obj.should_receive(:to_ary).and_return([:a, :b]) - - numerous = EnumerableSpecs::Numerous.new(1, obj, 2) - numerous.send(@method) { |i| i }.should == [1, :a, :b, 2] - end - - it "does not call #to_a" do - obj = mock("to_ary undefined") - obj.should_not_receive(:to_a) - - numerous = EnumerableSpecs::Numerous.new(1, obj, 2) - numerous.send(@method) { |i| i }.should == [1, obj, 2] - end - - it "appends an element that defines #to_ary that returns nil" do - obj = mock("to_ary defined") - obj.should_receive(:to_ary).and_return(nil) - - numerous = EnumerableSpecs::Numerous.new(1, obj, 2) - numerous.send(@method) { |i| i }.should == [1, obj, 2] - end - - it "raises a TypeError if an element defining #to_ary does not return an Array or nil" do - obj = mock("to_ary defined") - obj.should_receive(:to_ary).and_return("array") - - -> { [1, obj, 3].send(@method) { |i| i } }.should.raise(TypeError) - end - - it "returns an enumerator when no block given" do - enum = EnumerableSpecs::Numerous.new(1, 2).send(@method) - enum.should.instance_of?(Enumerator) - enum.each{ |i| [i] * i }.should == [1, 2, 2] - end - - it_should_behave_like :enumerable_enumeratorized_with_origin_size -end diff --git a/spec/ruby/core/enumerable/shared/entries.rb b/spec/ruby/core/enumerable/shared/entries.rb deleted file mode 100644 index e32eb23d2a..0000000000 --- a/spec/ruby/core/enumerable/shared/entries.rb +++ /dev/null @@ -1,16 +0,0 @@ -describe :enumerable_entries, shared: true do - it "returns an array containing the elements" do - numerous = EnumerableSpecs::Numerous.new(1, nil, 'a', 2, false, true) - numerous.send(@method).should == [1, nil, "a", 2, false, true] - end - - it "passes through the values yielded by #each_with_index" do - [:a, :b].each_with_index.send(@method).should == [[:a, 0], [:b, 1]] - end - - it "passes arguments to each" do - count = EnumerableSpecs::EachCounter.new(1, 2, 3) - count.send(@method, :hello, "world").should == [1, 2, 3] - count.arguments_passed.should == [:hello, "world"] - end -end diff --git a/spec/ruby/core/enumerable/shared/find.rb b/spec/ruby/core/enumerable/shared/find.rb deleted file mode 100644 index cdff640415..0000000000 --- a/spec/ruby/core/enumerable/shared/find.rb +++ /dev/null @@ -1,77 +0,0 @@ -require_relative 'enumerable_enumeratorized' - -describe :enumerable_find, shared: true do - # #detect and #find are aliases, so we only need one function - before :each do - ScratchPad.record [] - @elements = [2, 4, 6, 8, 10] - @numerous = EnumerableSpecs::Numerous.new(*@elements) - @empty = [] - end - - it "passes each entry in enum to block while block when block is false" do - visited_elements = [] - @numerous.send(@method) do |element| - visited_elements << element - false - end - visited_elements.should == @elements - end - - it "returns nil when the block is false and there is no ifnone proc given" do - @numerous.send(@method) {|e| false }.should == nil - end - - it "returns the first element for which the block is not false" do - @elements.each do |element| - @numerous.send(@method) {|e| e > element - 1 }.should == element - end - end - - it "returns the value of the ifnone proc if the block is false" do - fail_proc = -> { "cheeseburgers" } - @numerous.send(@method, fail_proc) {|e| false }.should == "cheeseburgers" - end - - it "doesn't call the ifnone proc if an element is found" do - fail_proc = -> { raise "This shouldn't have been called" } - @numerous.send(@method, fail_proc) {|e| e == @elements.first }.should == 2 - end - - it "calls the ifnone proc only once when the block is false" do - times = 0 - fail_proc = -> { times += 1; raise if times > 1; "cheeseburgers" } - @numerous.send(@method, fail_proc) {|e| false }.should == "cheeseburgers" - end - - it "calls the ifnone proc when there are no elements" do - fail_proc = -> { "yay" } - @empty.send(@method, fail_proc) {|e| true}.should == "yay" - end - - it "ignores the ifnone argument when nil" do - @numerous.send(@method, nil) {|e| false }.should == nil - end - - it "passes through the values yielded by #each_with_index" do - [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i]; nil } - ScratchPad.recorded.should == [[:a, 0], [:b, 1]] - end - - it "returns an enumerator when no block given" do - @numerous.send(@method).should.instance_of?(Enumerator) - end - - it "passes the ifnone proc to the enumerator" do - times = 0 - fail_proc = -> { times += 1; raise if times > 1; "cheeseburgers" } - @numerous.send(@method, fail_proc).each {|e| false }.should == "cheeseburgers" - end - - it "gathers whole arrays as elements when each yields multiple" do - multi = EnumerableSpecs::YieldsMulti.new - multi.send(@method) {|e| e == [1, 2] }.should == [1, 2] - end - - it_should_behave_like :enumerable_enumeratorized_with_unknown_size -end diff --git a/spec/ruby/core/enumerable/shared/find_all.rb b/spec/ruby/core/enumerable/shared/find_all.rb deleted file mode 100644 index 27f01de6e0..0000000000 --- a/spec/ruby/core/enumerable/shared/find_all.rb +++ /dev/null @@ -1,31 +0,0 @@ -require_relative 'enumerable_enumeratorized' - -describe :enumerable_find_all, shared: true do - before :each do - ScratchPad.record [] - @elements = (1..10).to_a - @numerous = EnumerableSpecs::Numerous.new(*@elements) - end - - it "returns all elements for which the block is not false" do - @numerous.send(@method) {|i| i % 3 == 0 }.should == [3, 6, 9] - @numerous.send(@method) {|i| true }.should == @elements - @numerous.send(@method) {|i| false }.should == [] - end - - it "returns an enumerator when no block given" do - @numerous.send(@method).should.instance_of?(Enumerator) - end - - it "passes through the values yielded by #each_with_index" do - [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i] } - ScratchPad.recorded.should == [[:a, 0], [:b, 1]] - end - - it "gathers whole arrays as elements when each yields multiple" do - multi = EnumerableSpecs::YieldsMulti.new - multi.send(@method) {|e| e == [3, 4, 5] }.should == [[3, 4, 5]] - end - - it_should_behave_like :enumerable_enumeratorized_with_origin_size -end diff --git a/spec/ruby/core/enumerable/shared/include.rb b/spec/ruby/core/enumerable/shared/include.rb deleted file mode 100644 index ea250f032b..0000000000 --- a/spec/ruby/core/enumerable/shared/include.rb +++ /dev/null @@ -1,34 +0,0 @@ -describe :enumerable_include, shared: true do - it "returns true if any element == argument for numbers" do - class EnumerableSpecIncludeP; def ==(obj) obj == 5; end; end - - elements = (0..5).to_a - EnumerableSpecs::Numerous.new(*elements).send(@method,5).should == true - EnumerableSpecs::Numerous.new(*elements).send(@method,10).should == false - EnumerableSpecs::Numerous.new(*elements).send(@method,EnumerableSpecIncludeP.new).should == true - end - - it "returns true if any element == argument for other objects" do - class EnumerableSpecIncludeP11; def ==(obj); obj == '11'; end; end - - elements = ('0'..'5').to_a + [EnumerableSpecIncludeP11.new] - EnumerableSpecs::Numerous.new(*elements).send(@method,'5').should == true - EnumerableSpecs::Numerous.new(*elements).send(@method,'10').should == false - EnumerableSpecs::Numerous.new(*elements).send(@method,EnumerableSpecIncludeP11.new).should == true - EnumerableSpecs::Numerous.new(*elements).send(@method,'11').should == true - end - - - it "returns true if any member of enum equals obj when == compare different classes (legacy rubycon)" do - # equality is tested with == - EnumerableSpecs::Numerous.new(2,4,6,8,10).send(@method, 2.0).should == true - EnumerableSpecs::Numerous.new(2,4,[6,8],10).send(@method, [6, 8]).should == true - EnumerableSpecs::Numerous.new(2,4,[6,8],10).send(@method, [6.0, 8.0]).should == true - end - - it "gathers whole arrays as elements when each yields multiple" do - multi = EnumerableSpecs::YieldsMulti.new - multi.send(@method, [1,2]).should == true - end - -end diff --git a/spec/ruby/core/enumerable/shared/inject.rb b/spec/ruby/core/enumerable/shared/inject.rb deleted file mode 100644 index 7da4f0ca99..0000000000 --- a/spec/ruby/core/enumerable/shared/inject.rb +++ /dev/null @@ -1,142 +0,0 @@ -require_relative '../../array/shared/iterable_and_tolerating_size_increasing' - -describe :enumerable_inject, shared: true do - it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do - a = [] - EnumerableSpecs::Numerous.new.send(@method, 0) { |memo, i| a << [memo, i]; i } - a.should == [[0, 2], [2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] - EnumerableSpecs::EachDefiner.new(true, true, true).send(@method, nil) {|result, i| i && result}.should == nil - end - - it "produces an array of the accumulator and the argument when given a block with a *arg" do - a = [] - [1,2].send(@method, 0) {|*args| a << args; args[0] + args[1]} - a.should == [[0, 1], [1, 2]] - end - - it "can take two argument" do - EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-).should == 4 - EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, "-").should == 4 - - [1, 2, 3].send(@method, 10, :-).should == 4 - [1, 2, 3].send(@method, 10, "-").should == 4 - end - - it "converts non-Symbol method name argument to String with #to_str if two arguments" do - name = Object.new - def name.to_str; "-"; end - - EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, name).should == 4 - [1, 2, 3].send(@method, 10, name).should == 4 - end - - it "raises TypeError when the second argument is not Symbol or String and it cannot be converted to String if two arguments" do - -> { EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) - -> { [1, 2, 3].send(@method, 10, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) - end - - it "ignores the block if two arguments" do - -> { - EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-) { raise "we never get here"}.should == 4 - }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true) - - -> { - [1, 2, 3].send(@method, 10, :-) { raise "we never get here"}.should == 4 - }.should complain(/#{__FILE__}:#{__LINE__-1}: warning: given block not used/, verbose: true) - end - - it "does not warn when given a Symbol with $VERBOSE true" do - -> { - [1, 2].send(@method, 0, :+) - [1, 2].send(@method, :+) - EnumerableSpecs::Numerous.new(1, 2).send(@method, 0, :+) - EnumerableSpecs::Numerous.new(1, 2).send(@method, :+) - }.should_not complain(verbose: true) - end - - it "can take a symbol argument" do - EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, :-).should == 4 - [10, 1, 2, 3].send(@method, :-).should == 4 - end - - it "can take a String argument" do - EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, "-").should == 4 - [10, 1, 2, 3].send(@method, "-").should == 4 - end - - it "converts non-Symbol method name argument to String with #to_str" do - name = Object.new - def name.to_str; "-"; end - - EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, name).should == 4 - [10, 1, 2, 3].send(@method, name).should == 4 - end - - it "raises TypeError when passed not Symbol or String method name argument and it cannot be converted to String" do - -> { EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) - -> { [10, 1, 2, 3].send(@method, Object.new) }.should.raise(TypeError, /is not a symbol nor a string/) - end - - it "without argument takes a block with an accumulator (with first element as initial value) and the current element. Value of block becomes new accumulator" do - a = [] - EnumerableSpecs::Numerous.new.send(@method) { |memo, i| a << [memo, i]; i } - a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] - end - - it "gathers whole arrays as elements when each yields multiple" do - multi = EnumerableSpecs::YieldsMulti.new - multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] - end - - it "with inject arguments(legacy rubycon)" do - # with inject argument - EnumerableSpecs::EachDefiner.new().send(@method, 1) {|acc,x| 999 }.should == 1 - EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| 999 }.should == 999 - EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| acc }.should == 1 - EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| x }.should == 2 - - EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method, 100) {|acc,x| acc + x }.should == 110 - EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method, 100) {|acc,x| acc * x }.should == 2400 - - EnumerableSpecs::EachDefiner.new('a','b','c').send(@method, "z") {|result, i| i+result}.should == "cbaz" - end - - it "without inject arguments(legacy rubycon)" do - # no inject argument - EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 }.should == 2 - EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| acc }.should == 2 - EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| x }.should == 2 - - EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method) {|acc,x| acc + x }.should == 10 - EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method) {|acc,x| acc * x }.should == 24 - - EnumerableSpecs::EachDefiner.new('a','b','c').send(@method) {|result, i| i+result}.should == "cba" - EnumerableSpecs::EachDefiner.new(3, 4, 5).send(@method) {|result, i| result*i}.should == 60 - EnumerableSpecs::EachDefiner.new([1], 2, 'a','b').send(@method){|r,i| r<<i}.should == [1, 2, 'a', 'b'] - end - - it "returns nil when fails(legacy rubycon)" do - EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil - end - - it "tolerates increasing a collection size during iterating Array" do - array = [:a, :b, :c] - ScratchPad.record [] - i = 0 - - array.send(@method, nil) do |_, e| - ScratchPad << e - array << i if i < 100 - i += 1 - end - - actual = ScratchPad.recorded - expected = [:a, :b, :c] + (0..99).to_a - actual.sort_by(&:to_s).should == expected.sort_by(&:to_s) - end - - it "raises an ArgumentError when no parameters or block is given" do - -> { [1,2].send(@method) }.should.raise(ArgumentError) - -> { {one: 1, two: 2}.send(@method) }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/core/enumerable/to_a_spec.rb b/spec/ruby/core/enumerable/to_a_spec.rb index 723f922574..f1796070f0 100644 --- a/spec/ruby/core/enumerable/to_a_spec.rb +++ b/spec/ruby/core/enumerable/to_a_spec.rb @@ -1,7 +1,19 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/entries' describe "Enumerable#to_a" do - it_behaves_like :enumerable_entries, :to_a + it "returns an array containing the elements" do + numerous = EnumerableSpecs::Numerous.new(1, nil, 'a', 2, false, true) + numerous.to_a.should == [1, nil, "a", 2, false, true] + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.to_a.should == [[:a, 0], [:b, 1]] + end + + it "passes arguments to each" do + count = EnumerableSpecs::EachCounter.new(1, 2, 3) + count.to_a(:hello, "world").should == [1, 2, 3] + count.arguments_passed.should == [:hello, "world"] + end end diff --git a/spec/ruby/core/enumerator/each_spec.rb b/spec/ruby/core/enumerator/each_spec.rb index 03be53fe05..64912b98b6 100644 --- a/spec/ruby/core/enumerator/each_spec.rb +++ b/spec/ruby/core/enumerator/each_spec.rb @@ -1,6 +1,21 @@ require_relative '../../spec_helper' +require_relative 'shared/each' describe "Enumerator#each" do + describe "passing source-yielded arguments to the block" do + before :each do + @object = -> e { e } + end + it_behaves_like :enum_each, nil + end + + describe "passing source-yielded arguments to the block (lazy)" do + before :each do + @object = -> e { e.lazy } + end + it_behaves_like :enum_each, nil + end + before :each do object_each_with_arguments = Object.new def object_each_with_arguments.each_with_arguments(arg, *args) diff --git a/spec/ruby/core/enumerator/each_with_object_spec.rb b/spec/ruby/core/enumerator/each_with_object_spec.rb index 84a45ae89d..0e0a4496f4 100644 --- a/spec/ruby/core/enumerator/each_with_object_spec.rb +++ b/spec/ruby/core/enumerator/each_with_object_spec.rb @@ -1,6 +1,42 @@ require_relative '../../spec_helper' -require_relative 'shared/with_object' describe "Enumerator#each_with_object" do - it_behaves_like :enum_with_object, :each_with_object + before :each do + @enum = [:a, :b].to_enum + @memo = '' + @block_params = @enum.each_with_object(@memo).to_a + end + + it "receives an argument" do + @enum.method(:each_with_object).arity.should == 1 + end + + context "with block" do + it "returns the given object" do + ret = @enum.each_with_object(@memo) do |elm, memo| + # nothing + end + ret.should.equal?(@memo) + end + + context "the block parameter" do + it "passes each element to first parameter" do + @block_params[0][0].should.equal?(:a) + @block_params[1][0].should.equal?(:b) + end + + it "passes the given object to last parameter" do + @block_params[0][1].should.equal?(@memo) + @block_params[1][1].should.equal?(@memo) + end + end + end + + context "without block" do + it "returns new Enumerator" do + ret = @enum.each_with_object(@memo) + ret.should.instance_of?(Enumerator) + ret.should_not.equal?(@enum) + end + end end diff --git a/spec/ruby/core/enumerator/enum_for_spec.rb b/spec/ruby/core/enumerator/enum_for_spec.rb deleted file mode 100644 index fbdf98545a..0000000000 --- a/spec/ruby/core/enumerator/enum_for_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require_relative '../../spec_helper' -require_relative 'shared/enum_for' - -describe "Enumerator#enum_for" do - it_behaves_like :enum_for, :enum_for -end diff --git a/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb b/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb index 8765bb2190..d9fd576e43 100644 --- a/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb +++ b/spec/ruby/core/enumerator/lazy/collect_concat_spec.rb @@ -1,8 +1,8 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/collect_concat' describe "Enumerator::Lazy#collect_concat" do - it_behaves_like :enumerator_lazy_collect_concat, :collect_concat + it "is an alias of Enumerator::Lazy#flat_map" do + Enumerator::Lazy.instance_method(:collect_concat).should == + Enumerator::Lazy.instance_method(:flat_map) + end end diff --git a/spec/ruby/core/enumerator/lazy/collect_spec.rb b/spec/ruby/core/enumerator/lazy/collect_spec.rb index 14b79ce16d..53a477e053 100644 --- a/spec/ruby/core/enumerator/lazy/collect_spec.rb +++ b/spec/ruby/core/enumerator/lazy/collect_spec.rb @@ -1,8 +1,8 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/collect' describe "Enumerator::Lazy#collect" do - it_behaves_like :enumerator_lazy_collect, :collect + it "is an alias of Enumerator::Lazy#map" do + Enumerator::Lazy.instance_method(:collect).should == + Enumerator::Lazy.instance_method(:map) + end end diff --git a/spec/ruby/core/enumerator/lazy/enum_for_spec.rb b/spec/ruby/core/enumerator/lazy/enum_for_spec.rb index 7e7783f6f1..b40c5d9915 100644 --- a/spec/ruby/core/enumerator/lazy/enum_for_spec.rb +++ b/spec/ruby/core/enumerator/lazy/enum_for_spec.rb @@ -1,8 +1,8 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/to_enum' describe "Enumerator::Lazy#enum_for" do - it_behaves_like :enumerator_lazy_to_enum, :enum_for + it "is an alias of Enumerator::Lazy#to_enum" do + Enumerator::Lazy.instance_method(:enum_for).should == + Enumerator::Lazy.instance_method(:to_enum) + end end diff --git a/spec/ruby/core/enumerator/lazy/filter_spec.rb b/spec/ruby/core/enumerator/lazy/filter_spec.rb index 43128241e0..3ca5376faa 100644 --- a/spec/ruby/core/enumerator/lazy/filter_spec.rb +++ b/spec/ruby/core/enumerator/lazy/filter_spec.rb @@ -1,6 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/select' describe "Enumerator::Lazy#filter" do - it_behaves_like :enumerator_lazy_select, :filter + it "is an alias of Enumerator::Lazy#select" do + Enumerator::Lazy.instance_method(:filter).should == + Enumerator::Lazy.instance_method(:select) + end end diff --git a/spec/ruby/core/enumerator/lazy/find_all_spec.rb b/spec/ruby/core/enumerator/lazy/find_all_spec.rb index 8b05c53803..64930dc61b 100644 --- a/spec/ruby/core/enumerator/lazy/find_all_spec.rb +++ b/spec/ruby/core/enumerator/lazy/find_all_spec.rb @@ -1,8 +1,8 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/select' describe "Enumerator::Lazy#find_all" do - it_behaves_like :enumerator_lazy_select, :find_all + it "is an alias of Enumerator::Lazy#select" do + Enumerator::Lazy.instance_method(:find_all).should == + Enumerator::Lazy.instance_method(:select) + end end diff --git a/spec/ruby/core/enumerator/lazy/flat_map_spec.rb b/spec/ruby/core/enumerator/lazy/flat_map_spec.rb index 5dcaa8bfa1..609bf95b9e 100644 --- a/spec/ruby/core/enumerator/lazy/flat_map_spec.rb +++ b/spec/ruby/core/enumerator/lazy/flat_map_spec.rb @@ -1,10 +1,78 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/collect_concat' +require_relative 'fixtures/classes' describe "Enumerator::Lazy#flat_map" do - it_behaves_like :enumerator_lazy_collect_concat, :flat_map + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.flat_map {} + ret.should.instance_of?(Enumerator::Lazy) + ret.should_not.equal?(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.flat_map { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.flat_map { |n| (n * 10).to_s }.first(6).should == %w[0 10 20 30 40 50] + + @eventsmixed.flat_map {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "flattens elements when the given block returned an array or responding to .each and .force" do + (0..Float::INFINITY).lazy.flat_map { |n| (n * 10).to_s.chars }.first(6).should == %w[0 1 0 2 0 3] + (0..Float::INFINITY).lazy.flat_map { |n| (n * 10).to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should == true + (0..Float::INFINITY).lazy.flat_map { |n| (n * 10).to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3] + end + end + + it "calls the block with initial values when yield with multiple arguments" do + yields = [] + @yieldsmixed.flat_map { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields + end + + it "raises an ArgumentError when not given a block" do + -> { @yieldsmixed.flat_map }.should.raise(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.flat_map {}.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.map {|n| n * 10 }.flat_map { |n| n.to_s }.first(6).should == %w[0 10 20 30 40 50] + + @eventsmixed.flat_map {}.flat_map {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "flattens elements when the given block returned an array or responding to .each and .force" do + (0..Float::INFINITY).lazy.map {|n| n * 10 }.flat_map { |n| n.to_s.chars }.first(6).should == %w[0 1 0 2 0 3] + (0..Float::INFINITY).lazy.map {|n| n * 10 }.flat_map { |n| n.to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should == true + (0..Float::INFINITY).lazy.map {|n| n * 10 }.flat_map { |n| n.to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3] + end + end + end + + it "works with an infinite enumerable" do + s = 0..Float::INFINITY + s.lazy.flat_map { |n| [-n, +n] }.first(200).should == + s.first(100).flat_map { |n| [-n, +n] }.to_a + end it "properly unwraps nested yields" do s = Enumerator.new do |y| loop do y << [1, 2] end end diff --git a/spec/ruby/core/enumerator/lazy/map_spec.rb b/spec/ruby/core/enumerator/lazy/map_spec.rb index 5cb998f5f7..2c7f8efab8 100644 --- a/spec/ruby/core/enumerator/lazy/map_spec.rb +++ b/spec/ruby/core/enumerator/lazy/map_spec.rb @@ -1,10 +1,62 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/collect' +require_relative 'fixtures/classes' describe "Enumerator::Lazy#map" do - it_behaves_like :enumerator_lazy_collect, :map + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.map {} + ret.should.instance_of?(Enumerator::Lazy) + ret.should_not.equal?(@yieldsmixed) + end + + it "keeps size" do + Enumerator::Lazy.new(Object.new, 100) {}.map {}.size.should == 100 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.map(&:succ).first(3).should == [1, 2, 3] + + @eventsmixed.map {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with initial values when yield with multiple arguments" do + yields = [] + @yieldsmixed.map { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields + end + + describe "on a nested Lazy" do + it "keeps size" do + Enumerator::Lazy.new(Object.new, 100) {}.map {}.map {}.size.should == 100 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.map(&:succ).map(&:succ).first(3).should == [2, 3, 4] + + @eventsmixed.map {}.map {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end + + it "works with an infinite enumerable" do + s = 0..Float::INFINITY + s.lazy.map { |n| n }.first(100).should == + s.first(100).map { |n| n }.to_a + end it "doesn't unwrap Arrays" do Enumerator.new {|y| y.yield([1])}.lazy.to_a.should == [[1]] diff --git a/spec/ruby/core/enumerator/lazy/select_spec.rb b/spec/ruby/core/enumerator/lazy/select_spec.rb index 3773d8f0a8..29c8f1bd80 100644 --- a/spec/ruby/core/enumerator/lazy/select_spec.rb +++ b/spec/ruby/core/enumerator/lazy/select_spec.rb @@ -1,10 +1,66 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/select' +require_relative 'fixtures/classes' describe "Enumerator::Lazy#select" do - it_behaves_like :enumerator_lazy_select, :select + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.select {} + ret.should.instance_of?(Enumerator::Lazy) + ret.should_not.equal?(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.select { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.select(&:even?).first(3).should == [0, 2, 4] + + @eventsmixed.select { true }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with a gathered array when yield with multiple arguments" do + yields = [] + @yieldsmixed.select { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields + end + + it "raises an ArgumentError when not given a block" do + -> { @yieldsmixed.select }.should.raise(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.select { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.select { |n| n > 5 }.select(&:even?).first(3).should == [6, 8, 10] + + @eventsmixed.select { true }.select { true }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end + + it "works with an infinite enumerable" do + s = 0..Float::INFINITY + s.lazy.select { |n| true }.first(100).should == + s.first(100).select { |n| true } + end it "doesn't pre-evaluate the next element" do eval_count = 0 diff --git a/spec/ruby/core/enumerator/lazy/shared/collect.rb b/spec/ruby/core/enumerator/lazy/shared/collect.rb deleted file mode 100644 index 0ed04c8e02..0000000000 --- a/spec/ruby/core/enumerator/lazy/shared/collect.rb +++ /dev/null @@ -1,62 +0,0 @@ -# -*- encoding: us-ascii -*- - -require_relative '../../../../spec_helper' -require_relative '../fixtures/classes' - -describe :enumerator_lazy_collect, shared: true do - before :each do - @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy - @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy - ScratchPad.record [] - end - - after :each do - ScratchPad.clear - end - - it "returns a new instance of Enumerator::Lazy" do - ret = @yieldsmixed.send(@method) {} - ret.should.instance_of?(Enumerator::Lazy) - ret.should_not.equal?(@yieldsmixed) - end - - it "keeps size" do - Enumerator::Lazy.new(Object.new, 100) {}.send(@method) {}.size.should == 100 - end - - describe "when the returned lazy enumerator is evaluated by Enumerable#first" do - it "stops after specified times" do - (0..Float::INFINITY).lazy.send(@method, &:succ).first(3).should == [1, 2, 3] - - @eventsmixed.send(@method) {}.first(1) - ScratchPad.recorded.should == [:before_yield] - end - end - - it "calls the block with initial values when yield with multiple arguments" do - yields = [] - @yieldsmixed.send(@method) { |v| yields << v }.force - yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields - end - - describe "on a nested Lazy" do - it "keeps size" do - Enumerator::Lazy.new(Object.new, 100) {}.send(@method) {}.send(@method) {}.size.should == 100 - end - - describe "when the returned lazy enumerator is evaluated by Enumerable#first" do - it "stops after specified times" do - (0..Float::INFINITY).lazy.send(@method, &:succ).send(@method, &:succ).first(3).should == [2, 3, 4] - - @eventsmixed.send(@method) {}.send(@method) {}.first(1) - ScratchPad.recorded.should == [:before_yield] - end - end - end - - it "works with an infinite enumerable" do - s = 0..Float::INFINITY - s.lazy.send(@method) { |n| n }.first(100).should == - s.first(100).send(@method) { |n| n }.to_a - end -end diff --git a/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb b/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb deleted file mode 100644 index 685e6d0594..0000000000 --- a/spec/ruby/core/enumerator/lazy/shared/collect_concat.rb +++ /dev/null @@ -1,78 +0,0 @@ -# -*- encoding: us-ascii -*- - -require_relative '../../../../spec_helper' -require_relative '../fixtures/classes' - -describe :enumerator_lazy_collect_concat, shared: true do - before :each do - @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy - @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy - ScratchPad.record [] - end - - after :each do - ScratchPad.clear - end - - it "returns a new instance of Enumerator::Lazy" do - ret = @yieldsmixed.send(@method) {} - ret.should.instance_of?(Enumerator::Lazy) - ret.should_not.equal?(@yieldsmixed) - end - - it "sets #size to nil" do - Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { true }.size.should == nil - end - - describe "when the returned lazy enumerator is evaluated by Enumerable#first" do - it "stops after specified times" do - (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s }.first(6).should == %w[0 10 20 30 40 50] - - @eventsmixed.send(@method) {}.first(1) - ScratchPad.recorded.should == [:before_yield] - end - - it "flattens elements when the given block returned an array or responding to .each and .force" do - (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.chars }.first(6).should == %w[0 1 0 2 0 3] - (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should == true - (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3] - end - end - - it "calls the block with initial values when yield with multiple arguments" do - yields = [] - @yieldsmixed.send(@method) { |v| yields << v }.force - yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields - end - - it "raises an ArgumentError when not given a block" do - -> { @yieldsmixed.send(@method) }.should.raise(ArgumentError) - end - - describe "on a nested Lazy" do - it "sets #size to nil" do - Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.send(@method) {}.size.should == nil - end - - describe "when the returned lazy enumerator is evaluated by Enumerable#first" do - it "stops after specified times" do - (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s }.first(6).should == %w[0 10 20 30 40 50] - - @eventsmixed.send(@method) {}.send(@method) {}.first(1) - ScratchPad.recorded.should == [:before_yield] - end - - it "flattens elements when the given block returned an array or responding to .each and .force" do - (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.chars }.first(6).should == %w[0 1 0 2 0 3] - (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should == true - (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3] - end - end - end - - it "works with an infinite enumerable" do - s = 0..Float::INFINITY - s.lazy.send(@method) { |n| [-n, +n] }.first(200).should == - s.first(100).send(@method) { |n| [-n, +n] }.to_a - end -end diff --git a/spec/ruby/core/enumerator/lazy/shared/select.rb b/spec/ruby/core/enumerator/lazy/shared/select.rb deleted file mode 100644 index 2d15176620..0000000000 --- a/spec/ruby/core/enumerator/lazy/shared/select.rb +++ /dev/null @@ -1,66 +0,0 @@ -# -*- encoding: us-ascii -*- - -require_relative '../../../../spec_helper' -require_relative '../fixtures/classes' - -describe :enumerator_lazy_select, shared: true do - before :each do - @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy - @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy - ScratchPad.record [] - end - - after :each do - ScratchPad.clear - end - - it "returns a new instance of Enumerator::Lazy" do - ret = @yieldsmixed.send(@method) {} - ret.should.instance_of?(Enumerator::Lazy) - ret.should_not.equal?(@yieldsmixed) - end - - it "sets #size to nil" do - Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { true }.size.should == nil - end - - describe "when the returned lazy enumerator is evaluated by Enumerable#first" do - it "stops after specified times" do - (0..Float::INFINITY).lazy.send(@method, &:even?).first(3).should == [0, 2, 4] - - @eventsmixed.send(@method) { true }.first(1) - ScratchPad.recorded.should == [:before_yield] - end - end - - it "calls the block with a gathered array when yield with multiple arguments" do - yields = [] - @yieldsmixed.send(@method) { |v| yields << v }.force - yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields - end - - it "raises an ArgumentError when not given a block" do - -> { @yieldsmixed.send(@method) }.should.raise(ArgumentError) - end - - describe "on a nested Lazy" do - it "sets #size to nil" do - Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.send(@method) { true }.size.should == nil - end - - describe "when the returned lazy enumerator is evaluated by Enumerable#first" do - it "stops after specified times" do - (0..Float::INFINITY).lazy.send(@method) { |n| n > 5 }.send(@method, &:even?).first(3).should == [6, 8, 10] - - @eventsmixed.send(@method) { true }.send(@method) { true }.first(1) - ScratchPad.recorded.should == [:before_yield] - end - end - end - - it "works with an infinite enumerable" do - s = 0..Float::INFINITY - s.lazy.send(@method) { |n| true }.first(100).should == - s.first(100).send(@method) { |n| true } - end -end diff --git a/spec/ruby/core/enumerator/lazy/shared/to_enum.rb b/spec/ruby/core/enumerator/lazy/shared/to_enum.rb deleted file mode 100644 index 75b9aefe8c..0000000000 --- a/spec/ruby/core/enumerator/lazy/shared/to_enum.rb +++ /dev/null @@ -1,55 +0,0 @@ -# -*- encoding: us-ascii -*- - -require_relative '../../../../spec_helper' - -describe :enumerator_lazy_to_enum, shared: true do - before :each do - @infinite = (0..Float::INFINITY).lazy - end - - it "requires multiple arguments" do - Enumerator::Lazy.instance_method(@method).arity.should < 0 - end - - it "returns a new instance of Enumerator::Lazy" do - ret = @infinite.send @method - ret.should.instance_of?(Enumerator::Lazy) - ret.should_not.equal?(@infinite) - end - - it "sets #size to nil when not given a block" do - Enumerator::Lazy.new(Object.new, 100) {}.send(@method).size.should == nil - end - - it "sets given block to size when given a block" do - Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { 30 }.size.should == 30 - end - - it "generates a lazy enumerator from the given name" do - @infinite.send(@method, :with_index, 10).first(3).should == [[0, 10], [1, 11], [2, 12]] - end - - it "passes given arguments to wrapped method" do - @infinite.send(@method, :each_slice, 2).map { |assoc| assoc.first * assoc.last }.first(4).should == [0, 6, 20, 42] - end - - it "used by some parent's methods though returning Lazy" do - { each_with_index: [], - with_index: [], - cycle: [1], - each_with_object: [Object.new], - with_object: [Object.new], - each_slice: [2], - each_entry: [], - each_cons: [2] - }.each_pair do |method, args| - @infinite.send(method, *args).should.instance_of?(Enumerator::Lazy) - end - end - - it "works with an infinite enumerable" do - s = 0..Float::INFINITY - s.lazy.send(@method, :with_index).first(100).should == - s.first(100).to_enum.send(@method, :with_index).to_a - end -end diff --git a/spec/ruby/core/enumerator/lazy/to_enum_spec.rb b/spec/ruby/core/enumerator/lazy/to_enum_spec.rb index 210e5294b7..c0233d60fa 100644 --- a/spec/ruby/core/enumerator/lazy/to_enum_spec.rb +++ b/spec/ruby/core/enumerator/lazy/to_enum_spec.rb @@ -1,8 +1,54 @@ -# -*- encoding: us-ascii -*- - require_relative '../../../spec_helper' -require_relative 'shared/to_enum' +require_relative 'fixtures/classes' describe "Enumerator::Lazy#to_enum" do - it_behaves_like :enumerator_lazy_to_enum, :to_enum + before :each do + @infinite = (0..Float::INFINITY).lazy + end + + it "requires multiple arguments" do + Enumerator::Lazy.instance_method(:to_enum).arity.should < 0 + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @infinite.to_enum + ret.should.instance_of?(Enumerator::Lazy) + ret.should_not.equal?(@infinite) + end + + it "sets #size to nil when not given a block" do + Enumerator::Lazy.new(Object.new, 100) {}.to_enum.size.should == nil + end + + it "sets given block to size when given a block" do + Enumerator::Lazy.new(Object.new, 100) {}.to_enum { 30 }.size.should == 30 + end + + it "generates a lazy enumerator from the given name" do + @infinite.to_enum(:with_index, 10).first(3).should == [[0, 10], [1, 11], [2, 12]] + end + + it "passes given arguments to wrapped method" do + @infinite.to_enum(:each_slice, 2).map { |assoc| assoc.first * assoc.last }.first(4).should == [0, 6, 20, 42] + end + + it "used by some parent's methods though returning Lazy" do + { each_with_index: [], + with_index: [], + cycle: [1], + each_with_object: [Object.new], + with_object: [Object.new], + each_slice: [2], + each_entry: [], + each_cons: [2] + }.each_pair do |method, args| + @infinite.send(method, *args).should.instance_of?(Enumerator::Lazy) + end + end + + it "works with an infinite enumerable" do + s = 0..Float::INFINITY + s.lazy.to_enum(:with_index).first(100).should == + s.first(100).to_enum.to_enum(:with_index).to_a + end end diff --git a/spec/ruby/core/enumerator/shared/each.rb b/spec/ruby/core/enumerator/shared/each.rb new file mode 100644 index 0000000000..18ca773207 --- /dev/null +++ b/spec/ruby/core/enumerator/shared/each.rb @@ -0,0 +1,46 @@ +# #each passes source-yielded values to the block by ordinary block arity +# (rb_yield_values2 semantics in CRuby), unlike the Enumerable collection methods +# which pack them via rb_enum_values_pack() (see enumerable/shared/value_packing.rb). +describe :enum_each, shared: true do + # @object must be set to a Proc that wraps an Enumerator into the receiver + # under test (e.g. -> e { e } for Enumerator#each, -> e { e.lazy } for Lazy#each). + describe "with a source that yields multiple values" do + before :each do + @enum = @object.call(Enumerator.new { |y| y.yield 1, 2; y.yield 3, 4 }) + end + + it "yields the first value to a single-argument block" do + collected = [] + @enum.each { |x| collected << x } + collected.should == [1, 3] + end + + it "yields each value to a multi-argument block" do + collected = [] + @enum.each { |x, y| collected << [x, y] } + collected.should == [[1, 2], [3, 4]] + end + + it "gathers the values for a splat block" do + collected = [] + @enum.each { |*args| collected << args } + collected.should == [[1, 2], [3, 4]] + end + end + + describe "with a source that yields a single value" do + it "yields the value to a single-argument block" do + collected = [] + @object.call(Enumerator.new { |y| y.yield 7; y.yield 8 }).each { |x| collected << x } + collected.should == [7, 8] + end + end + + describe "with a source that yields no value" do + it "yields nil to a single-argument block" do + collected = [] + @object.call(Enumerator.new { |y| y.yield; y.yield }).each { |x| collected << x } + collected.should == [nil, nil] + end + end +end diff --git a/spec/ruby/core/enumerator/shared/enum_for.rb b/spec/ruby/core/enumerator/shared/enum_for.rb deleted file mode 100644 index 4388103ecf..0000000000 --- a/spec/ruby/core/enumerator/shared/enum_for.rb +++ /dev/null @@ -1,57 +0,0 @@ -describe :enum_for, shared: true do - it "is defined in Kernel" do - Kernel.method_defined?(@method).should == true - end - - it "returns a new enumerator" do - "abc".send(@method).should.instance_of?(Enumerator) - end - - it "defaults the first argument to :each" do - enum = [1,2].send(@method) - enum.map { |v| v }.should == [1,2].each { |v| v } - end - - it "sets regexp matches in the caller" do - "wawa".send(@method, :scan, /./).map {|o| $& }.should == ["w", "a", "w", "a"] - a = [] - "wawa".send(@method, :scan, /./).each {|o| a << $& } - a.should == ["w", "a", "w", "a"] - end - - it "exposes multi-arg yields as an array" do - o = Object.new - def o.each - yield :a - yield :b1, :b2 - yield [:c] - yield :d1, :d2 - yield :e1, :e2, :e3 - end - - enum = o.send(@method) - enum.next.should == :a - enum.next.should == [:b1, :b2] - enum.next.should == [:c] - enum.next.should == [:d1, :d2] - enum.next.should == [:e1, :e2, :e3] - end - - it "uses the passed block's value to calculate the size of the enumerator" do - Object.new.enum_for { 100 }.size.should == 100 - end - - it "defers the evaluation of the passed block until #size is called" do - ScratchPad.record [] - - enum = Object.new.enum_for do - ScratchPad << :called - 100 - end - - ScratchPad.recorded.should.empty? - - enum.size - ScratchPad.recorded.should == [:called] - end -end diff --git a/spec/ruby/core/enumerator/shared/with_object.rb b/spec/ruby/core/enumerator/shared/with_object.rb deleted file mode 100644 index 50d4f24eb3..0000000000 --- a/spec/ruby/core/enumerator/shared/with_object.rb +++ /dev/null @@ -1,42 +0,0 @@ -require_relative '../../../spec_helper' - -describe :enum_with_object, shared: true do - before :each do - @enum = [:a, :b].to_enum - @memo = '' - @block_params = @enum.send(@method, @memo).to_a - end - - it "receives an argument" do - @enum.method(@method).arity.should == 1 - end - - context "with block" do - it "returns the given object" do - ret = @enum.send(@method, @memo) do |elm, memo| - # nothing - end - ret.should.equal?(@memo) - end - - context "the block parameter" do - it "passes each element to first parameter" do - @block_params[0][0].should.equal?(:a) - @block_params[1][0].should.equal?(:b) - end - - it "passes the given object to last parameter" do - @block_params[0][1].should.equal?(@memo) - @block_params[1][1].should.equal?(@memo) - end - end - end - - context "without block" do - it "returns new Enumerator" do - ret = @enum.send(@method, @memo) - ret.should.instance_of?(Enumerator) - ret.should_not.equal?(@enum) - end - end -end diff --git a/spec/ruby/core/enumerator/to_enum_spec.rb b/spec/ruby/core/enumerator/to_enum_spec.rb deleted file mode 100644 index 7fb73d0c3c..0000000000 --- a/spec/ruby/core/enumerator/to_enum_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require_relative '../../spec_helper' -require_relative 'shared/enum_for' - -describe "Enumerator#to_enum" do - it_behaves_like :enum_for, :to_enum -end diff --git a/spec/ruby/core/enumerator/with_object_spec.rb b/spec/ruby/core/enumerator/with_object_spec.rb index 58031fd765..790be66a11 100644 --- a/spec/ruby/core/enumerator/with_object_spec.rb +++ b/spec/ruby/core/enumerator/with_object_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/with_object' describe "Enumerator#with_object" do - it_behaves_like :enum_with_object, :with_object + it "is an alias of Enumerator#each_with_object" do + Enumerator.instance_method(:with_object).should == Enumerator.instance_method(:each_with_object) + end end diff --git a/spec/ruby/core/env/each_pair_spec.rb b/spec/ruby/core/env/each_pair_spec.rb index 2d7ed5faa0..1acd2fbb00 100644 --- a/spec/ruby/core/env/each_pair_spec.rb +++ b/spec/ruby/core/env/each_pair_spec.rb @@ -1,6 +1,63 @@ require_relative 'spec_helper' -require_relative 'shared/each' +require_relative '../enumerable/shared/enumeratorized' describe "ENV.each_pair" do - it_behaves_like :env_each, :each_pair + it "returns each pair" do + orig = ENV.to_hash + e = [] + begin + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "boo" + ENV.each_pair { |k, v| e << [k, v] }.should.equal?(ENV) + e.should.include?(["foo", "bar"]) + e.should.include?(["baz", "boo"]) + ensure + ENV.replace orig + end + end + + it "returns an Enumerator if called without a block" do + enum = ENV.each_pair + enum.should.instance_of?(Enumerator) + enum.each do |name, value| + ENV[name].should == value + end + end + + it_behaves_like :enumeratorized_with_origin_size, :each_pair, ENV + + describe "with encoding" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::BINARY + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "uses the locale encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + + ENV.each_pair do |key, value| + key.should.be_locale_env + value.should.be_locale_env + end + end + + it "transcodes from the locale encoding to Encoding.default_internal if set" do + Encoding.default_internal = internal = Encoding::IBM437 + + ENV.each_pair do |key, value| + key.encoding.should.equal?(internal) + if value.ascii_only? + value.encoding.should.equal?(internal) + end + end + end + end end diff --git a/spec/ruby/core/env/each_spec.rb b/spec/ruby/core/env/each_spec.rb index d1e06f55b6..166a0b4fc8 100644 --- a/spec/ruby/core/env/each_spec.rb +++ b/spec/ruby/core/env/each_spec.rb @@ -1,6 +1,7 @@ require_relative 'spec_helper' -require_relative 'shared/each' describe "ENV.each" do - it_behaves_like :env_each, :each + it "is an alias of ENV.each_pair" do + ENV.method(:each).should == ENV.method(:each_pair) + end end diff --git a/spec/ruby/core/env/element_set_spec.rb b/spec/ruby/core/env/element_set_spec.rb index 26dfee1ade..ca5d468009 100644 --- a/spec/ruby/core/env/element_set_spec.rb +++ b/spec/ruby/core/env/element_set_spec.rb @@ -1,6 +1,62 @@ require_relative '../../spec_helper' -require_relative 'shared/store' describe "ENV.[]=" do - it_behaves_like :env_store, :[]= + before :each do + @saved_foo = ENV["foo"] + end + + after :each do + ENV["foo"] = @saved_foo + end + + it "sets the environment variable to the given value" do + ENV["foo"] = "bar" + ENV["foo"].should == "bar" + end + + it "returns the value" do + value = "bar" + ENV.send(:[]=, "foo", value).should.equal?(value) + end + + it "deletes the environment variable when the value is nil" do + ENV["foo"] = "bar" + ENV["foo"] = nil + ENV.key?("foo").should == false + end + + it "coerces the key argument with #to_str" do + k = mock("key") + k.should_receive(:to_str).and_return("foo") + ENV[k] = "bar" + ENV["foo"].should == "bar" + end + + it "coerces the value argument with #to_str" do + v = mock("value") + v.should_receive(:to_str).and_return("bar") + ENV["foo"] = v + ENV["foo"].should == "bar" + end + + it "raises TypeError when the key is not coercible to String" do + -> { ENV[Object.new] = "bar" }.should.raise(TypeError, "no implicit conversion of Object into String") + end + + it "raises TypeError when the value is not coercible to String" do + -> { ENV["foo"] = Object.new }.should.raise(TypeError, "no implicit conversion of Object into String") + end + + it "raises Errno::EINVAL when the key contains the '=' character" do + -> { ENV["foo="] = "bar" }.should.raise(Errno::EINVAL) + end + + it "raises Errno::EINVAL when the key is an empty string" do + -> { ENV[""] = "bar" }.should.raise(Errno::EINVAL) + end + + it "does nothing when the key is not a valid environment variable key and the value is nil" do + ENV["foo="] = nil + ENV.key?("foo=").should == false + end end diff --git a/spec/ruby/core/env/filter_spec.rb b/spec/ruby/core/env/filter_spec.rb index 52f8b79a0b..54997bfda1 100644 --- a/spec/ruby/core/env/filter_spec.rb +++ b/spec/ruby/core/env/filter_spec.rb @@ -1,13 +1,13 @@ require_relative '../../spec_helper' -require_relative '../enumerable/shared/enumeratorized' -require_relative 'shared/select' describe "ENV.filter!" do - it_behaves_like :env_select!, :filter! - it_behaves_like :enumeratorized_with_origin_size, :filter!, ENV + it "is an alias of ENV.select!" do + ENV.method(:filter!).should == ENV.method(:select!) + end end describe "ENV.filter" do - it_behaves_like :env_select, :filter - it_behaves_like :enumeratorized_with_origin_size, :filter, ENV + it "is an alias of ENV.select" do + ENV.method(:filter).should == ENV.method(:select) + end end diff --git a/spec/ruby/core/env/has_key_spec.rb b/spec/ruby/core/env/has_key_spec.rb index 798668105d..7feeec8dfa 100644 --- a/spec/ruby/core/env/has_key_spec.rb +++ b/spec/ruby/core/env/has_key_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "ENV.has_key?" do - it_behaves_like :env_include, :has_key? + it "is an alias of ENV.include?" do + ENV.method(:has_key?).should == ENV.method(:include?) + end end diff --git a/spec/ruby/core/env/has_value_spec.rb b/spec/ruby/core/env/has_value_spec.rb index a2bf3eb877..b76ec0dc5d 100644 --- a/spec/ruby/core/env/has_value_spec.rb +++ b/spec/ruby/core/env/has_value_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/value' describe "ENV.has_value?" do - it_behaves_like :env_value, :has_value? + it "is an alias of ENV.value?" do + ENV.method(:has_value?).should == ENV.method(:value?) + end end diff --git a/spec/ruby/core/env/include_spec.rb b/spec/ruby/core/env/include_spec.rb index 3975f095ac..8e68aa3bfd 100644 --- a/spec/ruby/core/env/include_spec.rb +++ b/spec/ruby/core/env/include_spec.rb @@ -1,6 +1,32 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "ENV.include?" do - it_behaves_like :env_include, :include? + before :each do + @saved_foo = ENV["foo"] + end + + after :each do + ENV["foo"] = @saved_foo + end + + it "returns true if ENV has the key" do + ENV["foo"] = "bar" + ENV.include?( "foo").should == true + end + + it "returns false if ENV doesn't include the key" do + ENV.delete("foo") + ENV.include?( "foo").should == false + end + + it "coerces the key with #to_str" do + ENV["foo"] = "bar" + k = mock('key') + k.should_receive(:to_str).and_return("foo") + ENV.include?( k).should == true + end + + it "raises TypeError if the argument is not a String and does not respond to #to_str" do + -> { ENV.include?( Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String") + end end diff --git a/spec/ruby/core/env/key_spec.rb b/spec/ruby/core/env/key_spec.rb index 677cf35216..56f5f609a7 100644 --- a/spec/ruby/core/env/key_spec.rb +++ b/spec/ruby/core/env/key_spec.rb @@ -1,8 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "ENV.key?" do - it_behaves_like :env_include, :key? + it "is an alias of ENV.include?" do + ENV.method(:key?).should == ENV.method(:include?) + end end describe "ENV.key" do diff --git a/spec/ruby/core/env/length_spec.rb b/spec/ruby/core/env/length_spec.rb index c6f9062892..6baac6f7a4 100644 --- a/spec/ruby/core/env/length_spec.rb +++ b/spec/ruby/core/env/length_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "ENV.length" do - it_behaves_like :env_length, :length + it "is an alias of ENV.size" do + ENV.method(:length).should == ENV.method(:size) + end end diff --git a/spec/ruby/core/env/member_spec.rb b/spec/ruby/core/env/member_spec.rb index 9119022ae5..f062d41190 100644 --- a/spec/ruby/core/env/member_spec.rb +++ b/spec/ruby/core/env/member_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "ENV.member?" do - it_behaves_like :env_include, :member? + it "is an alias of ENV.include?" do + ENV.method(:member?).should == ENV.method(:include?) + end end diff --git a/spec/ruby/core/env/merge_spec.rb b/spec/ruby/core/env/merge_spec.rb index f10662cf79..78231afbb2 100644 --- a/spec/ruby/core/env/merge_spec.rb +++ b/spec/ruby/core/env/merge_spec.rb @@ -1,6 +1,106 @@ require_relative '../../spec_helper' -require_relative 'shared/update' describe "ENV.merge!" do - it_behaves_like :env_update, :merge! + before :each do + @saved_foo = ENV["foo"] + @saved_bar = ENV["bar"] + end + + after :each do + ENV["foo"] = @saved_foo + ENV["bar"] = @saved_bar + end + + it "adds the parameter hash to ENV, returning ENV" do + ENV.merge!("foo" => "0", "bar" => "1").should.equal?(ENV) + ENV["foo"].should == "0" + ENV["bar"].should == "1" + end + + it "adds the multiple parameter hashes to ENV, returning ENV" do + ENV.merge!({"foo" => "multi1"}, {"bar" => "multi2"}).should.equal?(ENV) + ENV["foo"].should == "multi1" + ENV["bar"].should == "multi2" + end + + it "returns ENV when no block given" do + ENV.merge!({"foo" => "0", "bar" => "1"}).should.equal?(ENV) + end + + it "yields key, the old value and the new value when replacing an entry" do + ENV.merge!({"foo" => "0", "bar" => "3"}) + a = [] + ENV.merge!({"foo" => "1", "bar" => "4"}) do |key, old, new| + a << [key, old, new] + new + end + a[0].should == ["foo", "0", "1"] + a[1].should == ["bar", "3", "4"] + end + + it "yields key, the old value and the new value when replacing an entry" do + ENV.merge!({"foo" => "0", "bar" => "3"}) + ENV.merge!({"foo" => "1", "bar" => "4"}) do |key, old, new| + (new.to_i + 1).to_s + end + ENV["foo"].should == "2" + ENV["bar"].should == "5" + end + + # BUG: https://bugs.ruby-lang.org/issues/16192 + it "does not evaluate the block when the name is new" do + ENV.delete("bar") + ENV.merge!({"foo" => "0"}) + ENV.merge!("bar" => "1") { |key, old, new| fail "Should not get here" } + ENV["bar"].should == "1" + end + + # BUG: https://bugs.ruby-lang.org/issues/16192 + it "does not use the block's return value as the value when the name is new" do + ENV.delete("bar") + ENV.merge!({"foo" => "0"}) + ENV.merge!("bar" => "1") { |key, old, new| "Should not use this value" } + ENV["foo"].should == "0" + ENV["bar"].should == "1" + end + + it "returns ENV when block given" do + ENV.merge!({"foo" => "0", "bar" => "1"}){}.should.equal?(ENV) + end + + it "raises TypeError when a name is not coercible to String" do + -> { ENV.merge!(Object.new => "0") }.should.raise(TypeError, "no implicit conversion of Object into String") + end + + it "raises TypeError when a value is not coercible to String" do + -> { ENV.merge!("foo" => Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String") + end + + it "raises Errno::EINVAL when a name contains the '=' character" do + -> { ENV.merge!("foo=" => "bar") }.should.raise(Errno::EINVAL) + end + + it "raises Errno::EINVAL when a name is an empty string" do + -> { ENV.merge!("" => "bar") }.should.raise(Errno::EINVAL) + end + + it "updates good data preceding an error" do + ENV["foo"] = "0" + begin + ENV.merge!({"foo" => "2", Object.new => "1"}) + rescue TypeError + ensure + ENV["foo"].should == "2" + end + end + + it "does not update good data following an error" do + ENV["foo"] = "0" + begin + ENV.merge!({Object.new => "1", "foo" => "2"}) + rescue TypeError + ensure + ENV["foo"].should == "0" + end + end end diff --git a/spec/ruby/core/env/select_spec.rb b/spec/ruby/core/env/select_spec.rb index c3a76f4434..2b92d61551 100644 --- a/spec/ruby/core/env/select_spec.rb +++ b/spec/ruby/core/env/select_spec.rb @@ -1,13 +1,68 @@ require_relative '../../spec_helper' require_relative '../enumerable/shared/enumeratorized' -require_relative 'shared/select' describe "ENV.select!" do - it_behaves_like :env_select!, :select! it_behaves_like :enumeratorized_with_origin_size, :select!, ENV + + before :each do + @saved_foo = ENV["foo"] + end + + after :each do + ENV["foo"] = @saved_foo + end + + it "removes environment variables for which the block returns false" do + ENV["foo"] = "bar" + ENV.select! { |k, v| k != "foo" } + ENV["foo"].should == nil + end + + it "returns self if any changes were made" do + ENV["foo"] = "bar" + (ENV.select! { |k, v| k != "foo" }).should == ENV + end + + it "returns nil if no changes were made" do + (ENV.select! { true }).should == nil + end + + it "returns an Enumerator if called without a block" do + ENV.select!.should.instance_of?(Enumerator) + end + + it "selects via the enumerator" do + enum = ENV.select! + ENV["foo"] = "bar" + enum.each { |k, v| k != "foo" } + ENV["foo"].should == nil + end end describe "ENV.select" do - it_behaves_like :env_select, :select it_behaves_like :enumeratorized_with_origin_size, :select, ENV + + before :each do + @saved_foo = ENV["foo"] + end + + after :each do + ENV["foo"] = @saved_foo + end + + it "returns a Hash of names and values for which block returns true" do + ENV["foo"] = "bar" + (ENV.select { |k, v| k == "foo" }).should == { "foo" => "bar" } + end + + it "returns an Enumerator when no block is given" do + enum = ENV.select + enum.should.instance_of?(Enumerator) + end + + it "selects via the enumerator" do + enum = ENV.select + ENV["foo"] = "bar" + enum.each { |k, v| k == "foo" }.should == { "foo" => "bar"} + end end diff --git a/spec/ruby/core/env/shared/each.rb b/spec/ruby/core/env/shared/each.rb deleted file mode 100644 index 0661ca924c..0000000000 --- a/spec/ruby/core/env/shared/each.rb +++ /dev/null @@ -1,65 +0,0 @@ -require_relative '../../enumerable/shared/enumeratorized' - -describe :env_each, shared: true do - it "returns each pair" do - orig = ENV.to_hash - e = [] - begin - ENV.clear - ENV["foo"] = "bar" - ENV["baz"] = "boo" - ENV.send(@method) { |k, v| e << [k, v] }.should.equal?(ENV) - e.should.include?(["foo", "bar"]) - e.should.include?(["baz", "boo"]) - ensure - ENV.replace orig - end - end - - it "returns an Enumerator if called without a block" do - enum = ENV.send(@method) - enum.should.instance_of?(Enumerator) - enum.each do |name, value| - ENV[name].should == value - end - end - - before :all do - @object = ENV - end - it_should_behave_like :enumeratorized_with_origin_size - - describe "with encoding" do - before :each do - @external = Encoding.default_external - @internal = Encoding.default_internal - - Encoding.default_external = Encoding::BINARY - end - - after :each do - Encoding.default_external = @external - Encoding.default_internal = @internal - end - - it "uses the locale encoding when Encoding.default_internal is nil" do - Encoding.default_internal = nil - - ENV.send(@method) do |key, value| - key.should.be_locale_env - value.should.be_locale_env - end - end - - it "transcodes from the locale encoding to Encoding.default_internal if set" do - Encoding.default_internal = internal = Encoding::IBM437 - - ENV.send(@method) do |key, value| - key.encoding.should.equal?(internal) - if value.ascii_only? - value.encoding.should.equal?(internal) - end - end - end - end -end diff --git a/spec/ruby/core/env/shared/include.rb b/spec/ruby/core/env/shared/include.rb deleted file mode 100644 index ceca02e3eb..0000000000 --- a/spec/ruby/core/env/shared/include.rb +++ /dev/null @@ -1,30 +0,0 @@ -describe :env_include, shared: true do - before :each do - @saved_foo = ENV["foo"] - end - - after :each do - ENV["foo"] = @saved_foo - end - - it "returns true if ENV has the key" do - ENV["foo"] = "bar" - ENV.send(@method, "foo").should == true - end - - it "returns false if ENV doesn't include the key" do - ENV.delete("foo") - ENV.send(@method, "foo").should == false - end - - it "coerces the key with #to_str" do - ENV["foo"] = "bar" - k = mock('key') - k.should_receive(:to_str).and_return("foo") - ENV.send(@method, k).should == true - end - - it "raises TypeError if the argument is not a String and does not respond to #to_str" do - -> { ENV.send(@method, Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String") - end -end diff --git a/spec/ruby/core/env/shared/length.rb b/spec/ruby/core/env/shared/length.rb deleted file mode 100644 index 6d788a3f4a..0000000000 --- a/spec/ruby/core/env/shared/length.rb +++ /dev/null @@ -1,13 +0,0 @@ -describe :env_length, shared: true do - it "returns the number of ENV entries" do - orig = ENV.to_hash - begin - ENV.clear - ENV["foo"] = "bar" - ENV["baz"] = "boo" - ENV.send(@method).should == 2 - ensure - ENV.replace orig - end - end -end diff --git a/spec/ruby/core/env/shared/select.rb b/spec/ruby/core/env/shared/select.rb deleted file mode 100644 index 8ec648a637..0000000000 --- a/spec/ruby/core/env/shared/select.rb +++ /dev/null @@ -1,61 +0,0 @@ -describe :env_select, shared: true do - before :each do - @saved_foo = ENV["foo"] - end - - after :each do - ENV["foo"] = @saved_foo - end - - it "returns a Hash of names and values for which block return true" do - ENV["foo"] = "bar" - (ENV.send(@method) { |k, v| k == "foo" }).should == { "foo" => "bar" } - end - - it "returns an Enumerator when no block is given" do - enum = ENV.send(@method) - enum.should.instance_of?(Enumerator) - end - - it "selects via the enumerator" do - enum = ENV.send(@method) - ENV["foo"] = "bar" - enum.each { |k, v| k == "foo" }.should == { "foo" => "bar"} - end -end - -describe :env_select!, shared: true do - before :each do - @saved_foo = ENV["foo"] - end - - after :each do - ENV["foo"] = @saved_foo - end - - it "removes environment variables for which the block returns true" do - ENV["foo"] = "bar" - ENV.send(@method) { |k, v| k != "foo" } - ENV["foo"].should == nil - end - - it "returns self if any changes were made" do - ENV["foo"] = "bar" - (ENV.send(@method) { |k, v| k != "foo" }).should == ENV - end - - it "returns nil if no changes were made" do - (ENV.send(@method) { true }).should == nil - end - - it "returns an Enumerator if called without a block" do - ENV.send(@method).should.instance_of?(Enumerator) - end - - it "selects via the enumerator" do - enum = ENV.send(@method) - ENV["foo"] = "bar" - enum.each { |k, v| k != "foo" } - ENV["foo"].should == nil - end -end diff --git a/spec/ruby/core/env/shared/store.rb b/spec/ruby/core/env/shared/store.rb deleted file mode 100644 index 388208a8ac..0000000000 --- a/spec/ruby/core/env/shared/store.rb +++ /dev/null @@ -1,60 +0,0 @@ -describe :env_store, shared: true do - before :each do - @saved_foo = ENV["foo"] - end - - after :each do - ENV["foo"] = @saved_foo - end - - it "sets the environment variable to the given value" do - ENV.send(@method, "foo", "bar") - ENV["foo"].should == "bar" - end - - it "returns the value" do - value = "bar" - ENV.send(@method, "foo", value).should.equal?(value) - end - - it "deletes the environment variable when the value is nil" do - ENV["foo"] = "bar" - ENV.send(@method, "foo", nil) - ENV.key?("foo").should == false - end - - it "coerces the key argument with #to_str" do - k = mock("key") - k.should_receive(:to_str).and_return("foo") - ENV.send(@method, k, "bar") - ENV["foo"].should == "bar" - end - - it "coerces the value argument with #to_str" do - v = mock("value") - v.should_receive(:to_str).and_return("bar") - ENV.send(@method, "foo", v) - ENV["foo"].should == "bar" - end - - it "raises TypeError when the key is not coercible to String" do - -> { ENV.send(@method, Object.new, "bar") }.should.raise(TypeError, "no implicit conversion of Object into String") - end - - it "raises TypeError when the value is not coercible to String" do - -> { ENV.send(@method, "foo", Object.new) }.should.raise(TypeError, "no implicit conversion of Object into String") - end - - it "raises Errno::EINVAL when the key contains the '=' character" do - -> { ENV.send(@method, "foo=", "bar") }.should.raise(Errno::EINVAL) - end - - it "raises Errno::EINVAL when the key is an empty string" do - -> { ENV.send(@method, "", "bar") }.should.raise(Errno::EINVAL) - end - - it "does nothing when the key is not a valid environment variable key and the value is nil" do - ENV.send(@method, "foo=", nil) - ENV.key?("foo=").should == false - end -end diff --git a/spec/ruby/core/env/shared/update.rb b/spec/ruby/core/env/shared/update.rb deleted file mode 100644 index 112cc2505d..0000000000 --- a/spec/ruby/core/env/shared/update.rb +++ /dev/null @@ -1,104 +0,0 @@ -describe :env_update, shared: true do - before :each do - @saved_foo = ENV["foo"] - @saved_bar = ENV["bar"] - end - - after :each do - ENV["foo"] = @saved_foo - ENV["bar"] = @saved_bar - end - - it "adds the parameter hash to ENV, returning ENV" do - ENV.send(@method, "foo" => "0", "bar" => "1").should.equal?(ENV) - ENV["foo"].should == "0" - ENV["bar"].should == "1" - end - - it "adds the multiple parameter hashes to ENV, returning ENV" do - ENV.send(@method, {"foo" => "multi1"}, {"bar" => "multi2"}).should.equal?(ENV) - ENV["foo"].should == "multi1" - ENV["bar"].should == "multi2" - end - - it "returns ENV when no block given" do - ENV.send(@method, {"foo" => "0", "bar" => "1"}).should.equal?(ENV) - end - - it "yields key, the old value and the new value when replacing an entry" do - ENV.send @method, {"foo" => "0", "bar" => "3"} - a = [] - ENV.send @method, {"foo" => "1", "bar" => "4"} do |key, old, new| - a << [key, old, new] - new - end - a[0].should == ["foo", "0", "1"] - a[1].should == ["bar", "3", "4"] - end - - it "yields key, the old value and the new value when replacing an entry" do - ENV.send @method, {"foo" => "0", "bar" => "3"} - ENV.send @method, {"foo" => "1", "bar" => "4"} do |key, old, new| - (new.to_i + 1).to_s - end - ENV["foo"].should == "2" - ENV["bar"].should == "5" - end - - # BUG: https://bugs.ruby-lang.org/issues/16192 - it "does not evaluate the block when the name is new" do - ENV.delete("bar") - ENV.send @method, {"foo" => "0"} - ENV.send(@method, "bar" => "1") { |key, old, new| fail "Should not get here" } - ENV["bar"].should == "1" - end - - # BUG: https://bugs.ruby-lang.org/issues/16192 - it "does not use the block's return value as the value when the name is new" do - ENV.delete("bar") - ENV.send @method, {"foo" => "0"} - ENV.send(@method, "bar" => "1") { |key, old, new| "Should not use this value" } - ENV["foo"].should == "0" - ENV["bar"].should == "1" - end - - it "returns ENV when block given" do - ENV.send(@method, {"foo" => "0", "bar" => "1"}){}.should.equal?(ENV) - end - - it "raises TypeError when a name is not coercible to String" do - -> { ENV.send @method, Object.new => "0" }.should.raise(TypeError, "no implicit conversion of Object into String") - end - - it "raises TypeError when a value is not coercible to String" do - -> { ENV.send @method, "foo" => Object.new }.should.raise(TypeError, "no implicit conversion of Object into String") - end - - it "raises Errno::EINVAL when a name contains the '=' character" do - -> { ENV.send(@method, "foo=" => "bar") }.should.raise(Errno::EINVAL) - end - - it "raises Errno::EINVAL when a name is an empty string" do - -> { ENV.send(@method, "" => "bar") }.should.raise(Errno::EINVAL) - end - - it "updates good data preceding an error" do - ENV["foo"] = "0" - begin - ENV.send @method, {"foo" => "2", Object.new => "1"} - rescue TypeError - ensure - ENV["foo"].should == "2" - end - end - - it "does not update good data following an error" do - ENV["foo"] = "0" - begin - ENV.send @method, {Object.new => "1", "foo" => "2"} - rescue TypeError - ensure - ENV["foo"].should == "0" - end - end -end diff --git a/spec/ruby/core/env/shared/value.rb b/spec/ruby/core/env/shared/value.rb deleted file mode 100644 index c2b5025465..0000000000 --- a/spec/ruby/core/env/shared/value.rb +++ /dev/null @@ -1,29 +0,0 @@ -describe :env_value, shared: true do - before :each do - @saved_foo = ENV["foo"] - end - - after :each do - ENV["foo"] = @saved_foo - end - - it "returns true if ENV has the value" do - ENV["foo"] = "bar" - ENV.send(@method, "bar").should == true - end - - it "returns false if ENV doesn't have the value" do - ENV.send(@method, "foo").should == false - end - - it "coerces the value element with #to_str" do - ENV["foo"] = "bar" - v = mock('value') - v.should_receive(:to_str).and_return("bar") - ENV.send(@method, v).should == true - end - - it "returns nil if the argument is not a String and does not respond to #to_str" do - ENV.send(@method, Object.new).should == nil - end -end diff --git a/spec/ruby/core/env/size_spec.rb b/spec/ruby/core/env/size_spec.rb index 7c8072481e..9c6de20df6 100644 --- a/spec/ruby/core/env/size_spec.rb +++ b/spec/ruby/core/env/size_spec.rb @@ -1,6 +1,15 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "ENV.size" do - it_behaves_like :env_length, :size + it "returns the number of ENV entries" do + orig = ENV.to_hash + begin + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "boo" + ENV.size.should == 2 + ensure + ENV.replace orig + end + end end diff --git a/spec/ruby/core/env/store_spec.rb b/spec/ruby/core/env/store_spec.rb index b4700e0a96..a5fd7e1e26 100644 --- a/spec/ruby/core/env/store_spec.rb +++ b/spec/ruby/core/env/store_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/store' describe "ENV.store" do - it_behaves_like :env_store, :store + it "is an alias of ENV.[]=" do + ENV.method(:store).should == ENV.method(:[]=) + end end diff --git a/spec/ruby/core/env/update_spec.rb b/spec/ruby/core/env/update_spec.rb index 95a8a2eb49..44d05d617f 100644 --- a/spec/ruby/core/env/update_spec.rb +++ b/spec/ruby/core/env/update_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/update' describe "ENV.update" do - it_behaves_like :env_update, :update + it "is an alias of ENV.merge!" do + ENV.method(:update).should == ENV.method(:merge!) + end end diff --git a/spec/ruby/core/env/value_spec.rb b/spec/ruby/core/env/value_spec.rb index 906e86ab39..c732cfbd15 100644 --- a/spec/ruby/core/env/value_spec.rb +++ b/spec/ruby/core/env/value_spec.rb @@ -1,6 +1,31 @@ require_relative '../../spec_helper' -require_relative 'shared/value' describe "ENV.value?" do - it_behaves_like :env_value, :value? + before :each do + @saved_foo = ENV["foo"] + end + + after :each do + ENV["foo"] = @saved_foo + end + + it "returns true if ENV has the value" do + ENV["foo"] = "bar" + ENV.value?("bar").should == true + end + + it "returns false if ENV doesn't have the value" do + ENV.value?("foo").should == false + end + + it "coerces the value element with #to_str" do + ENV["foo"] = "bar" + v = mock('value') + v.should_receive(:to_str).and_return("bar") + ENV.value?(v).should == true + end + + it "returns nil if the argument is not a String and does not respond to #to_str" do + ENV.value?(Object.new).should == nil + end end diff --git a/spec/ruby/core/false/inspect_spec.rb b/spec/ruby/core/false/inspect_spec.rb index 4cbb55d434..70f4aa0159 100644 --- a/spec/ruby/core/false/inspect_spec.rb +++ b/spec/ruby/core/false/inspect_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' describe "FalseClass#inspect" do - it "returns the string 'false'" do - false.inspect.should == "false" + it "is an alias of FalseClass#to_s" do + false.method(:inspect).should == false.method(:to_s) end end diff --git a/spec/ruby/core/false/xor_spec.rb b/spec/ruby/core/false/xor_spec.rb index 1b87b9f412..d8432ca326 100644 --- a/spec/ruby/core/false/xor_spec.rb +++ b/spec/ruby/core/false/xor_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' describe "FalseClass#^" do - it "returns false if other is nil or false, otherwise true" do - (false ^ false).should == false - (false ^ true).should == true - (false ^ nil).should == false - (false ^ "").should == true - (false ^ mock('x')).should == true + it "is an alias of FalseClass#|" do + false.method(:^).should == false.method(:|) end end diff --git a/spec/ruby/core/fiber/scheduler_spec.rb b/spec/ruby/core/fiber/scheduler_spec.rb index 15a03c1479..2a517ba93b 100644 --- a/spec/ruby/core/fiber/scheduler_spec.rb +++ b/spec/ruby/core/fiber/scheduler_spec.rb @@ -1,8 +1,5 @@ require_relative '../../spec_helper' -require_relative 'shared/scheduler' - -require "fiber" describe "Fiber.scheduler" do - it_behaves_like :scheduler, :scheduler + it "is already tested in Fiber.set_scheduler" end diff --git a/spec/ruby/core/fiber/set_scheduler_spec.rb b/spec/ruby/core/fiber/set_scheduler_spec.rb index 82f6acbe86..b34aff8734 100644 --- a/spec/ruby/core/fiber/set_scheduler_spec.rb +++ b/spec/ruby/core/fiber/set_scheduler_spec.rb @@ -1,8 +1,55 @@ require_relative '../../spec_helper' -require_relative 'shared/scheduler' require "fiber" describe "Fiber.scheduler" do - it_behaves_like :scheduler, :set_scheduler + it "validates the scheduler for required methods" do + required_methods = [:block, :unblock, :kernel_sleep, :io_wait] + required_methods.each do |missing_method| + scheduler = Object.new + required_methods.difference([missing_method]).each do |method| + scheduler.define_singleton_method(method) {} + end + -> { + suppress_warning { Fiber.set_scheduler(scheduler) } + }.should.raise(ArgumentError, /Scheduler must implement ##{missing_method}/) + end + end + + it "can set and get the scheduler" do + required_methods = [:block, :unblock, :kernel_sleep, :io_wait] + scheduler = Object.new + required_methods.each do |method| + scheduler.define_singleton_method(method) {} + end + suppress_warning { Fiber.set_scheduler(scheduler) } + Fiber.scheduler.should == scheduler + end + + it "returns the scheduler after setting it" do + required_methods = [:block, :unblock, :kernel_sleep, :io_wait] + scheduler = Object.new + required_methods.each do |method| + scheduler.define_singleton_method(method) {} + end + result = suppress_warning { Fiber.set_scheduler(scheduler) } + result.should == scheduler + end + + it "can remove the scheduler" do + required_methods = [:block, :unblock, :kernel_sleep, :io_wait] + scheduler = Object.new + required_methods.each do |method| + scheduler.define_singleton_method(method) {} + end + suppress_warning { Fiber.set_scheduler(scheduler) } + Fiber.set_scheduler(nil) + Fiber.scheduler.should == nil + end + + it "can assign a nil scheduler multiple times" do + Fiber.set_scheduler(nil) + Fiber.set_scheduler(nil) + Fiber.scheduler.should == nil + end end diff --git a/spec/ruby/core/fiber/shared/scheduler.rb b/spec/ruby/core/fiber/shared/scheduler.rb deleted file mode 100644 index 04bdded53a..0000000000 --- a/spec/ruby/core/fiber/shared/scheduler.rb +++ /dev/null @@ -1,51 +0,0 @@ -describe :scheduler, shared: true do - it "validates the scheduler for required methods" do - required_methods = [:block, :unblock, :kernel_sleep, :io_wait] - required_methods.each do |missing_method| - scheduler = Object.new - required_methods.difference([missing_method]).each do |method| - scheduler.define_singleton_method(method) {} - end - -> { - suppress_warning { Fiber.set_scheduler(scheduler) } - }.should.raise(ArgumentError, /Scheduler must implement ##{missing_method}/) - end - end - - it "can set and get the scheduler" do - required_methods = [:block, :unblock, :kernel_sleep, :io_wait] - scheduler = Object.new - required_methods.each do |method| - scheduler.define_singleton_method(method) {} - end - suppress_warning { Fiber.set_scheduler(scheduler) } - Fiber.scheduler.should == scheduler - end - - it "returns the scheduler after setting it" do - required_methods = [:block, :unblock, :kernel_sleep, :io_wait] - scheduler = Object.new - required_methods.each do |method| - scheduler.define_singleton_method(method) {} - end - result = suppress_warning { Fiber.set_scheduler(scheduler) } - result.should == scheduler - end - - it "can remove the scheduler" do - required_methods = [:block, :unblock, :kernel_sleep, :io_wait] - scheduler = Object.new - required_methods.each do |method| - scheduler.define_singleton_method(method) {} - end - suppress_warning { Fiber.set_scheduler(scheduler) } - Fiber.set_scheduler(nil) - Fiber.scheduler.should == nil - end - - it "can assign a nil scheduler multiple times" do - Fiber.set_scheduler(nil) - Fiber.set_scheduler(nil) - Fiber.scheduler.should == nil - end -end diff --git a/spec/ruby/core/file/delete_spec.rb b/spec/ruby/core/file/delete_spec.rb index 4098499942..7149b8a37d 100644 --- a/spec/ruby/core/file/delete_spec.rb +++ b/spec/ruby/core/file/delete_spec.rb @@ -1,6 +1,63 @@ require_relative '../../spec_helper' -require_relative 'shared/unlink' describe "File.delete" do - it_behaves_like :file_unlink, :delete + before :each do + @file1 = tmp('test.txt') + @file2 = tmp('test2.txt') + + touch @file1 + touch @file2 + end + + after :each do + File.delete(@file1) if File.exist?(@file1) + File.delete(@file2) if File.exist?(@file2) + + @file1 = nil + @file2 = nil + end + + it "returns 0 when called without arguments" do + File.delete.should == 0 + end + + it "deletes a single file" do + File.delete(@file1).should == 1 + File.should_not.exist?(@file1) + end + + it "deletes multiple files" do + File.delete(@file1, @file2).should == 2 + File.should_not.exist?(@file1) + File.should_not.exist?(@file2) + end + + it "raises a TypeError if not passed a String type" do + -> { File.delete(1) }.should.raise(TypeError) + end + + it "raises an Errno::ENOENT when the given file doesn't exist" do + -> { File.delete('bogus') }.should.raise(Errno::ENOENT) + end + + it "coerces a given parameter into a string if possible" do + mock = mock("to_str") + mock.should_receive(:to_str).and_return(@file1) + File.delete(mock).should == 1 + end + + it "accepts an object that has a #to_path method" do + File.delete(mock_to_path(@file1)).should == 1 + end + + platform_is :windows do + it "allows deleting an open file with File::SHARE_DELETE" do + path = tmp("share_delete.txt") + File.open(path, mode: File::CREAT | File::WRONLY | File::BINARY | File::SHARE_DELETE) do |f| + File.should.exist?(path) + File.delete(path) + end + File.should_not.exist?(path) + end + end end diff --git a/spec/ruby/core/file/fnmatch_spec.rb b/spec/ruby/core/file/fnmatch_spec.rb index a1b7fa12b3..44a143bddc 100644 --- a/spec/ruby/core/file/fnmatch_spec.rb +++ b/spec/ruby/core/file/fnmatch_spec.rb @@ -1,10 +1,302 @@ require_relative '../../spec_helper' -require_relative 'shared/fnmatch' describe "File.fnmatch" do - it_behaves_like :file_fnmatch, :fnmatch + it "matches entire strings" do + File.fnmatch('cat', 'cat').should == true + end + + it "does not match partial strings" do + File.fnmatch('cat', 'category').should == false + end + + it "does not support { } patterns by default" do + File.fnmatch('c{at,ub}s', 'cats').should == false + File.fnmatch('c{at,ub}s', 'c{at,ub}s').should == true + end + + it "supports some { } patterns when File::FNM_EXTGLOB is passed" do + File.fnmatch("{a,b}", "a", File::FNM_EXTGLOB).should == true + File.fnmatch("{a,b}", "b", File::FNM_EXTGLOB).should == true + File.fnmatch("c{at,ub}s", "cats", File::FNM_EXTGLOB).should == true + File.fnmatch("c{at,ub}s", "cubs", File::FNM_EXTGLOB).should == true + File.fnmatch("-c{at,ub}s-", "-cats-", File::FNM_EXTGLOB).should == true + File.fnmatch("-c{at,ub}s-", "-cubs-", File::FNM_EXTGLOB).should == true + File.fnmatch("{a,b,c}{d,e,f}{g,h}", "adg", File::FNM_EXTGLOB).should == true + File.fnmatch("{a,b,c}{d,e,f}{g,h}", "bdg", File::FNM_EXTGLOB).should == true + File.fnmatch("{a,b,c}{d,e,f}{g,h}", "ceh", File::FNM_EXTGLOB).should == true + File.fnmatch("{aa,bb,cc,dd}", "aa", File::FNM_EXTGLOB).should == true + File.fnmatch("{aa,bb,cc,dd}", "bb", File::FNM_EXTGLOB).should == true + File.fnmatch("{aa,bb,cc,dd}", "cc", File::FNM_EXTGLOB).should == true + File.fnmatch("{aa,bb,cc,dd}", "dd", File::FNM_EXTGLOB).should == true + File.fnmatch("{1,5{a,b{c,d}}}", "1", File::FNM_EXTGLOB).should == true + File.fnmatch("{1,5{a,b{c,d}}}", "5a", File::FNM_EXTGLOB).should == true + File.fnmatch("{1,5{a,b{c,d}}}", "5bc", File::FNM_EXTGLOB).should == true + File.fnmatch("{1,5{a,b{c,d}}}", "5bd", File::FNM_EXTGLOB).should == true + File.fnmatch("\\\\{a\\,b,b\\}c}", "\\a,b", File::FNM_EXTGLOB).should == true + File.fnmatch("\\\\{a\\,b,b\\}c}", "\\b}c", File::FNM_EXTGLOB).should == true + end + + it "doesn't support some { } patterns even when File::FNM_EXTGLOB is passed" do + File.fnmatch("a{0..3}b", "a0b", File::FNM_EXTGLOB).should == false + File.fnmatch("a{0..3}b", "a1b", File::FNM_EXTGLOB).should == false + File.fnmatch("a{0..3}b", "a2b", File::FNM_EXTGLOB).should == false + File.fnmatch("a{0..3}b", "a3b", File::FNM_EXTGLOB).should == false + File.fnmatch("{0..12}", "0", File::FNM_EXTGLOB).should == false + File.fnmatch("{0..12}", "6", File::FNM_EXTGLOB).should == false + File.fnmatch("{0..12}", "12", File::FNM_EXTGLOB).should == false + File.fnmatch("{3..-2}", "3", File::FNM_EXTGLOB).should == false + File.fnmatch("{3..-2}", "0", File::FNM_EXTGLOB).should == false + File.fnmatch("{3..-2}", "-2", File::FNM_EXTGLOB).should == false + File.fnmatch("{a..g}", "a", File::FNM_EXTGLOB).should == false + File.fnmatch("{a..g}", "d", File::FNM_EXTGLOB).should == false + File.fnmatch("{a..g}", "g", File::FNM_EXTGLOB).should == false + File.fnmatch("{g..a}", "a", File::FNM_EXTGLOB).should == false + File.fnmatch("{g..a}", "d", File::FNM_EXTGLOB).should == false + File.fnmatch("{g..a}", "g", File::FNM_EXTGLOB).should == false + File.fnmatch("escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false + File.fnmatch("escaping: {{,\\,,\\},\\{}", "escaping: ,", File::FNM_EXTGLOB).should == false + File.fnmatch("escaping: {{,\\,,\\},\\{}", "escaping: }", File::FNM_EXTGLOB).should == false + File.fnmatch("escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false + end + + it "doesn't match an extra } when File::FNM_EXTGLOB is passed" do + File.fnmatch('c{at,ub}}s', 'cats', File::FNM_EXTGLOB).should == false + end + + it "matches when both FNM_EXTGLOB and FNM_PATHNAME are passed" do + File.fnmatch("?.md", "a.md", File::FNM_EXTGLOB | File::FNM_PATHNAME).should == true + end + + it "matches a single character for each ? character" do + File.fnmatch('c?t', 'cat').should == true + File.fnmatch('c??t', 'cat').should == false + end + + it "matches zero or more characters for each * character" do + File.fnmatch('c*', 'cats').should == true + File.fnmatch('c*t', 'c/a/b/t').should == true + end + + it "does not match unterminated range of characters" do + File.fnmatch('abc[de', 'abcd').should == false + end + + it "does not match unterminated range of characters as a literal" do + File.fnmatch('abc[de', 'abc[de').should == false + end + + it "matches ranges of characters using bracket expression (e.g. [a-z])" do + File.fnmatch('ca[a-z]', 'cat').should == true + end + + it "matches ranges of characters using bracket expression, taking case into account" do + File.fnmatch('[a-z]', 'D').should == false + File.fnmatch('[^a-z]', 'D').should == true + File.fnmatch('[A-Z]', 'd').should == false + File.fnmatch('[^A-Z]', 'd').should == true + File.fnmatch('[a-z]', 'D', File::FNM_CASEFOLD).should == true + end + + it "does not match characters outside of the range of the bracket expression" do + File.fnmatch('ca[x-z]', 'cat').should == false + File.fnmatch('/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false + end + + it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do + File.fnmatch('ca[^t]', 'cat').should == false + File.fnmatch('ca[^t]', 'cas').should == true + File.fnmatch('ca[!t]', 'cat').should == false + end + + it "matches characters with a case sensitive comparison" do + File.fnmatch('cat', 'CAT').should == false + end + + it "matches characters with case insensitive comparison when flags includes FNM_CASEFOLD" do + File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD).should == true + end + + platform_is_not :windows do + it "doesn't match case sensitive characters on platforms with case sensitive paths, when flags include FNM_SYSCASE" do + File.fnmatch('cat', 'CAT', File::FNM_SYSCASE).should == false + end + end + + platform_is :windows do + it "matches case sensitive characters on platforms with case insensitive paths, when flags include FNM_SYSCASE" do + File.fnmatch('cat', 'CAT', File::FNM_SYSCASE).should == true + end + end + + it "matches wildcard with characters when flags includes FNM_PATHNAME" do + File.fnmatch('*a', 'aa', File::FNM_PATHNAME).should == true + File.fnmatch('a*', 'aa', File::FNM_PATHNAME).should == true + File.fnmatch('a*', 'aaa', File::FNM_PATHNAME).should == true + File.fnmatch('*a', 'aaa', File::FNM_PATHNAME).should == true + end + + it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do + File.fnmatch('?', '/', File::FNM_PATHNAME).should == false + File.fnmatch('*', '/', File::FNM_PATHNAME).should == false + end + + it "does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME" do + File.fnmatch('[/]', '/', File::FNM_PATHNAME).should == false + end + + it "matches literal ? or * in path when pattern includes \\? or \\*" do + File.fnmatch('\?', '?').should == true + File.fnmatch('\?', 'a').should == false + + File.fnmatch('\*', '*').should == true + File.fnmatch('\*', 'a').should == false + end + + it "matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \\a)" do + File.fnmatch('\a', 'a').should == true + File.fnmatch('this\b', 'thisb').should == true + end + + it "matches '\\' characters in path when flags includes FNM_NOESCAPE" do + File.fnmatch('\a', '\a', File::FNM_NOESCAPE).should == true + File.fnmatch('\a', 'a', File::FNM_NOESCAPE).should == false + File.fnmatch('\[foo\]\[bar\]', '[foo][bar]', File::FNM_NOESCAPE).should == false + end + + it "escapes special characters inside bracket expression" do + File.fnmatch('[\?]', '?').should == true + File.fnmatch('[\*]', '*').should == true + end + + it "does not match leading periods in filenames with wildcards by default" do + File.should_not.fnmatch('*', '.profile') + File.should.fnmatch('*', 'home/.profile') + File.should.fnmatch('*/*', 'home/.profile') + File.should_not.fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME) + end + + it "matches patterns with leading periods to dotfiles" do + File.fnmatch('.*', '.profile').should == true + File.fnmatch('.*', '.profile', File::FNM_PATHNAME).should == true + File.fnmatch(".*file", "nondotfile").should == false + File.fnmatch(".*file", "nondotfile", File::FNM_PATHNAME).should == false + end + + it "does not match directories with leading periods by default with FNM_PATHNAME" do + File.fnmatch('.*', '.directory/nondotfile', File::FNM_PATHNAME).should == false + File.fnmatch('.*', '.directory/.profile', File::FNM_PATHNAME).should == false + File.fnmatch('.*', 'foo/.directory/nondotfile', File::FNM_PATHNAME).should == false + File.fnmatch('.*', 'foo/.directory/.profile', File::FNM_PATHNAME).should == false + File.fnmatch('**/.dotfile', '.dotsubdir/.dotfile', File::FNM_PATHNAME).should == false + end + + it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do + File.fnmatch('*', '.profile', File::FNM_DOTMATCH).should == true + File.fnmatch('*', 'home/.profile', File::FNM_DOTMATCH).should == true + end + + it "matches multiple directories with ** and *" do + files = '**/*.rb' + File.fnmatch(files, 'main.rb').should == false + File.fnmatch(files, './main.rb').should == false + File.fnmatch(files, 'lib/song.rb').should == true + File.fnmatch('**.rb', 'main.rb').should == true + File.fnmatch('**.rb', './main.rb').should == false + File.fnmatch('**.rb', 'lib/song.rb').should == true + File.fnmatch('*', 'dave/.profile').should == true + end + + it "matches multiple directories with ** when flags includes File::FNM_PATHNAME" do + files = '**/*.rb' + flags = File::FNM_PATHNAME + + File.fnmatch(files, 'main.rb', flags).should == true + File.fnmatch(files, 'one/two/three/main.rb', flags).should == true + File.fnmatch(files, './main.rb', flags).should == false + + flags = File::FNM_PATHNAME | File::FNM_DOTMATCH + + File.fnmatch(files, './main.rb', flags).should == true + File.fnmatch(files, 'one/two/.main.rb', flags).should == true + + File.fnmatch("**/best/*", 'lib/my/best/song.rb').should == true + end + + it "returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME" do + pattern = '*/*' + File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME).should == false + + pattern = '**/foo' + File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should == false + end + + it "returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME" do + pattern = '*/*' + File.fnmatch(pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + + pattern = '**/foo' + File.fnmatch(pattern, 'a/b/c/foo', File::FNM_PATHNAME).should == true + File.fnmatch(pattern, '/a/b/c/foo', File::FNM_PATHNAME).should == true + File.fnmatch(pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should == true + File.fnmatch(pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + end + + it "has special handling for ./ when using * and FNM_PATHNAME" do + File.fnmatch('./*', '.', File::FNM_PATHNAME).should == false + File.fnmatch('./*', './', File::FNM_PATHNAME).should == true + File.fnmatch('./*/', './', File::FNM_PATHNAME).should == false + File.fnmatch('./**', './', File::FNM_PATHNAME).should == true + File.fnmatch('./**/', './', File::FNM_PATHNAME).should == true + File.fnmatch('./*', '.', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false + File.fnmatch('./*', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + File.fnmatch('./*/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false + File.fnmatch('./**', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + File.fnmatch('./**/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + end + + it "matches **/* with FNM_PATHNAME to recurse directories" do + File.fnmatch('nested/**/*', 'nested/subdir', File::FNM_PATHNAME).should == true + File.fnmatch('nested/**/*', 'nested/subdir/file', File::FNM_PATHNAME).should == true + File.fnmatch('nested/**/*', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + File.fnmatch('nested/**/*', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + end + + it "matches ** with FNM_PATHNAME only in current directory" do + File.fnmatch('nested/**', 'nested/subdir', File::FNM_PATHNAME).should == true + File.fnmatch('nested/**', 'nested/subdir/file', File::FNM_PATHNAME).should == false + File.fnmatch('nested/**', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true + File.fnmatch('nested/**', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false + end + + it "accepts an object that has a #to_path method" do + File.fnmatch('\*', mock_to_path('a')).should == false + end + + it "raises a TypeError if the first and second arguments are not string-like" do + -> { File.fnmatch(nil, nil, 0, 0) }.should.raise(ArgumentError) + -> { File.fnmatch(1, 'some/thing') }.should.raise(TypeError) + -> { File.fnmatch('some/thing', 1) }.should.raise(TypeError) + -> { File.fnmatch(1, 1) }.should.raise(TypeError) + end + + it "raises a TypeError if the third argument is not an Integer" do + -> { File.fnmatch("*/place", "path/to/file", "flags") }.should.raise(TypeError) + -> { File.fnmatch("*/place", "path/to/file", nil) }.should.raise(TypeError) + end + + it "does not raise a TypeError if the third argument can be coerced to an Integer" do + flags = mock("flags") + flags.should_receive(:to_int).and_return(10) + -> { File.fnmatch("*/place", "path/to/file", flags) }.should_not.raise + end + + it "matches multibyte characters" do + File.fnmatch("*/ä/ø/ñ", "a/ä/ø/ñ").should == true + end end describe "File.fnmatch?" do - it_behaves_like :file_fnmatch, :fnmatch? + it "is an alias of File.fnmatch" do + File.method(:fnmatch?).should == File.method(:fnmatch) + end end diff --git a/spec/ruby/core/file/path_spec.rb b/spec/ruby/core/file/path_spec.rb index ce78dbeede..f3b9b56dbe 100644 --- a/spec/ruby/core/file/path_spec.rb +++ b/spec/ruby/core/file/path_spec.rb @@ -1,8 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/path' describe "File#path" do - it_behaves_like :file_path, :path + it "is an alias of File#to_path" do + File.instance_method(:path).should == File.instance_method(:to_path) + end end describe "File.path" do diff --git a/spec/ruby/core/file/shared/fnmatch.rb b/spec/ruby/core/file/shared/fnmatch.rb deleted file mode 100644 index b9140d027d..0000000000 --- a/spec/ruby/core/file/shared/fnmatch.rb +++ /dev/null @@ -1,294 +0,0 @@ -describe :file_fnmatch, shared: true do - it "matches entire strings" do - File.send(@method, 'cat', 'cat').should == true - end - - it "does not match partial strings" do - File.send(@method, 'cat', 'category').should == false - end - - it "does not support { } patterns by default" do - File.send(@method, 'c{at,ub}s', 'cats').should == false - File.send(@method, 'c{at,ub}s', 'c{at,ub}s').should == true - end - - it "supports some { } patterns when File::FNM_EXTGLOB is passed" do - File.send(@method, "{a,b}", "a", File::FNM_EXTGLOB).should == true - File.send(@method, "{a,b}", "b", File::FNM_EXTGLOB).should == true - File.send(@method, "c{at,ub}s", "cats", File::FNM_EXTGLOB).should == true - File.send(@method, "c{at,ub}s", "cubs", File::FNM_EXTGLOB).should == true - File.send(@method, "-c{at,ub}s-", "-cats-", File::FNM_EXTGLOB).should == true - File.send(@method, "-c{at,ub}s-", "-cubs-", File::FNM_EXTGLOB).should == true - File.send(@method, "{a,b,c}{d,e,f}{g,h}", "adg", File::FNM_EXTGLOB).should == true - File.send(@method, "{a,b,c}{d,e,f}{g,h}", "bdg", File::FNM_EXTGLOB).should == true - File.send(@method, "{a,b,c}{d,e,f}{g,h}", "ceh", File::FNM_EXTGLOB).should == true - File.send(@method, "{aa,bb,cc,dd}", "aa", File::FNM_EXTGLOB).should == true - File.send(@method, "{aa,bb,cc,dd}", "bb", File::FNM_EXTGLOB).should == true - File.send(@method, "{aa,bb,cc,dd}", "cc", File::FNM_EXTGLOB).should == true - File.send(@method, "{aa,bb,cc,dd}", "dd", File::FNM_EXTGLOB).should == true - File.send(@method, "{1,5{a,b{c,d}}}", "1", File::FNM_EXTGLOB).should == true - File.send(@method, "{1,5{a,b{c,d}}}", "5a", File::FNM_EXTGLOB).should == true - File.send(@method, "{1,5{a,b{c,d}}}", "5bc", File::FNM_EXTGLOB).should == true - File.send(@method, "{1,5{a,b{c,d}}}", "5bd", File::FNM_EXTGLOB).should == true - File.send(@method, "\\\\{a\\,b,b\\}c}", "\\a,b", File::FNM_EXTGLOB).should == true - File.send(@method, "\\\\{a\\,b,b\\}c}", "\\b}c", File::FNM_EXTGLOB).should == true - end - - it "doesn't support some { } patterns even when File::FNM_EXTGLOB is passed" do - File.send(@method, "a{0..3}b", "a0b", File::FNM_EXTGLOB).should == false - File.send(@method, "a{0..3}b", "a1b", File::FNM_EXTGLOB).should == false - File.send(@method, "a{0..3}b", "a2b", File::FNM_EXTGLOB).should == false - File.send(@method, "a{0..3}b", "a3b", File::FNM_EXTGLOB).should == false - File.send(@method, "{0..12}", "0", File::FNM_EXTGLOB).should == false - File.send(@method, "{0..12}", "6", File::FNM_EXTGLOB).should == false - File.send(@method, "{0..12}", "12", File::FNM_EXTGLOB).should == false - File.send(@method, "{3..-2}", "3", File::FNM_EXTGLOB).should == false - File.send(@method, "{3..-2}", "0", File::FNM_EXTGLOB).should == false - File.send(@method, "{3..-2}", "-2", File::FNM_EXTGLOB).should == false - File.send(@method, "{a..g}", "a", File::FNM_EXTGLOB).should == false - File.send(@method, "{a..g}", "d", File::FNM_EXTGLOB).should == false - File.send(@method, "{a..g}", "g", File::FNM_EXTGLOB).should == false - File.send(@method, "{g..a}", "a", File::FNM_EXTGLOB).should == false - File.send(@method, "{g..a}", "d", File::FNM_EXTGLOB).should == false - File.send(@method, "{g..a}", "g", File::FNM_EXTGLOB).should == false - File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false - File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: ,", File::FNM_EXTGLOB).should == false - File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: }", File::FNM_EXTGLOB).should == false - File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false - end - - it "doesn't match an extra } when File::FNM_EXTGLOB is passed" do - File.send(@method, 'c{at,ub}}s', 'cats', File::FNM_EXTGLOB).should == false - end - - it "matches when both FNM_EXTGLOB and FNM_PATHNAME are passed" do - File.send(@method, "?.md", "a.md", File::FNM_EXTGLOB | File::FNM_PATHNAME).should == true - end - - it "matches a single character for each ? character" do - File.send(@method, 'c?t', 'cat').should == true - File.send(@method, 'c??t', 'cat').should == false - end - - it "matches zero or more characters for each * character" do - File.send(@method, 'c*', 'cats').should == true - File.send(@method, 'c*t', 'c/a/b/t').should == true - end - - it "does not match unterminated range of characters" do - File.send(@method, 'abc[de', 'abcd').should == false - end - - it "does not match unterminated range of characters as a literal" do - File.send(@method, 'abc[de', 'abc[de').should == false - end - - it "matches ranges of characters using bracket expression (e.g. [a-z])" do - File.send(@method, 'ca[a-z]', 'cat').should == true - end - - it "matches ranges of characters using bracket expression, taking case into account" do - File.send(@method, '[a-z]', 'D').should == false - File.send(@method, '[^a-z]', 'D').should == true - File.send(@method, '[A-Z]', 'd').should == false - File.send(@method, '[^A-Z]', 'd').should == true - File.send(@method, '[a-z]', 'D', File::FNM_CASEFOLD).should == true - end - - it "does not match characters outside of the range of the bracket expression" do - File.send(@method, 'ca[x-z]', 'cat').should == false - File.send(@method, '/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false - end - - it "matches ranges of characters using exclusive bracket expression (e.g. [^t] or [!t])" do - File.send(@method, 'ca[^t]', 'cat').should == false - File.send(@method, 'ca[^t]', 'cas').should == true - File.send(@method, 'ca[!t]', 'cat').should == false - end - - it "matches characters with a case sensitive comparison" do - File.send(@method, 'cat', 'CAT').should == false - end - - it "matches characters with case insensitive comparison when flags includes FNM_CASEFOLD" do - File.send(@method, 'cat', 'CAT', File::FNM_CASEFOLD).should == true - end - - platform_is_not :windows do - it "doesn't match case sensitive characters on platforms with case sensitive paths, when flags include FNM_SYSCASE" do - File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == false - end - end - - platform_is :windows do - it "matches case sensitive characters on platforms with case insensitive paths, when flags include FNM_SYSCASE" do - File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == true - end - end - - it "matches wildcard with characters when flags includes FNM_PATHNAME" do - File.send(@method, '*a', 'aa', File::FNM_PATHNAME).should == true - File.send(@method, 'a*', 'aa', File::FNM_PATHNAME).should == true - File.send(@method, 'a*', 'aaa', File::FNM_PATHNAME).should == true - File.send(@method, '*a', 'aaa', File::FNM_PATHNAME).should == true - end - - it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do - File.send(@method, '?', '/', File::FNM_PATHNAME).should == false - File.send(@method, '*', '/', File::FNM_PATHNAME).should == false - end - - it "does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME" do - File.send(@method, '[/]', '/', File::FNM_PATHNAME).should == false - end - - it "matches literal ? or * in path when pattern includes \\? or \\*" do - File.send(@method, '\?', '?').should == true - File.send(@method, '\?', 'a').should == false - - File.send(@method, '\*', '*').should == true - File.send(@method, '\*', 'a').should == false - end - - it "matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \\a)" do - File.send(@method, '\a', 'a').should == true - File.send(@method, 'this\b', 'thisb').should == true - end - - it "matches '\\' characters in path when flags includes FNM_NOESACPE" do - File.send(@method, '\a', '\a', File::FNM_NOESCAPE).should == true - File.send(@method, '\a', 'a', File::FNM_NOESCAPE).should == false - File.send(@method, '\[foo\]\[bar\]', '[foo][bar]', File::FNM_NOESCAPE).should == false - end - - it "escapes special characters inside bracket expression" do - File.send(@method, '[\?]', '?').should == true - File.send(@method, '[\*]', '*').should == true - end - - it "does not match leading periods in filenames with wildcards by default" do - File.should_not.send(@method, '*', '.profile') - File.should.send(@method, '*', 'home/.profile') - File.should.send(@method, '*/*', 'home/.profile') - File.should_not.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME) - end - - it "matches patterns with leading periods to dotfiles" do - File.send(@method, '.*', '.profile').should == true - File.send(@method, '.*', '.profile', File::FNM_PATHNAME).should == true - File.send(@method, ".*file", "nondotfile").should == false - File.send(@method, ".*file", "nondotfile", File::FNM_PATHNAME).should == false - end - - it "does not match directories with leading periods by default with FNM_PATHNAME" do - File.send(@method, '.*', '.directory/nondotfile', File::FNM_PATHNAME).should == false - File.send(@method, '.*', '.directory/.profile', File::FNM_PATHNAME).should == false - File.send(@method, '.*', 'foo/.directory/nondotfile', File::FNM_PATHNAME).should == false - File.send(@method, '.*', 'foo/.directory/.profile', File::FNM_PATHNAME).should == false - File.send(@method, '**/.dotfile', '.dotsubdir/.dotfile', File::FNM_PATHNAME).should == false - end - - it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do - File.send(@method, '*', '.profile', File::FNM_DOTMATCH).should == true - File.send(@method, '*', 'home/.profile', File::FNM_DOTMATCH).should == true - end - - it "matches multiple directories with ** and *" do - files = '**/*.rb' - File.send(@method, files, 'main.rb').should == false - File.send(@method, files, './main.rb').should == false - File.send(@method, files, 'lib/song.rb').should == true - File.send(@method, '**.rb', 'main.rb').should == true - File.send(@method, '**.rb', './main.rb').should == false - File.send(@method, '**.rb', 'lib/song.rb').should == true - File.send(@method, '*', 'dave/.profile').should == true - end - - it "matches multiple directories with ** when flags includes File::FNM_PATHNAME" do - files = '**/*.rb' - flags = File::FNM_PATHNAME - - File.send(@method, files, 'main.rb', flags).should == true - File.send(@method, files, 'one/two/three/main.rb', flags).should == true - File.send(@method, files, './main.rb', flags).should == false - - flags = File::FNM_PATHNAME | File::FNM_DOTMATCH - - File.send(@method, files, './main.rb', flags).should == true - File.send(@method, files, 'one/two/.main.rb', flags).should == true - - File.send(@method, "**/best/*", 'lib/my/best/song.rb').should == true - end - - it "returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME" do - pattern = '*/*' - File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should == false - - pattern = '**/foo' - File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should == false - end - - it "returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME" do - pattern = '*/*' - File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - - pattern = '**/foo' - File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should == true - File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should == true - File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should == true - File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - end - - it "has special handling for ./ when using * and FNM_PATHNAME" do - File.send(@method, './*', '.', File::FNM_PATHNAME).should == false - File.send(@method, './*', './', File::FNM_PATHNAME).should == true - File.send(@method, './*/', './', File::FNM_PATHNAME).should == false - File.send(@method, './**', './', File::FNM_PATHNAME).should == true - File.send(@method, './**/', './', File::FNM_PATHNAME).should == true - File.send(@method, './*', '.', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false - File.send(@method, './*', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - File.send(@method, './*/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false - File.send(@method, './**', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - File.send(@method, './**/', './', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - end - - it "matches **/* with FNM_PATHNAME to recurse directories" do - File.send(@method, 'nested/**/*', 'nested/subdir', File::FNM_PATHNAME).should == true - File.send(@method, 'nested/**/*', 'nested/subdir/file', File::FNM_PATHNAME).should == true - File.send(@method, 'nested/**/*', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - File.send(@method, 'nested/**/*', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - end - - it "matches ** with FNM_PATHNAME only in current directory" do - File.send(@method, 'nested/**', 'nested/subdir', File::FNM_PATHNAME).should == true - File.send(@method, 'nested/**', 'nested/subdir/file', File::FNM_PATHNAME).should == false - File.send(@method, 'nested/**', 'nested/.dotsubdir', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == true - File.send(@method, 'nested/**', 'nested/.dotsubir/.dotfile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should == false - end - - it "accepts an object that has a #to_path method" do - File.send(@method, '\*', mock_to_path('a')).should == false - end - - it "raises a TypeError if the first and second arguments are not string-like" do - -> { File.send(@method, nil, nil, 0, 0) }.should.raise(ArgumentError) - -> { File.send(@method, 1, 'some/thing') }.should.raise(TypeError) - -> { File.send(@method, 'some/thing', 1) }.should.raise(TypeError) - -> { File.send(@method, 1, 1) }.should.raise(TypeError) - end - - it "raises a TypeError if the third argument is not an Integer" do - -> { File.send(@method, "*/place", "path/to/file", "flags") }.should.raise(TypeError) - -> { File.send(@method, "*/place", "path/to/file", nil) }.should.raise(TypeError) - end - - it "does not raise a TypeError if the third argument can be coerced to an Integer" do - flags = mock("flags") - flags.should_receive(:to_int).and_return(10) - -> { File.send(@method, "*/place", "path/to/file", flags) }.should_not.raise - end - - it "matches multibyte characters" do - File.fnmatch("*/ä/ø/ñ", "a/ä/ø/ñ").should == true - end -end diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb deleted file mode 100644 index 6c6f7d4234..0000000000 --- a/spec/ruby/core/file/shared/path.rb +++ /dev/null @@ -1,82 +0,0 @@ -describe :file_path, shared: true do - before :each do - @path = tmp("file_to_path") - @name = File.basename(@path) - touch @path - end - - after :each do - @file.close if @file and !@file.closed? - rm_r @path - end - - it "returns a String" do - @file = File.new @path - @file.send(@method).should.instance_of?(String) - end - - it "returns a different String on every call" do - @file = File.new @path - path1 = @file.send(@method) - path2 = @file.send(@method) - path1.should == path2 - path1.should_not.equal?(path2) - end - - it "returns a mutable String" do - @file = File.new @path.dup.freeze - path = @file.send(@method) - path.should == @path - path.should_not.frozen? - path << "test" - @file.send(@method).should == @path - end - - it "calls to_str on argument and returns exact value" do - path = mock('path') - path.should_receive(:to_str).and_return(@path) - @file = File.new path - @file.send(@method).should == @path - end - - it "does not normalise the path it returns" do - Dir.chdir(tmp("")) do - unorm = "./#{@name}" - @file = File.new unorm - @file.send(@method).should == unorm - end - end - - it "does not canonicalize the path it returns" do - dir = File.basename tmp("") - path = "#{tmp("")}../#{dir}/#{@name}" - @file = File.new path - @file.send(@method).should == path - end - - it "does not absolute-ise the path it returns" do - Dir.chdir(tmp("")) do - @file = File.new @name - @file.send(@method).should == @name - end - end - - it "preserves the encoding of the path" do - path = @path.force_encoding("euc-jp") - @file = File.new path - @file.send(@method).encoding.should == Encoding.find("euc-jp") - end - - platform_is :linux do - guard -> { defined?(File::TMPFILE) } do - before :each do - @dir = tmp("tmpfilespec") - mkdir_p @dir - end - - after :each do - rm_r @dir - end - end - end -end diff --git a/spec/ruby/core/file/shared/unlink.rb b/spec/ruby/core/file/shared/unlink.rb deleted file mode 100644 index 0032907ba2..0000000000 --- a/spec/ruby/core/file/shared/unlink.rb +++ /dev/null @@ -1,61 +0,0 @@ -describe :file_unlink, shared: true do - before :each do - @file1 = tmp('test.txt') - @file2 = tmp('test2.txt') - - touch @file1 - touch @file2 - end - - after :each do - File.send(@method, @file1) if File.exist?(@file1) - File.send(@method, @file2) if File.exist?(@file2) - - @file1 = nil - @file2 = nil - end - - it "returns 0 when called without arguments" do - File.send(@method).should == 0 - end - - it "deletes a single file" do - File.send(@method, @file1).should == 1 - File.should_not.exist?(@file1) - end - - it "deletes multiple files" do - File.send(@method, @file1, @file2).should == 2 - File.should_not.exist?(@file1) - File.should_not.exist?(@file2) - end - - it "raises a TypeError if not passed a String type" do - -> { File.send(@method, 1) }.should.raise(TypeError) - end - - it "raises an Errno::ENOENT when the given file doesn't exist" do - -> { File.send(@method, 'bogus') }.should.raise(Errno::ENOENT) - end - - it "coerces a given parameter into a string if possible" do - mock = mock("to_str") - mock.should_receive(:to_str).and_return(@file1) - File.send(@method, mock).should == 1 - end - - it "accepts an object that has a #to_path method" do - File.send(@method, mock_to_path(@file1)).should == 1 - end - - platform_is :windows do - it "allows deleting an open file with File::SHARE_DELETE" do - path = tmp("share_delete.txt") - File.open(path, mode: File::CREAT | File::WRONLY | File::BINARY | File::SHARE_DELETE) do |f| - File.should.exist?(path) - File.send(@method, path) - end - File.should_not.exist?(path) - end - end -end diff --git a/spec/ruby/core/file/sticky_spec.rb b/spec/ruby/core/file/sticky_spec.rb index 5f7b2d93eb..782541abf3 100644 --- a/spec/ruby/core/file/sticky_spec.rb +++ b/spec/ruby/core/file/sticky_spec.rb @@ -41,7 +41,7 @@ describe "File.sticky?" do touch(filename) stat = File.stat(filename) mode = stat.mode - raise_error(Errno::EFTYPE){File.chmod(mode|01000, filename)} + -> { File.chmod(mode|01000, filename) }.should.raise(Errno::EFTYPE) File.sticky?(filename).should == false rm_r filename diff --git a/spec/ruby/core/file/to_path_spec.rb b/spec/ruby/core/file/to_path_spec.rb index 6d168a065c..b968d3b2b9 100644 --- a/spec/ruby/core/file/to_path_spec.rb +++ b/spec/ruby/core/file/to_path_spec.rb @@ -1,6 +1,84 @@ require_relative '../../spec_helper' -require_relative 'shared/path' describe "File#to_path" do - it_behaves_like :file_path, :to_path + before :each do + @path = tmp("file_to_path") + @name = File.basename(@path) + touch @path + end + + after :each do + @file.close if @file and !@file.closed? + rm_r @path + end + + it "returns a String" do + @file = File.new @path + @file.to_path.should.instance_of?(String) + end + + it "returns a different String on every call" do + @file = File.new @path + path1 = @file.to_path + path2 = @file.to_path + path1.should == path2 + path1.should_not.equal?(path2) + end + + it "returns a mutable String" do + @file = File.new @path.dup.freeze + path = @file.to_path + path.should == @path + path.should_not.frozen? + path << "test" + @file.to_path.should == @path + end + + it "calls to_str on argument and returns exact value" do + path = mock('path') + path.should_receive(:to_str).and_return(@path) + @file = File.new path + @file.to_path.should == @path + end + + it "does not normalise the path it returns" do + Dir.chdir(tmp("")) do + unorm = "./#{@name}" + @file = File.new unorm + @file.to_path.should == unorm + end + end + + it "does not canonicalize the path it returns" do + dir = File.basename tmp("") + path = "#{tmp("")}../#{dir}/#{@name}" + @file = File.new path + @file.to_path.should == path + end + + it "does not absolute-ise the path it returns" do + Dir.chdir(tmp("")) do + @file = File.new @name + @file.to_path.should == @name + end + end + + it "preserves the encoding of the path" do + path = @path.force_encoding("euc-jp") + @file = File.new path + @file.to_path.encoding.should == Encoding.find("euc-jp") + end + + platform_is :linux do + guard -> { defined?(File::TMPFILE) } do + before :each do + @dir = tmp("tmpfilespec") + mkdir_p @dir + end + + after :each do + rm_r @dir + end + end + end end diff --git a/spec/ruby/core/file/unlink_spec.rb b/spec/ruby/core/file/unlink_spec.rb index 28872d55ed..db0c08f3ae 100644 --- a/spec/ruby/core/file/unlink_spec.rb +++ b/spec/ruby/core/file/unlink_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/unlink' describe "File.unlink" do - it_behaves_like :file_unlink, :unlink + it "is an alias of File.delete" do + File.method(:unlink).should == File.method(:delete) + end end diff --git a/spec/ruby/core/file/zero_spec.rb b/spec/ruby/core/file/zero_spec.rb index 01c7505ef2..09b0decf48 100644 --- a/spec/ruby/core/file/zero_spec.rb +++ b/spec/ruby/core/file/zero_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/file/zero' describe "File.zero?" do - it_behaves_like :file_zero, :zero?, File - it_behaves_like :file_zero_missing, :zero?, File + it "is an alias of File.empty?" do + File.method(:zero?).should == File.method(:empty?) + end end diff --git a/spec/ruby/core/filetest/empty_spec.rb b/spec/ruby/core/filetest/empty_spec.rb new file mode 100644 index 0000000000..2c7fbe0dcd --- /dev/null +++ b/spec/ruby/core/filetest/empty_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' +require_relative '../../shared/file/zero' + +describe "FileTest.empty?" do + it_behaves_like :file_zero, :empty?, FileTest + it_behaves_like :file_zero_missing, :empty?, FileTest +end diff --git a/spec/ruby/core/filetest/zero_spec.rb b/spec/ruby/core/filetest/zero_spec.rb index 92cab67f1b..de024c25ff 100644 --- a/spec/ruby/core/filetest/zero_spec.rb +++ b/spec/ruby/core/filetest/zero_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/file/zero' describe "FileTest.zero?" do - it_behaves_like :file_zero, :zero?, FileTest - it_behaves_like :file_zero_missing, :zero?, FileTest + it "is an alias of FileTest.empty?" do + FileTest.method(:zero?).should == FileTest.method(:empty?) + end end diff --git a/spec/ruby/core/float/angle_spec.rb b/spec/ruby/core/float/angle_spec.rb index c07249aa97..ac182c5b73 100644 --- a/spec/ruby/core/float/angle_spec.rb +++ b/spec/ruby/core/float/angle_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Float#angle" do - it_behaves_like :float_arg, :angle + it "is an alias of Float#arg" do + Float.instance_method(:angle).should == Float.instance_method(:arg) + end end diff --git a/spec/ruby/core/float/arg_spec.rb b/spec/ruby/core/float/arg_spec.rb index d3a50668f8..c9c602bbf6 100644 --- a/spec/ruby/core/float/arg_spec.rb +++ b/spec/ruby/core/float/arg_spec.rb @@ -1,6 +1,38 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Float#arg" do - it_behaves_like :float_arg, :arg + it "returns NaN if NaN" do + f = nan_value + f.arg.nan?.should == true + end + + it "returns self if NaN" do + f = nan_value + f.arg.should.equal?(f) + end + + it "returns 0 if positive" do + 1.0.arg.should == 0 + end + + it "returns 0 if +0.0" do + 0.0.arg.should == 0 + end + + it "returns 0 if +Infinity" do + infinity_value.arg.should == 0 + end + + it "returns Pi if negative" do + (-1.0).arg.should == Math::PI + end + + # This was established in r23960 + it "returns Pi if -0.0" do + (-0.0).arg.should == Math::PI + end + + it "returns Pi if -Infinity" do + (-infinity_value).arg.should == Math::PI + end end diff --git a/spec/ruby/core/float/case_compare_spec.rb b/spec/ruby/core/float/case_compare_spec.rb index b902fbea18..c82803642d 100644 --- a/spec/ruby/core/float/case_compare_spec.rb +++ b/spec/ruby/core/float/case_compare_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/equal' describe "Float#===" do - it_behaves_like :float_equal, :=== + it "is an alias of Float#==" do + Float.instance_method(:===).should == Float.instance_method(:==) + end end diff --git a/spec/ruby/core/float/equal_value_spec.rb b/spec/ruby/core/float/equal_value_spec.rb index 03eea5108e..37d0e162d3 100644 --- a/spec/ruby/core/float/equal_value_spec.rb +++ b/spec/ruby/core/float/equal_value_spec.rb @@ -1,6 +1,40 @@ require_relative '../../spec_helper' -require_relative 'shared/equal' describe "Float#==" do - it_behaves_like :float_equal, :== + it "returns true if self has the same value as other" do + (1.0 == 1).should == true + (2.71828 == 1.428).should == false + (-4.2 == 4.2).should == false + end + + it "calls 'other == self' if coercion fails" do + x = mock('other') + def x.==(other) + 2.0 == other + end + + (1.0 == x).should == false + (2.0 == x).should == true + end + + it "returns false if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value == n).should == false + (n == nan_value).should == false + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value == n).should == false + (n == infinity_value).should == false + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + ((-infinity_value) == n).should == false + (n == -infinity_value).should == false + } + end end diff --git a/spec/ruby/core/float/fdiv_spec.rb b/spec/ruby/core/float/fdiv_spec.rb index be25ee283b..8a3ead4880 100644 --- a/spec/ruby/core/float/fdiv_spec.rb +++ b/spec/ruby/core/float/fdiv_spec.rb @@ -1,6 +1,61 @@ require_relative '../../spec_helper' -require_relative 'shared/quo' describe "Float#fdiv" do - it_behaves_like :float_quo, :fdiv + it "performs floating-point division between self and an Integer" do + 8.9.fdiv(7).should == 1.2714285714285716 + end + + it "performs floating-point division between self and an Integer" do + 8.9.fdiv(9999999999999**9).should == 8.900000000008011e-117 + end + + it "performs floating-point division between self and a Float" do + 2827.22.fdiv(872.111111).should == 3.2418116961704433 + end + + it "returns NaN when the argument is NaN" do + -1819.999999.fdiv(nan_value).nan?.should == true + 11109.1981271.fdiv(nan_value).nan?.should == true + end + + it "returns Infinity when the argument is 0.0" do + 2827.22.fdiv(0.0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0.0 and self is negative" do + -48229.282.fdiv(0.0).infinite?.should == -1 + end + + it "returns Infinity when the argument is 0" do + 2827.22.fdiv(0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0 and self is negative" do + -48229.282.fdiv(0).infinite?.should == -1 + end + + it "returns 0.0 when the argument is Infinity" do + 47292.2821.fdiv(infinity_value).should == 0.0 + end + + it "returns -0.0 when the argument is -Infinity" do + 1.9999918.fdiv(-infinity_value).should == -0.0 + end + + it "performs floating-point division between self and a Rational" do + 74620.09.fdiv(Rational(2,3)).should == 111930.135 + end + + it "performs floating-point division between self and a Complex" do + 74620.09.fdiv(Complex(8,2)).should == Complex( + 8778.834117647059, -2194.7085294117646) + end + + it "raises a TypeError when argument isn't numeric" do + -> { 27292.2.fdiv(mock('non-numeric')) }.should.raise(TypeError) + end + + it "raises an ArgumentError when passed multiple arguments" do + -> { 272.221.fdiv(6,0.2) }.should.raise(ArgumentError) + end end diff --git a/spec/ruby/core/float/inspect_spec.rb b/spec/ruby/core/float/inspect_spec.rb index 4be1927d84..3827167c17 100644 --- a/spec/ruby/core/float/inspect_spec.rb +++ b/spec/ruby/core/float/inspect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' describe "Float#inspect" do - it_behaves_like :float_to_s, :inspect + it "is an alias of Float#to_s" do + Float.instance_method(:inspect).should == Float.instance_method(:to_s) + end end diff --git a/spec/ruby/core/float/magnitude_spec.rb b/spec/ruby/core/float/magnitude_spec.rb index 7cdd8ef28a..4a753267e0 100644 --- a/spec/ruby/core/float/magnitude_spec.rb +++ b/spec/ruby/core/float/magnitude_spec.rb @@ -2,5 +2,13 @@ require_relative "../../spec_helper" require_relative 'shared/abs' describe "Float#magnitude" do - it_behaves_like :float_abs, :magnitude + ruby_version_is ""..."3.4" do + it_behaves_like :float_abs, :magnitude + end + + ruby_version_is "3.4" do + it "is an alias of Float#abs" do + Float.instance_method(:magnitude).should == Float.instance_method(:abs) + end + end end diff --git a/spec/ruby/core/float/modulo_spec.rb b/spec/ruby/core/float/modulo_spec.rb index 8ae80a0b05..8b7aedf822 100644 --- a/spec/ruby/core/float/modulo_spec.rb +++ b/spec/ruby/core/float/modulo_spec.rb @@ -1,10 +1,56 @@ require_relative '../../spec_helper' -require_relative 'shared/modulo' describe "Float#%" do - it_behaves_like :float_modulo, :% + it "returns self modulo other" do + (6543.21 % 137).should be_close(104.21, TOLERANCE) + (5667.19 % bignum_value).should be_close(5667.19, TOLERANCE) + (6543.21 % 137.24).should be_close(92.9299999999996, TOLERANCE) + + (-1.0 % 1).should == 0 + end + + it "returns self when modulus is +Infinity" do + (4.2 % Float::INFINITY).should == 4.2 + end + + it "returns -Infinity when modulus is -Infinity" do + (4.2 % -Float::INFINITY).should == -Float::INFINITY + end + + it "returns NaN when called on NaN or Infinities" do + (Float::NAN % 42).should.nan? + (Float::INFINITY % 42).should.nan? + (-Float::INFINITY % 42).should.nan? + end + + it "returns NaN when modulus is NaN" do + (4.2 % Float::NAN).should.nan? + end + + it "returns -0.0 when called on -0.0 with a non zero modulus" do + r = -0.0 % 42 + r.should == 0 + (1/r).should < 0 + + r = -0.0 % Float::INFINITY + r.should == 0 + (1/r).should < 0 + end + + it "tries to coerce the modulus" do + obj = mock("modulus") + obj.should_receive(:coerce).with(1.25).and_return([1.25, 0.5]) + (1.25 % obj).should == 0.25 + end + + it "raises a ZeroDivisionError if other is zero" do + -> { 1.0 % 0 }.should.raise(ZeroDivisionError) + -> { 1.0 % 0.0 }.should.raise(ZeroDivisionError) + end end describe "Float#modulo" do - it_behaves_like :float_modulo, :modulo + it "is an alias of Float#%" do + Float.instance_method(:modulo).should == Float.instance_method(:%) + end end diff --git a/spec/ruby/core/float/phase_spec.rb b/spec/ruby/core/float/phase_spec.rb index 2aa84024b4..ad112ac9fe 100644 --- a/spec/ruby/core/float/phase_spec.rb +++ b/spec/ruby/core/float/phase_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Float#phase" do - it_behaves_like :float_arg, :phase + it "is an alias of Float#arg" do + Float.instance_method(:phase).should == Float.instance_method(:arg) + end end diff --git a/spec/ruby/core/float/quo_spec.rb b/spec/ruby/core/float/quo_spec.rb index b5c64f9d87..0e9a7a0a0c 100644 --- a/spec/ruby/core/float/quo_spec.rb +++ b/spec/ruby/core/float/quo_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/quo' describe "Float#quo" do - it_behaves_like :float_quo, :quo + it "is an alias of Float#fdiv" do + Float.instance_method(:quo).should == Float.instance_method(:fdiv) + end end diff --git a/spec/ruby/core/float/shared/arg.rb b/spec/ruby/core/float/shared/arg.rb deleted file mode 100644 index de0024313d..0000000000 --- a/spec/ruby/core/float/shared/arg.rb +++ /dev/null @@ -1,36 +0,0 @@ -describe :float_arg, shared: true do - it "returns NaN if NaN" do - f = nan_value - f.send(@method).nan?.should == true - end - - it "returns self if NaN" do - f = nan_value - f.send(@method).should.equal?(f) - end - - it "returns 0 if positive" do - 1.0.send(@method).should == 0 - end - - it "returns 0 if +0.0" do - 0.0.send(@method).should == 0 - end - - it "returns 0 if +Infinity" do - infinity_value.send(@method).should == 0 - end - - it "returns Pi if negative" do - (-1.0).send(@method).should == Math::PI - end - - # This was established in r23960 - it "returns Pi if -0.0" do - (-0.0).send(@method).should == Math::PI - end - - it "returns Pi if -Infinity" do - (-infinity_value).send(@method).should == Math::PI - end -end diff --git a/spec/ruby/core/float/shared/equal.rb b/spec/ruby/core/float/shared/equal.rb deleted file mode 100644 index 4d524e1cf2..0000000000 --- a/spec/ruby/core/float/shared/equal.rb +++ /dev/null @@ -1,38 +0,0 @@ -describe :float_equal, shared: true do - it "returns true if self has the same value as other" do - 1.0.send(@method, 1).should == true - 2.71828.send(@method, 1.428).should == false - -4.2.send(@method, 4.2).should == false - end - - it "calls 'other == self' if coercion fails" do - x = mock('other') - def x.==(other) - 2.0 == other - end - - 1.0.send(@method, x).should == false - 2.0.send(@method, x).should == true - end - - it "returns false if one side is NaN" do - [1.0, 42, bignum_value].each { |n| - (nan_value.send(@method, n)).should == false - (n.send(@method, nan_value)).should == false - } - end - - it "handles positive infinity" do - [1.0, 42, bignum_value].each { |n| - (infinity_value.send(@method, n)).should == false - (n.send(@method, infinity_value)).should == false - } - end - - it "handles negative infinity" do - [1.0, 42, bignum_value].each { |n| - ((-infinity_value).send(@method, n)).should == false - (n.send(@method, -infinity_value)).should == false - } - end -end diff --git a/spec/ruby/core/float/shared/modulo.rb b/spec/ruby/core/float/shared/modulo.rb deleted file mode 100644 index 1efee1476d..0000000000 --- a/spec/ruby/core/float/shared/modulo.rb +++ /dev/null @@ -1,48 +0,0 @@ -describe :float_modulo, shared: true do - it "returns self modulo other" do - 6543.21.send(@method, 137).should be_close(104.21, TOLERANCE) - 5667.19.send(@method, bignum_value).should be_close(5667.19, TOLERANCE) - 6543.21.send(@method, 137.24).should be_close(92.9299999999996, TOLERANCE) - - -1.0.send(@method, 1).should == 0 - end - - it "returns self when modulus is +Infinity" do - 4.2.send(@method, Float::INFINITY).should == 4.2 - end - - it "returns -Infinity when modulus is -Infinity" do - 4.2.send(@method, -Float::INFINITY).should == -Float::INFINITY - end - - it "returns NaN when called on NaN or Infinities" do - Float::NAN.send(@method, 42).should.nan? - Float::INFINITY.send(@method, 42).should.nan? - (-Float::INFINITY).send(@method, 42).should.nan? - end - - it "returns NaN when modulus is NaN" do - 4.2.send(@method, Float::NAN).should.nan? - end - - it "returns -0.0 when called on -0.0 with a non zero modulus" do - r = (-0.0).send(@method, 42) - r.should == 0 - (1/r).should < 0 - - r = (-0.0).send(@method, Float::INFINITY) - r.should == 0 - (1/r).should < 0 - end - - it "tries to coerce the modulus" do - obj = mock("modulus") - obj.should_receive(:coerce).with(1.25).and_return([1.25, 0.5]) - (1.25 % obj).should == 0.25 - end - - it "raises a ZeroDivisionError if other is zero" do - -> { 1.0.send(@method, 0) }.should.raise(ZeroDivisionError) - -> { 1.0.send(@method, 0.0) }.should.raise(ZeroDivisionError) - end -end diff --git a/spec/ruby/core/float/shared/quo.rb b/spec/ruby/core/float/shared/quo.rb deleted file mode 100644 index 930187aaf7..0000000000 --- a/spec/ruby/core/float/shared/quo.rb +++ /dev/null @@ -1,59 +0,0 @@ -describe :float_quo, shared: true do - it "performs floating-point division between self and an Integer" do - 8.9.send(@method, 7).should == 1.2714285714285716 - end - - it "performs floating-point division between self and an Integer" do - 8.9.send(@method, 9999999999999**9).should == 8.900000000008011e-117 - end - - it "performs floating-point division between self and a Float" do - 2827.22.send(@method, 872.111111).should == 3.2418116961704433 - end - - it "returns NaN when the argument is NaN" do - -1819.999999.send(@method, nan_value).nan?.should == true - 11109.1981271.send(@method, nan_value).nan?.should == true - end - - it "returns Infinity when the argument is 0.0" do - 2827.22.send(@method, 0.0).infinite?.should == 1 - end - - it "returns -Infinity when the argument is 0.0 and self is negative" do - -48229.282.send(@method, 0.0).infinite?.should == -1 - end - - it "returns Infinity when the argument is 0" do - 2827.22.send(@method, 0).infinite?.should == 1 - end - - it "returns -Infinity when the argument is 0 and self is negative" do - -48229.282.send(@method, 0).infinite?.should == -1 - end - - it "returns 0.0 when the argument is Infinity" do - 47292.2821.send(@method, infinity_value).should == 0.0 - end - - it "returns -0.0 when the argument is -Infinity" do - 1.9999918.send(@method, -infinity_value).should == -0.0 - end - - it "performs floating-point division between self and a Rational" do - 74620.09.send(@method, Rational(2,3)).should == 111930.135 - end - - it "performs floating-point division between self and a Complex" do - 74620.09.send(@method, Complex(8,2)).should == Complex( - 8778.834117647059, -2194.7085294117646) - end - - it "raises a TypeError when argument isn't numeric" do - -> { 27292.2.send(@method, mock('non-numeric')) }.should.raise(TypeError) - end - - it "raises an ArgumentError when passed multiple arguments" do - -> { 272.221.send(@method, 6,0.2) }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/core/float/shared/to_s.rb b/spec/ruby/core/float/shared/to_s.rb deleted file mode 100644 index 81ffdd9e81..0000000000 --- a/spec/ruby/core/float/shared/to_s.rb +++ /dev/null @@ -1,308 +0,0 @@ -describe :float_to_s, shared: true do - it "returns 'NaN' for NaN" do - nan_value().send(@method).should == 'NaN' - end - - it "returns 'Infinity' for positive infinity" do - infinity_value().send(@method).should == 'Infinity' - end - - it "returns '-Infinity' for negative infinity" do - (-infinity_value()).send(@method).should == '-Infinity' - end - - it "returns '0.0' for 0.0" do - 0.0.send(@method).should == "0.0" - end - - platform_is_not :openbsd do - it "emits '-' for -0.0" do - -0.0.send(@method).should == "-0.0" - end - end - - it "emits a '-' for negative values" do - -3.14.send(@method).should == "-3.14" - end - - it "emits a trailing '.0' for a whole number" do - 50.0.send(@method).should == "50.0" - end - - it "emits a trailing '.0' for the mantissa in e format" do - 1.0e20.send(@method).should == "1.0e+20" - end - - it "uses non-e format for a positive value with fractional part having 5 significant figures" do - 0.0001.send(@method).should == "0.0001" - end - - it "uses non-e format for a negative value with fractional part having 5 significant figures" do - -0.0001.send(@method).should == "-0.0001" - end - - it "uses e format for a positive value with fractional part having 6 significant figures" do - 0.00001.send(@method).should == "1.0e-05" - end - - it "uses e format for a negative value with fractional part having 6 significant figures" do - -0.00001.send(@method).should == "-1.0e-05" - end - - it "uses non-e format for a positive value with whole part having 15 significant figures" do - 10000000000000.0.send(@method).should == "10000000000000.0" - end - - it "uses non-e format for a negative value with whole part having 15 significant figures" do - -10000000000000.0.send(@method).should == "-10000000000000.0" - end - - it "uses non-e format for a positive value with whole part having 16 significant figures" do - 100000000000000.0.send(@method).should == "100000000000000.0" - end - - it "uses non-e format for a negative value with whole part having 16 significant figures" do - -100000000000000.0.send(@method).should == "-100000000000000.0" - end - - it "uses e format for a positive value with whole part having 18 significant figures" do - 10000000000000000.0.send(@method).should == "1.0e+16" - end - - it "uses e format for a negative value with whole part having 18 significant figures" do - -10000000000000000.0.send(@method).should == "-1.0e+16" - end - - it "uses e format for a positive value with whole part having 17 significant figures" do - 1000000000000000.0.send(@method).should == "1.0e+15" - end - - it "uses e format for a negative value with whole part having 17 significant figures" do - -1000000000000000.0.send(@method).should == "-1.0e+15" - end - - # #3273 - it "outputs the minimal, unique form necessary to recreate the value" do - value = 0.21611564636388508 - string = "0.21611564636388508" - - value.send(@method).should == string - string.to_f.should == value - end - - it "outputs the minimal, unique form to represent the value" do - 0.56.send(@method).should == "0.56" - end - - describe "matches" do - it "random examples in all ranges" do - # 50.times do - # bytes = (0...8).map { rand(256) } - # string = bytes.pack('C8') - # float = string.unpack('D').first - # puts "#{'%.20g' % float}.send(@method).should == #{float.send(@method).inspect}" - # end - - 2.5540217314354050325e+163.send(@method).should == "2.554021731435405e+163" - 2.5492588360356597544e-172.send(@method).should == "2.5492588360356598e-172" - 1.742770260934704852e-82.send(@method).should == "1.7427702609347049e-82" - 6.2108093676180883209e-104.send(@method).should == "6.210809367618088e-104" - -3.3448803488331067402e-143.send(@method).should == "-3.3448803488331067e-143" - -2.2740074343500832557e-168.send(@method).should == "-2.2740074343500833e-168" - 7.0587971678048535732e+191.send(@method).should == "7.058797167804854e+191" - -284438.88327586348169.send(@method).should == "-284438.8832758635" - 3.953272468476091301e+105.send(@method).should == "3.9532724684760913e+105" - -3.6361359552959847853e+100.send(@method).should == "-3.636135955295985e+100" - -1.3222325865575206185e-31.send(@method).should == "-1.3222325865575206e-31" - 1.1440138916932761366e+130.send(@method).should == "1.1440138916932761e+130" - 4.8750891560387561157e-286.send(@method).should == "4.875089156038756e-286" - 5.6101113356591453525e-257.send(@method).should == "5.610111335659145e-257" - -3.829644279545809575e-100.send(@method).should == "-3.8296442795458096e-100" - 1.5342839401396406117e-194.send(@method).should == "1.5342839401396406e-194" - 2.2284972755169921402e-144.send(@method).should == "2.228497275516992e-144" - 2.1825655917065601737e-61.send(@method).should == "2.1825655917065602e-61" - -2.6672271363524338322e-62.send(@method).should == "-2.667227136352434e-62" - -1.9257995160119059415e+21.send(@method).should == "-1.925799516011906e+21" - -8.9096732962887121718e-198.send(@method).should == "-8.909673296288712e-198" - 2.0202075376548644959e-90.send(@method).should == "2.0202075376548645e-90" - -7.7341602581786258961e-266.send(@method).should == "-7.734160258178626e-266" - 3.5134482598733635046e+98.send(@method).should == "3.5134482598733635e+98" - -2.124411722371029134e+154.send(@method).should == "-2.124411722371029e+154" - -4.573908787355718687e+110.send(@method).should == "-4.573908787355719e+110" - -1.9344425934170969879e-232.send(@method).should == "-1.934442593417097e-232" - -1.3274227399979271095e+171.send(@method).should == "-1.3274227399979271e+171" - 9.3495270482104442383e-283.send(@method).should == "9.349527048210444e-283" - -4.2046059371986483233e+307.send(@method).should == "-4.2046059371986483e+307" - 3.6133547278583543004e-117.send(@method).should == "3.613354727858354e-117" - 4.9247416523566613499e-08.send(@method).should == "4.9247416523566613e-08" - 1.6936145488250064007e-71.send(@method).should == "1.6936145488250064e-71" - 2.4455483206829433098e+96.send(@method).should == "2.4455483206829433e+96" - 7.9797449851436455384e+124.send(@method).should == "7.979744985143646e+124" - -1.3873689634457876774e-129.send(@method).should == "-1.3873689634457877e-129" - 3.9761102037533483075e+284.send(@method).should == "3.976110203753348e+284" - -4.2819791952139402486e-303.send(@method).should == "-4.28197919521394e-303" - -5.7981017546689831298e-116.send(@method).should == "-5.798101754668983e-116" - -3.953266497860534199e-28.send(@method).should == "-3.953266497860534e-28" - -2.0659852720290440959e-243.send(@method).should == "-2.065985272029044e-243" - 8.9670488995878688018e-05.send(@method).should == "8.967048899587869e-05" - -1.2317943708113061768e-98.send(@method).should == "-1.2317943708113062e-98" - -3.8930768307633080463e+248.send(@method).should == "-3.893076830763308e+248" - 6.5854032671803925627e-239.send(@method).should == "6.5854032671803926e-239" - 4.6257022188980878952e+177.send(@method).should == "4.625702218898088e+177" - -1.9397155125507235603e-187.send(@method).should == "-1.9397155125507236e-187" - 8.5752156951245705056e+117.send(@method).should == "8.57521569512457e+117" - -2.4784875958162501671e-132.send(@method).should == "-2.4784875958162502e-132" - -4.4125691841230058457e-203.send(@method).should == "-4.412569184123006e-203" - end - - it "random examples in human ranges" do - # 50.times do - # formatted = '' - # rand(1..3).times do - # formatted << rand(10).to_s - # end - # formatted << '.' - # rand(1..9).times do - # formatted << rand(10).to_s - # end - # float = formatted.to_f - # puts "#{'%.20f' % float}.send(@method).should == #{float.send(@method).inspect}" - # end - - 5.17869899999999994122.send(@method).should == "5.178699" - 905.62695729999995819526.send(@method).should == "905.6269573" - 62.75999999999999801048.send(@method).should == "62.76" - 6.93856795800000014651.send(@method).should == "6.938567958" - 4.95999999999999996447.send(@method).should == "4.96" - 32.77993899999999882766.send(@method).should == "32.779939" - 544.12756779999995160324.send(@method).should == "544.1275678" - 66.25801119999999855281.send(@method).should == "66.2580112" - 7.90000000000000035527.send(@method).should == "7.9" - 5.93100000000000004974.send(@method).should == "5.931" - 5.21229313600000043749.send(@method).should == "5.212293136" - 503.44173809000000119340.send(@method).should == "503.44173809" - 79.26000000000000511591.send(@method).should == "79.26" - 8.51524999999999998579.send(@method).should == "8.51525" - 174.00000000000000000000.send(@method).should == "174.0" - 50.39580000000000126192.send(@method).should == "50.3958" - 35.28999999999999914735.send(@method).should == "35.29" - 5.43136675399999990788.send(@method).should == "5.431366754" - 654.07680000000004838512.send(@method).should == "654.0768" - 6.07423700000000010846.send(@method).should == "6.074237" - 102.25779799999999397642.send(@method).should == "102.257798" - 5.08129999999999970584.send(@method).should == "5.0813" - 6.00000000000000000000.send(@method).should == "6.0" - 8.30000000000000071054.send(@method).should == "8.3" - 32.68345999999999662577.send(@method).should == "32.68346" - 581.11170000000004165486.send(@method).should == "581.1117" - 76.31342999999999676675.send(@method).should == "76.31343" - 438.30826000000001840817.send(@method).should == "438.30826" - 482.06631994000002805478.send(@method).should == "482.06631994" - 55.92721026899999969828.send(@method).should == "55.927210269" - 4.00000000000000000000.send(@method).should == "4.0" - 55.86693999999999959982.send(@method).should == "55.86694" - 787.98299999999994724931.send(@method).should == "787.983" - 5.73810511000000023074.send(@method).should == "5.73810511" - 74.51926810000000500622.send(@method).should == "74.5192681" - 892.89999999999997726263.send(@method).should == "892.9" - 68.27299999999999613465.send(@method).should == "68.273" - 904.10000000000002273737.send(@method).should == "904.1" - 5.23200000000000020606.send(@method).should == "5.232" - 4.09628000000000014325.send(@method).should == "4.09628" - 46.05152633699999853434.send(@method).should == "46.051526337" - 142.12884990599999923688.send(@method).should == "142.128849906" - 3.83057023500000015659.send(@method).should == "3.830570235" - 11.81684594699999912848.send(@method).should == "11.816845947" - 80.50000000000000000000.send(@method).should == "80.5" - 382.18215010000000120272.send(@method).should == "382.1821501" - 55.38444606899999911320.send(@method).should == "55.384446069" - 5.78000000000000024869.send(@method).should == "5.78" - 2.88244999999999995666.send(@method).should == "2.88245" - 43.27709999999999723741.send(@method).should == "43.2771" - end - - it "random values from divisions" do - (1.0 / 7).send(@method).should == "0.14285714285714285" - - # 50.times do - # a = rand(10) - # b = rand(10) - # c = rand(10) - # d = rand(10) - # expression = "#{a}.#{b} / #{c}.#{d}" - # puts " (#{expression}).send(@method).should == #{eval(expression).send(@method).inspect}" - # end - - (1.1 / 7.1).send(@method).should == "0.15492957746478875" - (6.5 / 8.8).send(@method).should == "0.7386363636363635" - (4.8 / 4.3).send(@method).should == "1.1162790697674418" - (4.0 / 1.9).send(@method).should == "2.1052631578947367" - (9.1 / 0.8).send(@method).should == "11.374999999999998" - (5.3 / 7.5).send(@method).should == "0.7066666666666667" - (2.8 / 1.8).send(@method).should == "1.5555555555555554" - (2.1 / 2.5).send(@method).should == "0.8400000000000001" - (3.5 / 6.0).send(@method).should == "0.5833333333333334" - (4.6 / 0.3).send(@method).should == "15.333333333333332" - (0.6 / 2.4).send(@method).should == "0.25" - (1.3 / 9.1).send(@method).should == "0.14285714285714288" - (0.3 / 5.0).send(@method).should == "0.06" - (5.0 / 4.2).send(@method).should == "1.1904761904761905" - (3.0 / 2.0).send(@method).should == "1.5" - (6.3 / 2.0).send(@method).should == "3.15" - (5.4 / 6.0).send(@method).should == "0.9" - (9.6 / 8.1).send(@method).should == "1.1851851851851851" - (8.7 / 1.6).send(@method).should == "5.437499999999999" - (1.9 / 7.8).send(@method).should == "0.24358974358974358" - (0.5 / 2.1).send(@method).should == "0.23809523809523808" - (9.3 / 5.8).send(@method).should == "1.6034482758620692" - (2.7 / 8.0).send(@method).should == "0.3375" - (9.7 / 7.8).send(@method).should == "1.2435897435897436" - (8.1 / 2.4).send(@method).should == "3.375" - (7.7 / 2.7).send(@method).should == "2.8518518518518516" - (7.9 / 1.7).send(@method).should == "4.647058823529412" - (6.5 / 8.2).send(@method).should == "0.7926829268292683" - (7.8 / 9.6).send(@method).should == "0.8125" - (2.2 / 4.6).send(@method).should == "0.47826086956521746" - (0.0 / 1.0).send(@method).should == "0.0" - (8.3 / 2.9).send(@method).should == "2.8620689655172415" - (3.1 / 6.1).send(@method).should == "0.5081967213114754" - (2.8 / 7.8).send(@method).should == "0.358974358974359" - (8.0 / 0.1).send(@method).should == "80.0" - (1.7 / 6.4).send(@method).should == "0.265625" - (1.8 / 5.4).send(@method).should == "0.3333333333333333" - (8.0 / 5.8).send(@method).should == "1.3793103448275863" - (5.2 / 4.1).send(@method).should == "1.2682926829268295" - (9.8 / 5.8).send(@method).should == "1.6896551724137934" - (5.4 / 9.5).send(@method).should == "0.5684210526315789" - (8.4 / 4.9).send(@method).should == "1.7142857142857142" - (1.7 / 3.5).send(@method).should == "0.4857142857142857" - (1.2 / 5.1).send(@method).should == "0.23529411764705882" - (1.4 / 2.0).send(@method).should == "0.7" - (4.8 / 8.0).send(@method).should == "0.6" - (9.0 / 2.5).send(@method).should == "3.6" - (0.2 / 0.6).send(@method).should == "0.33333333333333337" - (7.8 / 5.2).send(@method).should == "1.5" - (9.5 / 5.5).send(@method).should == "1.7272727272727273" - end - end - - describe 'encoding' do - before :each do - @internal = Encoding.default_internal - end - - after :each do - Encoding.default_internal = @internal - end - - it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do - Encoding.default_internal = nil - 1.23.send(@method).encoding.should.equal?(Encoding::US_ASCII) - end - - it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do - Encoding.default_internal = Encoding::IBM437 - 5.47.send(@method).encoding.should.equal?(Encoding::US_ASCII) - end - end -end diff --git a/spec/ruby/core/float/to_int_spec.rb b/spec/ruby/core/float/to_int_spec.rb index 084a58b431..ff70d508ff 100644 --- a/spec/ruby/core/float/to_int_spec.rb +++ b/spec/ruby/core/float/to_int_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_i' describe "Float#to_int" do - it_behaves_like :float_to_i, :to_int + it "is an alias of Float#to_i" do + Float.instance_method(:to_int).should == Float.instance_method(:to_i) + end end diff --git a/spec/ruby/core/float/to_s_spec.rb b/spec/ruby/core/float/to_s_spec.rb index 6727a883f8..3fd64581c2 100644 --- a/spec/ruby/core/float/to_s_spec.rb +++ b/spec/ruby/core/float/to_s_spec.rb @@ -1,6 +1,310 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' describe "Float#to_s" do - it_behaves_like :float_to_s, :to_s + it "returns 'NaN' for NaN" do + nan_value().to_s.should == 'NaN' + end + + it "returns 'Infinity' for positive infinity" do + infinity_value().to_s.should == 'Infinity' + end + + it "returns '-Infinity' for negative infinity" do + (-infinity_value()).to_s.should == '-Infinity' + end + + it "returns '0.0' for 0.0" do + 0.0.to_s.should == "0.0" + end + + platform_is_not :openbsd do + it "emits '-' for -0.0" do + -0.0.to_s.should == "-0.0" + end + end + + it "emits a '-' for negative values" do + -3.14.to_s.should == "-3.14" + end + + it "emits a trailing '.0' for a whole number" do + 50.0.to_s.should == "50.0" + end + + it "emits a trailing '.0' for the mantissa in e format" do + 1.0e20.to_s.should == "1.0e+20" + end + + it "uses non-e format for a positive value with fractional part having 5 significant figures" do + 0.0001.to_s.should == "0.0001" + end + + it "uses non-e format for a negative value with fractional part having 5 significant figures" do + -0.0001.to_s.should == "-0.0001" + end + + it "uses e format for a positive value with fractional part having 6 significant figures" do + 0.00001.to_s.should == "1.0e-05" + end + + it "uses e format for a negative value with fractional part having 6 significant figures" do + -0.00001.to_s.should == "-1.0e-05" + end + + it "uses non-e format for a positive value with whole part having 15 significant figures" do + 10000000000000.0.to_s.should == "10000000000000.0" + end + + it "uses non-e format for a negative value with whole part having 15 significant figures" do + -10000000000000.0.to_s.should == "-10000000000000.0" + end + + it "uses non-e format for a positive value with whole part having 16 significant figures" do + 100000000000000.0.to_s.should == "100000000000000.0" + end + + it "uses non-e format for a negative value with whole part having 16 significant figures" do + -100000000000000.0.to_s.should == "-100000000000000.0" + end + + it "uses e format for a positive value with whole part having 18 significant figures" do + 10000000000000000.0.to_s.should == "1.0e+16" + end + + it "uses e format for a negative value with whole part having 18 significant figures" do + -10000000000000000.0.to_s.should == "-1.0e+16" + end + + it "uses e format for a positive value with whole part having 17 significant figures" do + 1000000000000000.0.to_s.should == "1.0e+15" + end + + it "uses e format for a negative value with whole part having 17 significant figures" do + -1000000000000000.0.to_s.should == "-1.0e+15" + end + + # #3273 + it "outputs the minimal, unique form necessary to recreate the value" do + value = 0.21611564636388508 + string = "0.21611564636388508" + + value.to_s.should == string + string.to_f.should == value + end + + it "outputs the minimal, unique form to represent the value" do + 0.56.to_s.should == "0.56" + end + + describe "matches" do + it "random examples in all ranges" do + # 50.times do + # bytes = (0...8).map { rand(256) } + # string = bytes.pack('C8') + # float = string.unpack('D').first + # puts "#{'%.20g' % float}.to_s.should == #{float.to_s.inspect}" + # end + + 2.5540217314354050325e+163.to_s.should == "2.554021731435405e+163" + 2.5492588360356597544e-172.to_s.should == "2.5492588360356598e-172" + 1.742770260934704852e-82.to_s.should == "1.7427702609347049e-82" + 6.2108093676180883209e-104.to_s.should == "6.210809367618088e-104" + -3.3448803488331067402e-143.to_s.should == "-3.3448803488331067e-143" + -2.2740074343500832557e-168.to_s.should == "-2.2740074343500833e-168" + 7.0587971678048535732e+191.to_s.should == "7.058797167804854e+191" + -284438.88327586348169.to_s.should == "-284438.8832758635" + 3.953272468476091301e+105.to_s.should == "3.9532724684760913e+105" + -3.6361359552959847853e+100.to_s.should == "-3.636135955295985e+100" + -1.3222325865575206185e-31.to_s.should == "-1.3222325865575206e-31" + 1.1440138916932761366e+130.to_s.should == "1.1440138916932761e+130" + 4.8750891560387561157e-286.to_s.should == "4.875089156038756e-286" + 5.6101113356591453525e-257.to_s.should == "5.610111335659145e-257" + -3.829644279545809575e-100.to_s.should == "-3.8296442795458096e-100" + 1.5342839401396406117e-194.to_s.should == "1.5342839401396406e-194" + 2.2284972755169921402e-144.to_s.should == "2.228497275516992e-144" + 2.1825655917065601737e-61.to_s.should == "2.1825655917065602e-61" + -2.6672271363524338322e-62.to_s.should == "-2.667227136352434e-62" + -1.9257995160119059415e+21.to_s.should == "-1.925799516011906e+21" + -8.9096732962887121718e-198.to_s.should == "-8.909673296288712e-198" + 2.0202075376548644959e-90.to_s.should == "2.0202075376548645e-90" + -7.7341602581786258961e-266.to_s.should == "-7.734160258178626e-266" + 3.5134482598733635046e+98.to_s.should == "3.5134482598733635e+98" + -2.124411722371029134e+154.to_s.should == "-2.124411722371029e+154" + -4.573908787355718687e+110.to_s.should == "-4.573908787355719e+110" + -1.9344425934170969879e-232.to_s.should == "-1.934442593417097e-232" + -1.3274227399979271095e+171.to_s.should == "-1.3274227399979271e+171" + 9.3495270482104442383e-283.to_s.should == "9.349527048210444e-283" + -4.2046059371986483233e+307.to_s.should == "-4.2046059371986483e+307" + 3.6133547278583543004e-117.to_s.should == "3.613354727858354e-117" + 4.9247416523566613499e-08.to_s.should == "4.9247416523566613e-08" + 1.6936145488250064007e-71.to_s.should == "1.6936145488250064e-71" + 2.4455483206829433098e+96.to_s.should == "2.4455483206829433e+96" + 7.9797449851436455384e+124.to_s.should == "7.979744985143646e+124" + -1.3873689634457876774e-129.to_s.should == "-1.3873689634457877e-129" + 3.9761102037533483075e+284.to_s.should == "3.976110203753348e+284" + -4.2819791952139402486e-303.to_s.should == "-4.28197919521394e-303" + -5.7981017546689831298e-116.to_s.should == "-5.798101754668983e-116" + -3.953266497860534199e-28.to_s.should == "-3.953266497860534e-28" + -2.0659852720290440959e-243.to_s.should == "-2.065985272029044e-243" + 8.9670488995878688018e-05.to_s.should == "8.967048899587869e-05" + -1.2317943708113061768e-98.to_s.should == "-1.2317943708113062e-98" + -3.8930768307633080463e+248.to_s.should == "-3.893076830763308e+248" + 6.5854032671803925627e-239.to_s.should == "6.5854032671803926e-239" + 4.6257022188980878952e+177.to_s.should == "4.625702218898088e+177" + -1.9397155125507235603e-187.to_s.should == "-1.9397155125507236e-187" + 8.5752156951245705056e+117.to_s.should == "8.57521569512457e+117" + -2.4784875958162501671e-132.to_s.should == "-2.4784875958162502e-132" + -4.4125691841230058457e-203.to_s.should == "-4.412569184123006e-203" + end + + it "random examples in human ranges" do + # 50.times do + # formatted = '' + # rand(1..3).times do + # formatted << rand(10).to_s + # end + # formatted << '.' + # rand(1..9).times do + # formatted << rand(10).to_s + # end + # float = formatted.to_f + # puts "#{'%.20f' % float}.to_s.should == #{float.to_s.inspect}" + # end + + 5.17869899999999994122.to_s.should == "5.178699" + 905.62695729999995819526.to_s.should == "905.6269573" + 62.75999999999999801048.to_s.should == "62.76" + 6.93856795800000014651.to_s.should == "6.938567958" + 4.95999999999999996447.to_s.should == "4.96" + 32.77993899999999882766.to_s.should == "32.779939" + 544.12756779999995160324.to_s.should == "544.1275678" + 66.25801119999999855281.to_s.should == "66.2580112" + 7.90000000000000035527.to_s.should == "7.9" + 5.93100000000000004974.to_s.should == "5.931" + 5.21229313600000043749.to_s.should == "5.212293136" + 503.44173809000000119340.to_s.should == "503.44173809" + 79.26000000000000511591.to_s.should == "79.26" + 8.51524999999999998579.to_s.should == "8.51525" + 174.00000000000000000000.to_s.should == "174.0" + 50.39580000000000126192.to_s.should == "50.3958" + 35.28999999999999914735.to_s.should == "35.29" + 5.43136675399999990788.to_s.should == "5.431366754" + 654.07680000000004838512.to_s.should == "654.0768" + 6.07423700000000010846.to_s.should == "6.074237" + 102.25779799999999397642.to_s.should == "102.257798" + 5.08129999999999970584.to_s.should == "5.0813" + 6.00000000000000000000.to_s.should == "6.0" + 8.30000000000000071054.to_s.should == "8.3" + 32.68345999999999662577.to_s.should == "32.68346" + 581.11170000000004165486.to_s.should == "581.1117" + 76.31342999999999676675.to_s.should == "76.31343" + 438.30826000000001840817.to_s.should == "438.30826" + 482.06631994000002805478.to_s.should == "482.06631994" + 55.92721026899999969828.to_s.should == "55.927210269" + 4.00000000000000000000.to_s.should == "4.0" + 55.86693999999999959982.to_s.should == "55.86694" + 787.98299999999994724931.to_s.should == "787.983" + 5.73810511000000023074.to_s.should == "5.73810511" + 74.51926810000000500622.to_s.should == "74.5192681" + 892.89999999999997726263.to_s.should == "892.9" + 68.27299999999999613465.to_s.should == "68.273" + 904.10000000000002273737.to_s.should == "904.1" + 5.23200000000000020606.to_s.should == "5.232" + 4.09628000000000014325.to_s.should == "4.09628" + 46.05152633699999853434.to_s.should == "46.051526337" + 142.12884990599999923688.to_s.should == "142.128849906" + 3.83057023500000015659.to_s.should == "3.830570235" + 11.81684594699999912848.to_s.should == "11.816845947" + 80.50000000000000000000.to_s.should == "80.5" + 382.18215010000000120272.to_s.should == "382.1821501" + 55.38444606899999911320.to_s.should == "55.384446069" + 5.78000000000000024869.to_s.should == "5.78" + 2.88244999999999995666.to_s.should == "2.88245" + 43.27709999999999723741.to_s.should == "43.2771" + end + + it "random values from divisions" do + (1.0 / 7).to_s.should == "0.14285714285714285" + + # 50.times do + # a = rand(10) + # b = rand(10) + # c = rand(10) + # d = rand(10) + # expression = "#{a}.#{b} / #{c}.#{d}" + # puts " (#{expression}).to_s.should == #{eval(expression).to_s.inspect}" + # end + + (1.1 / 7.1).to_s.should == "0.15492957746478875" + (6.5 / 8.8).to_s.should == "0.7386363636363635" + (4.8 / 4.3).to_s.should == "1.1162790697674418" + (4.0 / 1.9).to_s.should == "2.1052631578947367" + (9.1 / 0.8).to_s.should == "11.374999999999998" + (5.3 / 7.5).to_s.should == "0.7066666666666667" + (2.8 / 1.8).to_s.should == "1.5555555555555554" + (2.1 / 2.5).to_s.should == "0.8400000000000001" + (3.5 / 6.0).to_s.should == "0.5833333333333334" + (4.6 / 0.3).to_s.should == "15.333333333333332" + (0.6 / 2.4).to_s.should == "0.25" + (1.3 / 9.1).to_s.should == "0.14285714285714288" + (0.3 / 5.0).to_s.should == "0.06" + (5.0 / 4.2).to_s.should == "1.1904761904761905" + (3.0 / 2.0).to_s.should == "1.5" + (6.3 / 2.0).to_s.should == "3.15" + (5.4 / 6.0).to_s.should == "0.9" + (9.6 / 8.1).to_s.should == "1.1851851851851851" + (8.7 / 1.6).to_s.should == "5.437499999999999" + (1.9 / 7.8).to_s.should == "0.24358974358974358" + (0.5 / 2.1).to_s.should == "0.23809523809523808" + (9.3 / 5.8).to_s.should == "1.6034482758620692" + (2.7 / 8.0).to_s.should == "0.3375" + (9.7 / 7.8).to_s.should == "1.2435897435897436" + (8.1 / 2.4).to_s.should == "3.375" + (7.7 / 2.7).to_s.should == "2.8518518518518516" + (7.9 / 1.7).to_s.should == "4.647058823529412" + (6.5 / 8.2).to_s.should == "0.7926829268292683" + (7.8 / 9.6).to_s.should == "0.8125" + (2.2 / 4.6).to_s.should == "0.47826086956521746" + (0.0 / 1.0).to_s.should == "0.0" + (8.3 / 2.9).to_s.should == "2.8620689655172415" + (3.1 / 6.1).to_s.should == "0.5081967213114754" + (2.8 / 7.8).to_s.should == "0.358974358974359" + (8.0 / 0.1).to_s.should == "80.0" + (1.7 / 6.4).to_s.should == "0.265625" + (1.8 / 5.4).to_s.should == "0.3333333333333333" + (8.0 / 5.8).to_s.should == "1.3793103448275863" + (5.2 / 4.1).to_s.should == "1.2682926829268295" + (9.8 / 5.8).to_s.should == "1.6896551724137934" + (5.4 / 9.5).to_s.should == "0.5684210526315789" + (8.4 / 4.9).to_s.should == "1.7142857142857142" + (1.7 / 3.5).to_s.should == "0.4857142857142857" + (1.2 / 5.1).to_s.should == "0.23529411764705882" + (1.4 / 2.0).to_s.should == "0.7" + (4.8 / 8.0).to_s.should == "0.6" + (9.0 / 2.5).to_s.should == "3.6" + (0.2 / 0.6).to_s.should == "0.33333333333333337" + (7.8 / 5.2).to_s.should == "1.5" + (9.5 / 5.5).to_s.should == "1.7272727272727273" + end + end + + describe 'encoding' do + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + 1.23.to_s.encoding.should.equal?(Encoding::US_ASCII) + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do + Encoding.default_internal = Encoding::IBM437 + 5.47.to_s.encoding.should.equal?(Encoding::US_ASCII) + end + end end diff --git a/spec/ruby/core/hash/has_value_spec.rb b/spec/ruby/core/hash/has_value_spec.rb index d40e52ebfd..95b8390a66 100644 --- a/spec/ruby/core/hash/has_value_spec.rb +++ b/spec/ruby/core/hash/has_value_spec.rb @@ -1,16 +1,7 @@ require_relative '../../spec_helper' describe "Hash#has_value?" do - it "returns true if the value exists in the hash" do - { a: :b }.has_value?(:a).should == false - { 1 => 2 }.has_value?(2).should == true - h = Hash.new(5) - h.has_value?(5).should == false - h = Hash.new { 5 } - h.has_value?(5).should == false - end - - it "uses == semantics for comparing values" do - { 5 => 2.0 }.has_value?(2).should == true + it "is an alias of Hash#value?" do + Hash.instance_method(:has_value?).should == Hash.instance_method(:value?) end end diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb index 9e566fcee9..7a444f7f25 100644 --- a/spec/ruby/core/hash/merge_spec.rb +++ b/spec/ruby/core/hash/merge_spec.rb @@ -117,7 +117,78 @@ describe "Hash#merge" do end describe "Hash#merge!" do - it "is an alias of Hash#update" do - Hash.instance_method(:merge!).should == Hash.instance_method(:update) + it "adds the entries from other, overwriting duplicate keys. Returns self" do + h = { _1: 'a', _2: '3' } + h.merge!(_1: '9', _9: 2).should.equal?(h) + h.should == { _1: "9", _2: "3", _9: 2 } + end + + it "sets any duplicate key to the value of block if passed a block" do + h1 = { a: 2, b: -1 } + h2 = { a: -2, c: 1 } + h1.merge!(h2) { |k,x,y| 3.14 }.should.equal?(h1) + h1.should == { c: 1, b: -1, a: 3.14 } + + h1.merge!(h1) { nil } + h1.should == { a: nil, b: nil, c: nil } + end + + it "tries to convert the passed argument to a hash using #to_hash" do + obj = mock('{1=>2}') + obj.should_receive(:to_hash).and_return({ 1 => 2 }) + { 3 => 4 }.merge!(obj).should == { 1 => 2, 3 => 4 } + end + + it "does not call to_hash on hash subclasses" do + { 3 => 4 }.merge!(HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 } + end + + it "processes entries with same order as merge()" do + h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] } + merge_bang_pairs = [] + merge_pairs = [] + h.merge(h) { |*arg| merge_pairs << arg } + h.merge!(h) { |*arg| merge_bang_pairs << arg } + merge_bang_pairs.should == merge_pairs + end + + it "raises a FrozenError on a frozen instance that is modified" do + -> do + HashSpecs.frozen_hash.merge!(1 => 2) + end.should.raise(FrozenError) + end + + it "checks frozen status before coercing an object with #to_hash" do + obj = mock("to_hash frozen") + # This is necessary because mock cleanup code cannot run on the frozen + # object. + def obj.to_hash() raise Exception, "should not receive #to_hash" end + obj.freeze + + -> { HashSpecs.frozen_hash.merge!(obj) }.should.raise(FrozenError) + end + + # see redmine #1571 + it "raises a FrozenError on a frozen instance that would not be modified" do + -> do + HashSpecs.frozen_hash.merge!(HashSpecs.empty_frozen_hash) + end.should.raise(FrozenError) + end + + it "does not raise an exception if changing the value of an existing key during iteration" do + hash = {1 => 2, 3 => 4, 5 => 6} + hash2 = {1 => :foo, 3 => :bar} + hash.each { hash.merge!(hash2) } + hash.should == {1 => :foo, 3 => :bar, 5 => 6} + end + + it "accepts multiple hashes" do + result = { a: 1 }.merge!({ b: 2 }, { c: 3 }, { d: 4 }) + result.should == { a: 1, b: 2, c: 3, d: 4 } + end + + it "accepts zero arguments" do + hash = { a: 1 } + hash.merge!.should.eql?(hash) end end diff --git a/spec/ruby/core/hash/update_spec.rb b/spec/ruby/core/hash/update_spec.rb index f3a3e6b4db..04070baad8 100644 --- a/spec/ruby/core/hash/update_spec.rb +++ b/spec/ruby/core/hash/update_spec.rb @@ -1,79 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' describe "Hash#update" do - it "adds the entries from other, overwriting duplicate keys. Returns self" do - h = { _1: 'a', _2: '3' } - h.update(_1: '9', _9: 2).should.equal?(h) - h.should == { _1: "9", _2: "3", _9: 2 } - end - - it "sets any duplicate key to the value of block if passed a block" do - h1 = { a: 2, b: -1 } - h2 = { a: -2, c: 1 } - h1.update(h2) { |k,x,y| 3.14 }.should.equal?(h1) - h1.should == { c: 1, b: -1, a: 3.14 } - - h1.update(h1) { nil } - h1.should == { a: nil, b: nil, c: nil } - end - - it "tries to convert the passed argument to a hash using #to_hash" do - obj = mock('{1=>2}') - obj.should_receive(:to_hash).and_return({ 1 => 2 }) - { 3 => 4 }.update(obj).should == { 1 => 2, 3 => 4 } - end - - it "does not call to_hash on hash subclasses" do - { 3 => 4 }.update(HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 } - end - - it "processes entries with same order as merge()" do - h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] } - merge_bang_pairs = [] - merge_pairs = [] - h.merge(h) { |*arg| merge_pairs << arg } - h.update(h) { |*arg| merge_bang_pairs << arg } - merge_bang_pairs.should == merge_pairs - end - - it "raises a FrozenError on a frozen instance that is modified" do - -> do - HashSpecs.frozen_hash.update(1 => 2) - end.should.raise(FrozenError) - end - - it "checks frozen status before coercing an object with #to_hash" do - obj = mock("to_hash frozen") - # This is necessary because mock cleanup code cannot run on the frozen - # object. - def obj.to_hash() raise Exception, "should not receive #to_hash" end - obj.freeze - - -> { HashSpecs.frozen_hash.update(obj) }.should.raise(FrozenError) - end - - # see redmine #1571 - it "raises a FrozenError on a frozen instance that would not be modified" do - -> do - HashSpecs.frozen_hash.update(HashSpecs.empty_frozen_hash) - end.should.raise(FrozenError) - end - - it "does not raise an exception if changing the value of an existing key during iteration" do - hash = {1 => 2, 3 => 4, 5 => 6} - hash2 = {1 => :foo, 3 => :bar} - hash.each { hash.update(hash2) } - hash.should == {1 => :foo, 3 => :bar, 5 => 6} - end - - it "accepts multiple hashes" do - result = { a: 1 }.update({ b: 2 }, { c: 3 }, { d: 4 }) - result.should == { a: 1, b: 2, c: 3, d: 4 } - end - - it "accepts zero arguments" do - hash = { a: 1 } - hash.update.should.eql?(hash) + it "is an alias of Hash#merge!" do + Hash.instance_method(:update).should == Hash.instance_method(:merge!) end end diff --git a/spec/ruby/core/hash/value_spec.rb b/spec/ruby/core/hash/value_spec.rb index 9cfbe576d2..8e4732480f 100644 --- a/spec/ruby/core/hash/value_spec.rb +++ b/spec/ruby/core/hash/value_spec.rb @@ -1,7 +1,16 @@ require_relative '../../spec_helper' describe "Hash#value?" do - it "is an alias of Hash#has_value?" do - Hash.instance_method(:value?).should == Hash.instance_method(:has_value?) + it "returns true if the value exists in the hash" do + { a: :b }.value?(:a).should == false + { 1 => 2 }.value?(2).should == true + h = Hash.new(5) + h.value?(5).should == false + h = Hash.new { 5 } + h.value?(5).should == false + end + + it "uses == semantics for comparing values" do + { 5 => 2.0 }.value?(2).should == true end end diff --git a/spec/ruby/core/integer/abs_spec.rb b/spec/ruby/core/integer/abs_spec.rb index c40356db12..768eebdce2 100644 --- a/spec/ruby/core/integer/abs_spec.rb +++ b/spec/ruby/core/integer/abs_spec.rb @@ -1,6 +1,20 @@ require_relative '../../spec_helper' -require_relative 'shared/abs' describe "Integer#abs" do - it_behaves_like :integer_abs, :abs + context "fixnum" do + it "returns self's absolute fixnum value" do + { 0 => [0, -0, +0], 2 => [2, -2, +2], 100 => [100, -100, +100] }.each do |key, values| + values.each do |value| + value.abs.should == key + end + end + end + end + + context "bignum" do + it "returns the absolute bignum value" do + bignum_value(39).abs.should == 18446744073709551655 + (-bignum_value(18)).abs.should == 18446744073709551634 + end + end end diff --git a/spec/ruby/core/integer/case_compare_spec.rb b/spec/ruby/core/integer/case_compare_spec.rb index e5dde2c64a..1e0c6cb411 100644 --- a/spec/ruby/core/integer/case_compare_spec.rb +++ b/spec/ruby/core/integer/case_compare_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/equal' describe "Integer#===" do - it_behaves_like :integer_equal, :=== + it "is an alias of Integer#==" do + Integer.instance_method(:===).should == Integer.instance_method(:==) + end end diff --git a/spec/ruby/core/integer/equal_value_spec.rb b/spec/ruby/core/integer/equal_value_spec.rb index 67a73713af..dc73552267 100644 --- a/spec/ruby/core/integer/equal_value_spec.rb +++ b/spec/ruby/core/integer/equal_value_spec.rb @@ -1,6 +1,65 @@ require_relative '../../spec_helper' -require_relative 'shared/equal' describe "Integer#==" do - it_behaves_like :integer_equal, :== + context "fixnum" do + it "returns true if self has the same value as other" do + (1 == 1).should == true + (9 == 5).should == false + + # Actually, these call Float#==, Integer#== etc. + (9 == 9.0).should == true + (9 == 9.01).should == false + + (10 == bignum_value).should == false + end + + it "calls 'other == self' if the given argument is not an Integer" do + (1 == '*').should == false + + obj = mock('one other') + obj.should_receive(:==).any_number_of_times.and_return(false) + (1 == obj).should == false + + obj = mock('another') + obj.should_receive(:==).any_number_of_times.and_return(true) + (2 == obj).should == true + end + end + + context "bignum" do + before :each do + @bignum = bignum_value + end + + it "returns true if self has the same value as the given argument" do + (@bignum == @bignum).should == true + (@bignum == @bignum.to_f).should == true + + (@bignum == @bignum + 1).should == false + ((@bignum + 1) == @bignum).should == false + + (@bignum == 9).should == false + (@bignum == 9.01).should == false + + (@bignum == bignum_value(10)).should == false + end + + it "calls 'other == self' if the given argument is not an Integer" do + obj = mock('not integer') + obj.should_receive(:==).and_return(true) + (@bignum == obj).should == true + end + + it "returns the result of 'other == self' as a boolean" do + obj = mock('not integer') + obj.should_receive(:==).exactly(2).times.and_return("woot", nil) + (@bignum == obj).should == true + (@bignum == obj).should == false + end + + it "does not lose precision when comparing with a Float" do + ((bignum_value(1) == bignum_value.to_f)).should == false + ((bignum_value == bignum_value.to_f)).should == true + end + end end diff --git a/spec/ruby/core/integer/inspect_spec.rb b/spec/ruby/core/integer/inspect_spec.rb new file mode 100644 index 0000000000..0b0d5cc7a9 --- /dev/null +++ b/spec/ruby/core/integer/inspect_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Integer#inspect" do + it "is an alias of Integer#to_s" do + Integer.instance_method(:inspect).should == Integer.instance_method(:to_s) + end +end diff --git a/spec/ruby/core/integer/magnitude_spec.rb b/spec/ruby/core/integer/magnitude_spec.rb index 48cf1a8534..000e5be7f7 100644 --- a/spec/ruby/core/integer/magnitude_spec.rb +++ b/spec/ruby/core/integer/magnitude_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/abs' describe "Integer#magnitude" do - it_behaves_like :integer_abs, :magnitude + it "is an alias of Integer#abs" do + Integer.instance_method(:magnitude).should == Integer.instance_method(:abs) + end end diff --git a/spec/ruby/core/integer/modulo_spec.rb b/spec/ruby/core/integer/modulo_spec.rb index e263338e38..2680f49510 100644 --- a/spec/ruby/core/integer/modulo_spec.rb +++ b/spec/ruby/core/integer/modulo_spec.rb @@ -1,10 +1,122 @@ require_relative '../../spec_helper' -require_relative 'shared/modulo' describe "Integer#%" do - it_behaves_like :integer_modulo, :% + context "fixnum" do + it "returns the modulus obtained from dividing self by the given argument" do + # test all possible combinations: + # - integer/double/bignum argument + # - positive/negative argument + # - positive/negative self + # - self greater/smaller than argument + + (13 % 4).should == 1 + (4 % 13).should == 4 + + (13 % 4.0).should == 1 + (4 % 13.0).should == 4 + + (-200 % 256).should == 56 + (-1000 % 512).should == 24 + + (-200 % -256).should == -200 + (-1000 % -512).should == -488 + + (200 % -256).should == -56 + (1000 % -512).should == -24 + + (13 % -4.0).should == -3.0 + (4 % -13.0).should == -9.0 + + (-13 % -4.0).should == -1.0 + (-4 % -13.0).should == -4.0 + + (-13 % 4.0).should == 3.0 + (-4 % 13.0).should == 9.0 + + (1 % 2.0).should == 1.0 + (200 % bignum_value).should == 200 + + (4 % bignum_value(10)).should == 4 + (4 % -bignum_value(10)).should == -18446744073709551622 + (-4 % bignum_value(10)).should == 18446744073709551622 + (-4 % -bignum_value(10)).should == -4 + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { 13 % 0 }.should.raise(ZeroDivisionError) + -> { 0 % 0 }.should.raise(ZeroDivisionError) + -> { -10 % 0 }.should.raise(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { 0 % 0.0 }.should.raise(ZeroDivisionError) + -> { 10 % 0.0 }.should.raise(ZeroDivisionError) + -> { -10 % 0.0 }.should.raise(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + -> { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 % obj + }.should.raise(TypeError) + -> { 13 % "10" }.should.raise(TypeError) + -> { 13 % :symbol }.should.raise(TypeError) + end + end + + context "bignum" do + before :each do + @bignum = bignum_value(10) + end + + it "returns the modulus obtained from dividing self by the given argument" do + # test all possible combinations: + # - integer/double/bignum argument + # - positive/negative argument + # - positive/negative self + # - self greater/smaller than argument + + (@bignum % 5).should == 1 + (@bignum % -5).should == -4 + (-@bignum % 5).should == 4 + (-@bignum % -5).should == -1 + + (@bignum % 2.22).should be_close(1.5603603603605034, TOLERANCE) + (@bignum % -2.22).should be_close(-0.6596396396394968, TOLERANCE) + (-@bignum % 2.22).should be_close(0.6596396396394968, TOLERANCE) + (-@bignum % -2.22).should be_close(-1.5603603603605034, TOLERANCE) + + (@bignum % (@bignum + 10)).should == 18446744073709551626 + (@bignum % -(@bignum + 10)).should == -10 + (-@bignum % (@bignum + 10)).should == 10 + (-@bignum % -(@bignum + 10)).should == -18446744073709551626 + + ((@bignum + 10) % @bignum).should == 10 + ((@bignum + 10) % -@bignum).should == -18446744073709551616 + (-(@bignum + 10) % @bignum).should == 18446744073709551616 + (-(@bignum + 10) % -@bignum).should == -10 + end + + it "raises a ZeroDivisionError when the given argument is 0" do + -> { @bignum % 0 }.should.raise(ZeroDivisionError) + -> { -@bignum % 0 }.should.raise(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + -> { @bignum % 0.0 }.should.raise(ZeroDivisionError) + -> { -@bignum % 0.0 }.should.raise(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + -> { @bignum % mock('10') }.should.raise(TypeError) + -> { @bignum % "10" }.should.raise(TypeError) + -> { @bignum % :symbol }.should.raise(TypeError) + end + end end describe "Integer#modulo" do - it_behaves_like :integer_modulo, :modulo + it "is an alias of Integer#%" do + Integer.instance_method(:modulo).should == Integer.instance_method(:%) + end end diff --git a/spec/ruby/core/integer/next_spec.rb b/spec/ruby/core/integer/next_spec.rb index 63c4e67893..da54dec454 100644 --- a/spec/ruby/core/integer/next_spec.rb +++ b/spec/ruby/core/integer/next_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/next' describe "Integer#next" do - it_behaves_like :integer_next, :next + it "is an alias of Integer#succ" do + Integer.instance_method(:next).should == Integer.instance_method(:succ) + end end diff --git a/spec/ruby/core/integer/shared/abs.rb b/spec/ruby/core/integer/shared/abs.rb deleted file mode 100644 index 43844c9065..0000000000 --- a/spec/ruby/core/integer/shared/abs.rb +++ /dev/null @@ -1,18 +0,0 @@ -describe :integer_abs, shared: true do - context "fixnum" do - it "returns self's absolute fixnum value" do - { 0 => [0, -0, +0], 2 => [2, -2, +2], 100 => [100, -100, +100] }.each do |key, values| - values.each do |value| - value.send(@method).should == key - end - end - end - end - - context "bignum" do - it "returns the absolute bignum value" do - bignum_value(39).send(@method).should == 18446744073709551655 - (-bignum_value(18)).send(@method).should == 18446744073709551634 - end - end -end diff --git a/spec/ruby/core/integer/shared/equal.rb b/spec/ruby/core/integer/shared/equal.rb deleted file mode 100644 index c621ba3f81..0000000000 --- a/spec/ruby/core/integer/shared/equal.rb +++ /dev/null @@ -1,63 +0,0 @@ -describe :integer_equal, shared: true do - context "fixnum" do - it "returns true if self has the same value as other" do - 1.send(@method, 1).should == true - 9.send(@method, 5).should == false - - # Actually, these call Float#==, Integer#== etc. - 9.send(@method, 9.0).should == true - 9.send(@method, 9.01).should == false - - 10.send(@method, bignum_value).should == false - end - - it "calls 'other == self' if the given argument is not an Integer" do - 1.send(@method, '*').should == false - - obj = mock('one other') - obj.should_receive(:==).any_number_of_times.and_return(false) - 1.send(@method, obj).should == false - - obj = mock('another') - obj.should_receive(:==).any_number_of_times.and_return(true) - 2.send(@method, obj).should == true - end - end - - context "bignum" do - before :each do - @bignum = bignum_value - end - - it "returns true if self has the same value as the given argument" do - @bignum.send(@method, @bignum).should == true - @bignum.send(@method, @bignum.to_f).should == true - - @bignum.send(@method, @bignum + 1).should == false - (@bignum + 1).send(@method, @bignum).should == false - - @bignum.send(@method, 9).should == false - @bignum.send(@method, 9.01).should == false - - @bignum.send(@method, bignum_value(10)).should == false - end - - it "calls 'other == self' if the given argument is not an Integer" do - obj = mock('not integer') - obj.should_receive(:==).and_return(true) - @bignum.send(@method, obj).should == true - end - - it "returns the result of 'other == self' as a boolean" do - obj = mock('not integer') - obj.should_receive(:==).exactly(2).times.and_return("woot", nil) - @bignum.send(@method, obj).should == true - @bignum.send(@method, obj).should == false - end - - it "does not lose precision when comparing with a Float" do - (bignum_value(1).send(@method, bignum_value.to_f)).should == false - (bignum_value.send(@method, bignum_value.to_f)).should == true - end - end -end diff --git a/spec/ruby/core/integer/shared/modulo.rb b/spec/ruby/core/integer/shared/modulo.rb deleted file mode 100644 index d0b5e26ed5..0000000000 --- a/spec/ruby/core/integer/shared/modulo.rb +++ /dev/null @@ -1,114 +0,0 @@ -describe :integer_modulo, shared: true do - context "fixnum" do - it "returns the modulus obtained from dividing self by the given argument" do - # test all possible combinations: - # - integer/double/bignum argument - # - positive/negative argument - # - positive/negative self - # - self greater/smaller than argument - - 13.send(@method, 4).should == 1 - 4.send(@method, 13).should == 4 - - 13.send(@method, 4.0).should == 1 - 4.send(@method, 13.0).should == 4 - - (-200).send(@method, 256).should == 56 - (-1000).send(@method, 512).should == 24 - - (-200).send(@method, -256).should == -200 - (-1000).send(@method, -512).should == -488 - - (200).send(@method, -256).should == -56 - (1000).send(@method, -512).should == -24 - - 13.send(@method, -4.0).should == -3.0 - 4.send(@method, -13.0).should == -9.0 - - -13.send(@method, -4.0).should == -1.0 - -4.send(@method, -13.0).should == -4.0 - - -13.send(@method, 4.0).should == 3.0 - -4.send(@method, 13.0).should == 9.0 - - 1.send(@method, 2.0).should == 1.0 - 200.send(@method, bignum_value).should == 200 - - 4.send(@method, bignum_value(10)).should == 4 - 4.send(@method, -bignum_value(10)).should == -18446744073709551622 - -4.send(@method, bignum_value(10)).should == 18446744073709551622 - -4.send(@method, -bignum_value(10)).should == -4 - end - - it "raises a ZeroDivisionError when the given argument is 0" do - -> { 13.send(@method, 0) }.should.raise(ZeroDivisionError) - -> { 0.send(@method, 0) }.should.raise(ZeroDivisionError) - -> { -10.send(@method, 0) }.should.raise(ZeroDivisionError) - end - - it "raises a ZeroDivisionError when the given argument is 0 and a Float" do - -> { 0.send(@method, 0.0) }.should.raise(ZeroDivisionError) - -> { 10.send(@method, 0.0) }.should.raise(ZeroDivisionError) - -> { -10.send(@method, 0.0) }.should.raise(ZeroDivisionError) - end - - it "raises a TypeError when given a non-Integer" do - -> { - (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) - 13.send(@method, obj) - }.should.raise(TypeError) - -> { 13.send(@method, "10") }.should.raise(TypeError) - -> { 13.send(@method, :symbol) }.should.raise(TypeError) - end - end - - context "bignum" do - before :each do - @bignum = bignum_value(10) - end - - it "returns the modulus obtained from dividing self by the given argument" do - # test all possible combinations: - # - integer/double/bignum argument - # - positive/negative argument - # - positive/negative self - # - self greater/smaller than argument - - @bignum.send(@method, 5).should == 1 - @bignum.send(@method, -5).should == -4 - (-@bignum).send(@method, 5).should == 4 - (-@bignum).send(@method, -5).should == -1 - - @bignum.send(@method, 2.22).should be_close(1.5603603603605034, TOLERANCE) - @bignum.send(@method, -2.22).should be_close(-0.6596396396394968, TOLERANCE) - (-@bignum).send(@method, 2.22).should be_close(0.6596396396394968, TOLERANCE) - (-@bignum).send(@method, -2.22).should be_close(-1.5603603603605034, TOLERANCE) - - @bignum.send(@method, @bignum + 10).should == 18446744073709551626 - @bignum.send(@method, -(@bignum + 10)).should == -10 - (-@bignum).send(@method, @bignum + 10).should == 10 - (-@bignum).send(@method, -(@bignum + 10)).should == -18446744073709551626 - - (@bignum + 10).send(@method, @bignum).should == 10 - (@bignum + 10).send(@method, -@bignum).should == -18446744073709551616 - (-(@bignum + 10)).send(@method, @bignum).should == 18446744073709551616 - (-(@bignum + 10)).send(@method, -@bignum).should == -10 - end - - it "raises a ZeroDivisionError when the given argument is 0" do - -> { @bignum.send(@method, 0) }.should.raise(ZeroDivisionError) - -> { (-@bignum).send(@method, 0) }.should.raise(ZeroDivisionError) - end - - it "raises a ZeroDivisionError when the given argument is 0 and a Float" do - -> { @bignum.send(@method, 0.0) }.should.raise(ZeroDivisionError) - -> { -@bignum.send(@method, 0.0) }.should.raise(ZeroDivisionError) - end - - it "raises a TypeError when given a non-Integer" do - -> { @bignum.send(@method, mock('10')) }.should.raise(TypeError) - -> { @bignum.send(@method, "10") }.should.raise(TypeError) - -> { @bignum.send(@method, :symbol) }.should.raise(TypeError) - end - end -end diff --git a/spec/ruby/core/integer/shared/next.rb b/spec/ruby/core/integer/shared/next.rb deleted file mode 100644 index 85b83d6965..0000000000 --- a/spec/ruby/core/integer/shared/next.rb +++ /dev/null @@ -1,25 +0,0 @@ -describe :integer_next, shared: true do - it "returns the next larger positive Fixnum" do - 2.send(@method).should == 3 - end - - it "returns the next larger negative Fixnum" do - (-2).send(@method).should == -1 - end - - it "returns the next larger positive Bignum" do - bignum_value.send(@method).should == bignum_value(1) - end - - it "returns the next larger negative Bignum" do - (-bignum_value(1)).send(@method).should == -bignum_value - end - - it "overflows a Fixnum to a Bignum" do - fixnum_max.send(@method).should == fixnum_max + 1 - end - - it "underflows a Bignum to a Fixnum" do - (fixnum_min - 1).send(@method).should == fixnum_min - end -end diff --git a/spec/ruby/core/integer/succ_spec.rb b/spec/ruby/core/integer/succ_spec.rb index 9ae9a14fe7..2201a4c76d 100644 --- a/spec/ruby/core/integer/succ_spec.rb +++ b/spec/ruby/core/integer/succ_spec.rb @@ -1,6 +1,27 @@ require_relative '../../spec_helper' -require_relative 'shared/next' describe "Integer#succ" do - it_behaves_like :integer_next, :succ + it "returns the next larger positive Fixnum" do + 2.succ.should == 3 + end + + it "returns the next larger negative Fixnum" do + (-2).succ.should == -1 + end + + it "returns the next larger positive Bignum" do + bignum_value.succ.should == bignum_value(1) + end + + it "returns the next larger negative Bignum" do + (-bignum_value(1)).succ.should == -bignum_value + end + + it "overflows a Fixnum to a Bignum" do + fixnum_max.succ.should == fixnum_max + 1 + end + + it "underflows a Bignum to a Fixnum" do + (fixnum_min - 1).succ.should == fixnum_min + end end diff --git a/spec/ruby/core/io/buffer/map_spec.rb b/spec/ruby/core/io/buffer/map_spec.rb index 4b28539ad8..97764c2dd7 100644 --- a/spec/ruby/core/io/buffer/map_spec.rb +++ b/spec/ruby/core/io/buffer/map_spec.rb @@ -73,30 +73,34 @@ describe "IO::Buffer.map" do @buffer.should.valid? end - guard -> { Process.respond_to?(:fork) } do - it "is shareable across processes" do - file_name = tmp("shared_buffer") - @file = File.open(file_name, "w+") - @file << "I'm private" - @file.rewind - @buffer = IO::Buffer.map(@file) - - IO.popen("-") do |child_pipe| - if child_pipe - # Synchronize on child's output. - child_pipe.readlines.first.chomp.should == @buffer.to_s - @buffer.get_string.should == "I'm shared!" - - @file.read.should == "I'm shared!" - else - @buffer.set_string("I'm shared!") - puts @buffer + # IO::Buffer.map seems not shareable across processes on OpenBSD. + # See https://rubyci.s3.amazonaws.com/openbsd-current/ruby-master/log/20260129T163005Z.fail.html.gz + platform_is_not :openbsd do + guard -> { Process.respond_to?(:fork) } do + it "is shareable across processes" do + file_name = tmp("shared_buffer") + @file = File.open(file_name, "w+") + @file << "I'm private" + @file.rewind + @buffer = IO::Buffer.map(@file) + + IO.popen("-") do |child_pipe| + if child_pipe + # Synchronize on child's output. + child_pipe.readlines.first.chomp.should == @buffer.to_s + @buffer.get_string.should == "I'm shared!" + + @file.read.should == "I'm shared!" + else + @buffer.set_string("I'm shared!") + puts @buffer + end + ensure + child_pipe&.close end ensure - child_pipe&.close + File.unlink(file_name) end - ensure - File.unlink(file_name) end end diff --git a/spec/ruby/core/io/each_char_spec.rb b/spec/ruby/core/io/each_char_spec.rb index 5d460a1e7c..7c1ca4f069 100644 --- a/spec/ruby/core/io/each_char_spec.rb +++ b/spec/ruby/core/io/each_char_spec.rb @@ -1,12 +1,75 @@ -# -*- encoding: utf-8 -*- require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/chars' describe "IO#each_char" do - it_behaves_like :io_chars, :each_char + before :each do + @io = IOSpecs.io_fixture "lines.txt" + ScratchPad.record [] + end + + after :each do + @io.close unless @io.closed? + end + + it "yields each character" do + @io.readline.should == "Voici la ligne une.\n" + + count = 0 + @io.each_char do |c| + ScratchPad << c + break if 4 < count += 1 + end + + ScratchPad.recorded.should == ["Q", "u", "i", " ", "è"] + end + + describe "when no block is given" do + it "returns an Enumerator" do + enum = @io.each_char + enum.should.instance_of?(Enumerator) + enum.first(5).should == ["V", "o", "i", "c", "i"] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.each_char.size.should == nil + end + end + end + end + + it "returns itself" do + @io.each_char { |c| }.should.equal?(@io) + end + + it "returns an enumerator for a closed stream" do + IOSpecs.closed_io.each_char.should.instance_of?(Enumerator) + end + + it "raises an IOError when an enumerator created on a closed stream is accessed" do + -> { IOSpecs.closed_io.each_char.first }.should.raise(IOError) + end + + it "raises IOError on closed stream" do + -> { IOSpecs.closed_io.each_char {} }.should.raise(IOError) + end end describe "IO#each_char" do - it_behaves_like :io_chars_empty, :each_char + before :each do + @name = tmp("io_each_char") + @io = new_io @name, "w+:utf-8" + ScratchPad.record [] + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "does not yield any characters on an empty stream" do + @io.each_char { |c| ScratchPad << c } + ScratchPad.recorded.should == [] + end end diff --git a/spec/ruby/core/io/each_codepoint_spec.rb b/spec/ruby/core/io/each_codepoint_spec.rb index 26cc87fc0e..758a524986 100644 --- a/spec/ruby/core/io/each_codepoint_spec.rb +++ b/spec/ruby/core/io/each_codepoint_spec.rb @@ -1,10 +1,57 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/codepoints' # See redmine #1667 describe "IO#each_codepoint" do - it_behaves_like :io_codepoints, :each_codepoint + before :each do + @io = IOSpecs.io_fixture "lines.txt" + @enum = @io.each_codepoint + end + + after :each do + @io.close + end + + describe "when no block is given" do + it "returns an Enumerator" do + @enum.should.instance_of?(Enumerator) + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @enum.size.should == nil + end + end + end + end + + it "yields each codepoint" do + @enum.first(25).should == [ + 86, 111, 105, 99, 105, 32, 108, 97, 32, 108, 105, 103, 110, + 101, 32, 117, 110, 101, 46, 10, 81, 117, 105, 32, 232 + ] + end + + it "yields each codepoint starting from the current position" do + @io.pos = 130 + @enum.to_a.should == [101, 32, 115, 105, 120, 46, 10] + end + + it "raises an error if reading invalid sequence" do + @io.pos = 60 # inside of a multibyte sequence + -> { @enum.first }.should.raise(ArgumentError) + end + + it "does not change $_" do + $_ = "test" + @enum.to_a + $_.should == "test" + end + + it "raises an IOError when self is not readable" do + -> { IOSpecs.closed_io.each_codepoint.to_a }.should.raise(IOError) + end end describe "IO#each_codepoint" do diff --git a/spec/ruby/core/io/each_line_spec.rb b/spec/ruby/core/io/each_line_spec.rb index 58d26b325d..bcda4040b8 100644 --- a/spec/ruby/core/io/each_line_spec.rb +++ b/spec/ruby/core/io/each_line_spec.rb @@ -1,11 +1,251 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/each' describe "IO#each_line" do - it_behaves_like :io_each, :each_line + before :each do + @io = IOSpecs.io_fixture "lines.txt" + ScratchPad.record [] + end + + after :each do + @io.close if @io + end + + describe "with no separator" do + it "yields each line to the passed block" do + @io.each_line { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines + end + + it "yields each line starting from the current position" do + @io.pos = 41 + @io.each_line { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines[2..-1] + end + + it "returns self" do + @io.each_line { |l| l }.should.equal?(@io) + end + + it "does not change $_" do + $_ = "test" + @io.each_line { |s| s } + $_.should == "test" + end + + it "raises an IOError when self is not readable" do + -> { IOSpecs.closed_io.each_line {} }.should.raise(IOError) + end + + it "makes line count accessible via lineno" do + @io.each_line { ScratchPad << @io.lineno } + ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ] + end + + it "makes line count accessible via $." do + @io.each_line { ScratchPad << $. } + ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ] + end + + describe "when no block is given" do + it "returns an Enumerator" do + enum = @io.each_line + enum.should.instance_of?(Enumerator) + + enum.each { |l| ScratchPad << l } + ScratchPad.recorded.should == IOSpecs.lines + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.each_line.size.should == nil + end + end + end + end + end + + describe "with limit" do + describe "when limit is 0" do + it "raises an ArgumentError" do + # must pass block so Enumerator is evaluated and raises + -> { @io.each_line(0){} }.should.raise(ArgumentError) + end + end + + it "does not accept Integers that don't fit in a C off_t" do + -> { @io.each_line(2**128){} }.should.raise(RangeError) + end + end + + describe "when passed a String containing one space as a separator" do + it "uses the passed argument as the line separator" do + @io.each_line(" ") { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_space_separator + end + + it "does not change $_" do + $_ = "test" + @io.each_line(" ") { |s| } + $_.should == "test" + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(" ") + + @io.each_line(obj) { |l| ScratchPad << l } + ScratchPad.recorded.should == IOSpecs.lines_space_separator + end + end + + describe "when passed nil as a separator" do + it "yields self's content starting from the current position when the passed separator is nil" do + @io.pos = 100 + @io.each_line(nil) { |s| ScratchPad << s } + ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] + end + end + + describe "when passed an empty String as a separator" do + it "yields each paragraph" do + @io.each_line("") { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs + end + + it "discards leading newlines" do + @io.readline + @io.readline + @io.each_line("") { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1] + end + end + + describe "with both separator and limit" do + describe "when no block is given" do + it "returns an Enumerator" do + enum = @io.each_line(nil, 1024) + enum.should.instance_of?(Enumerator) + + enum.each { |l| ScratchPad << l } + ScratchPad.recorded.should == [IOSpecs.lines.join] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.each_line(nil, 1024).size.should == nil + end + end + end + end + + describe "when a block is given" do + it "accepts an empty block" do + @io.each_line(nil, 1024) {}.should.equal?(@io) + end + + describe "when passed nil as a separator" do + it "yields self's content starting from the current position when the passed separator is nil" do + @io.pos = 100 + @io.each_line(nil, 1024) { |s| ScratchPad << s } + ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] + end + end + + describe "when passed an empty String as a separator" do + it "yields each paragraph" do + @io.each_line("", 1024) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs + end + + it "discards leading newlines" do + @io.readline + @io.readline + @io.each_line("", 1024) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1] + end + end + end + end + + describe "when passed chomp" do + it "yields each line without trailing newline characters to the passed block" do + @io.each_line(chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters + end + + it "raises exception when options passed as Hash" do + -> { + @io.each_line({ chomp: true }) { |s| } + }.should.raise(TypeError) + + -> { + @io.each_line("\n", 1, { chomp: true }) { |s| } + }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 0..2)") + end + end + + describe "when passed chomp and a separator" do + it "yields each line without separator to the passed block" do + @io.each_line(" ", chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_space_separator_without_trailing_spaces + end + end + + describe "when passed chomp and empty line as a separator" do + it "yields each paragraph without trailing new line characters" do + @io.each_line("", 1024, chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs_without_trailing_new_line_characters + end + end + + describe "when passed chomp and nil as a separator" do + it "yields self's content" do + @io.pos = 100 + @io.each_line(nil, chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] + end + end + + describe "when passed chomp, nil as a separator, and a limit" do + it "yields each line of limit size without truncating trailing new line character" do + # 43 - is a size of the 1st paragraph in the file + @io.each_line(nil, 43, chomp: true) { |s| ScratchPad << s } + + ScratchPad.recorded.should == [ + "Voici la ligne une.\nQui è la linea due.\n\n\n", + "Aquí está la línea tres.\n" + "Hier ist Zeile ", + "vier.\n\nEstá aqui a linha cinco.\nHere is li", + "ne six.\n" + ] + end + end + + describe "when passed too many arguments" do + it "raises ArgumentError" do + -> { + @io.each_line("", 1, "excess argument", chomp: true) {} + }.should.raise(ArgumentError) + end + end end describe "IO#each_line" do - it_behaves_like :io_each_default_separator, :each_line + before :each do + @io = IOSpecs.io_fixture "lines.txt" + ScratchPad.record [] + suppress_warning {@sep, $/ = $/, " "} + end + + after :each do + @io.close if @io + suppress_warning {$/ = @sep} + end + + it "uses $/ as the default line separator" do + @io.each_line { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_space_separator + end end diff --git a/spec/ruby/core/io/each_spec.rb b/spec/ruby/core/io/each_spec.rb index 91ecbd19c8..594052256e 100644 --- a/spec/ruby/core/io/each_spec.rb +++ b/spec/ruby/core/io/each_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/each' describe "IO#each" do - it_behaves_like :io_each, :each -end - -describe "IO#each" do - it_behaves_like :io_each_default_separator, :each + it "is an alias of IO#each_line" do + IO.instance_method(:each).should == IO.instance_method(:each_line) + end end diff --git a/spec/ruby/core/io/eof_spec.rb b/spec/ruby/core/io/eof_spec.rb index c8955abde0..561daa4ec3 100644 --- a/spec/ruby/core/io/eof_spec.rb +++ b/spec/ruby/core/io/eof_spec.rb @@ -105,3 +105,9 @@ describe "IO#eof?" do @r.should.eof? end end + +describe "IO#eof" do + it "is an alias of IO#eof?" do + IO.instance_method(:eof).should == IO.instance_method(:eof?) + end +end diff --git a/spec/ruby/core/io/foreach_spec.rb b/spec/ruby/core/io/foreach_spec.rb index 015988f9fb..ccd2f25517 100644 --- a/spec/ruby/core/io/foreach_spec.rb +++ b/spec/ruby/core/io/foreach_spec.rb @@ -28,7 +28,7 @@ describe "IO.foreach" do ScratchPad.recorded.should == ["hello\n", "line2\n"] end - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do it "gets data from a fork when passed -" do parent_pid = $$ diff --git a/spec/ruby/core/io/isatty_spec.rb b/spec/ruby/core/io/isatty_spec.rb index 3b6c69b190..60b97d21be 100644 --- a/spec/ruby/core/io/isatty_spec.rb +++ b/spec/ruby/core/io/isatty_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/tty' describe "IO#isatty" do - it_behaves_like :io_tty, :isatty + it "is an alias of IO#tty?" do + IO.instance_method(:isatty).should == IO.instance_method(:tty?) + end end diff --git a/spec/ruby/core/io/pos_spec.rb b/spec/ruby/core/io/pos_spec.rb index e6cda2643d..bbe25ce97b 100644 --- a/spec/ruby/core/io/pos_spec.rb +++ b/spec/ruby/core/io/pos_spec.rb @@ -3,7 +3,37 @@ require_relative 'fixtures/classes' require_relative 'shared/pos' describe "IO#pos" do - it_behaves_like :io_pos, :pos + before :each do + @fname = tmp('test.txt') + File.open(@fname, 'w') { |f| f.write "123" } + end + + after :each do + rm_r @fname + end + + it "gets the offset" do + File.open @fname do |f| + f.pos.should == 0 + f.read 1 + f.pos.should == 1 + f.read 2 + f.pos.should == 3 + end + end + + it "raises IOError on closed stream" do + -> { IOSpecs.closed_io.pos }.should.raise(IOError) + end + + it "resets #eof?" do + open @fname do |io| + io.read 1 + io.read 1 + io.pos + io.should_not.eof? + end + end end describe "IO#pos=" do diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb index dd787c9b60..5be969e280 100644 --- a/spec/ruby/core/io/read_spec.rb +++ b/spec/ruby/core/io/read_spec.rb @@ -163,7 +163,7 @@ ruby_version_is ""..."4.0" do end end - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do it "opens a pipe to a fork if the rest is -" do str = nil suppress_warning do # https://bugs.ruby-lang.org/issues/19630 diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb index 640e253200..d41d24d7d1 100644 --- a/spec/ruby/core/io/readlines_spec.rb +++ b/spec/ruby/core/io/readlines_spec.rb @@ -189,7 +189,7 @@ describe "IO.readlines" do lines.should == ["hello\n", "line2\n"] end - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do it "gets data from a fork when passed -" do lines = nil suppress_warning do # https://bugs.ruby-lang.org/issues/19630 diff --git a/spec/ruby/core/io/shared/chars.rb b/spec/ruby/core/io/shared/chars.rb deleted file mode 100644 index efd4d5ee10..0000000000 --- a/spec/ruby/core/io/shared/chars.rb +++ /dev/null @@ -1,73 +0,0 @@ -# -*- encoding: utf-8 -*- -describe :io_chars, shared: true do - before :each do - @io = IOSpecs.io_fixture "lines.txt" - ScratchPad.record [] - end - - after :each do - @io.close unless @io.closed? - end - - it "yields each character" do - @io.readline.should == "Voici la ligne une.\n" - - count = 0 - @io.send(@method) do |c| - ScratchPad << c - break if 4 < count += 1 - end - - ScratchPad.recorded.should == ["Q", "u", "i", " ", "è"] - end - - describe "when no block is given" do - it "returns an Enumerator" do - enum = @io.send(@method) - enum.should.instance_of?(Enumerator) - enum.first(5).should == ["V", "o", "i", "c", "i"] - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - @io.send(@method).size.should == nil - end - end - end - end - - it "returns itself" do - @io.send(@method) { |c| }.should.equal?(@io) - end - - it "returns an enumerator for a closed stream" do - IOSpecs.closed_io.send(@method).should.instance_of?(Enumerator) - end - - it "raises an IOError when an enumerator created on a closed stream is accessed" do - -> { IOSpecs.closed_io.send(@method).first }.should.raise(IOError) - end - - it "raises IOError on closed stream" do - -> { IOSpecs.closed_io.send(@method) {} }.should.raise(IOError) - end -end - -describe :io_chars_empty, shared: true do - before :each do - @name = tmp("io_each_char") - @io = new_io @name, "w+:utf-8" - ScratchPad.record [] - end - - after :each do - @io.close unless @io.closed? - rm_r @name - end - - it "does not yield any characters on an empty stream" do - @io.send(@method) { |c| ScratchPad << c } - ScratchPad.recorded.should == [] - end -end diff --git a/spec/ruby/core/io/shared/codepoints.rb b/spec/ruby/core/io/shared/codepoints.rb deleted file mode 100644 index 21c756986f..0000000000 --- a/spec/ruby/core/io/shared/codepoints.rb +++ /dev/null @@ -1,54 +0,0 @@ -# -*- encoding: utf-8 -*- -require_relative '../fixtures/classes' - -describe :io_codepoints, shared: true do - before :each do - @io = IOSpecs.io_fixture "lines.txt" - @enum = @io.send(@method) - end - - after :each do - @io.close - end - - describe "when no block is given" do - it "returns an Enumerator" do - @enum.should.instance_of?(Enumerator) - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - @enum.size.should == nil - end - end - end - end - - it "yields each codepoint" do - @enum.first(25).should == [ - 86, 111, 105, 99, 105, 32, 108, 97, 32, 108, 105, 103, 110, - 101, 32, 117, 110, 101, 46, 10, 81, 117, 105, 32, 232 - ] - end - - it "yields each codepoint starting from the current position" do - @io.pos = 130 - @enum.to_a.should == [101, 32, 115, 105, 120, 46, 10] - end - - it "raises an error if reading invalid sequence" do - @io.pos = 60 # inside of a multibyte sequence - -> { @enum.first }.should.raise(ArgumentError) - end - - it "does not change $_" do - $_ = "test" - @enum.to_a - $_.should == "test" - end - - it "raises an IOError when self is not readable" do - -> { IOSpecs.closed_io.send(@method).to_a }.should.raise(IOError) - end -end diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb deleted file mode 100644 index ae60c3506a..0000000000 --- a/spec/ruby/core/io/shared/each.rb +++ /dev/null @@ -1,251 +0,0 @@ -# -*- encoding: utf-8 -*- -require_relative '../fixtures/classes' - -describe :io_each, shared: true do - before :each do - @io = IOSpecs.io_fixture "lines.txt" - ScratchPad.record [] - end - - after :each do - @io.close if @io - end - - describe "with no separator" do - it "yields each line to the passed block" do - @io.send(@method) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.lines - end - - it "yields each line starting from the current position" do - @io.pos = 41 - @io.send(@method) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.lines[2..-1] - end - - it "returns self" do - @io.send(@method) { |l| l }.should.equal?(@io) - end - - it "does not change $_" do - $_ = "test" - @io.send(@method) { |s| s } - $_.should == "test" - end - - it "raises an IOError when self is not readable" do - -> { IOSpecs.closed_io.send(@method) {} }.should.raise(IOError) - end - - it "makes line count accessible via lineno" do - @io.send(@method) { ScratchPad << @io.lineno } - ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ] - end - - it "makes line count accessible via $." do - @io.send(@method) { ScratchPad << $. } - ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ] - end - - describe "when no block is given" do - it "returns an Enumerator" do - enum = @io.send(@method) - enum.should.instance_of?(Enumerator) - - enum.each { |l| ScratchPad << l } - ScratchPad.recorded.should == IOSpecs.lines - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - @io.send(@method).size.should == nil - end - end - end - end - end - - describe "with limit" do - describe "when limit is 0" do - it "raises an ArgumentError" do - # must pass block so Enumerator is evaluated and raises - -> { @io.send(@method, 0){} }.should.raise(ArgumentError) - end - end - - it "does not accept Integers that don't fit in a C off_t" do - -> { @io.send(@method, 2**128){} }.should.raise(RangeError) - end - end - - describe "when passed a String containing one space as a separator" do - it "uses the passed argument as the line separator" do - @io.send(@method, " ") { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.lines_space_separator - end - - it "does not change $_" do - $_ = "test" - @io.send(@method, " ") { |s| } - $_.should == "test" - end - - it "tries to convert the passed separator to a String using #to_str" do - obj = mock("to_str") - obj.stub!(:to_str).and_return(" ") - - @io.send(@method, obj) { |l| ScratchPad << l } - ScratchPad.recorded.should == IOSpecs.lines_space_separator - end - end - - describe "when passed nil as a separator" do - it "yields self's content starting from the current position when the passed separator is nil" do - @io.pos = 100 - @io.send(@method, nil) { |s| ScratchPad << s } - ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] - end - end - - describe "when passed an empty String as a separator" do - it "yields each paragraph" do - @io.send(@method, "") { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.paragraphs - end - - it "discards leading newlines" do - @io.readline - @io.readline - @io.send(@method, "") { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1] - end - end - - describe "with both separator and limit" do - describe "when no block is given" do - it "returns an Enumerator" do - enum = @io.send(@method, nil, 1024) - enum.should.instance_of?(Enumerator) - - enum.each { |l| ScratchPad << l } - ScratchPad.recorded.should == [IOSpecs.lines.join] - end - - describe "returned Enumerator" do - describe "size" do - it "should return nil" do - @io.send(@method, nil, 1024).size.should == nil - end - end - end - end - - describe "when a block is given" do - it "accepts an empty block" do - @io.send(@method, nil, 1024) {}.should.equal?(@io) - end - - describe "when passed nil as a separator" do - it "yields self's content starting from the current position when the passed separator is nil" do - @io.pos = 100 - @io.send(@method, nil, 1024) { |s| ScratchPad << s } - ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] - end - end - - describe "when passed an empty String as a separator" do - it "yields each paragraph" do - @io.send(@method, "", 1024) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.paragraphs - end - - it "discards leading newlines" do - @io.readline - @io.readline - @io.send(@method, "", 1024) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.paragraphs[1..-1] - end - end - end - end - - describe "when passed chomp" do - it "yields each line without trailing newline characters to the passed block" do - @io.send(@method, chomp: true) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters - end - - it "raises exception when options passed as Hash" do - -> { - @io.send(@method, { chomp: true }) { |s| } - }.should.raise(TypeError) - - -> { - @io.send(@method, "\n", 1, { chomp: true }) { |s| } - }.should.raise(ArgumentError, "wrong number of arguments (given 3, expected 0..2)") - end - end - - describe "when passed chomp and a separator" do - it "yields each line without separator to the passed block" do - @io.send(@method, " ", chomp: true) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.lines_space_separator_without_trailing_spaces - end - end - - describe "when passed chomp and empty line as a separator" do - it "yields each paragraph without trailing new line characters" do - @io.send(@method, "", 1024, chomp: true) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.paragraphs_without_trailing_new_line_characters - end - end - - describe "when passed chomp and nil as a separator" do - it "yields self's content" do - @io.pos = 100 - @io.send(@method, nil, chomp: true) { |s| ScratchPad << s } - ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] - end - end - - describe "when passed chomp, nil as a separator, and a limit" do - it "yields each line of limit size without truncating trailing new line character" do - # 43 - is a size of the 1st paragraph in the file - @io.send(@method, nil, 43, chomp: true) { |s| ScratchPad << s } - - ScratchPad.recorded.should == [ - "Voici la ligne une.\nQui è la linea due.\n\n\n", - "Aquí está la línea tres.\n" + "Hier ist Zeile ", - "vier.\n\nEstá aqui a linha cinco.\nHere is li", - "ne six.\n" - ] - end - end - - describe "when passed too many arguments" do - it "raises ArgumentError" do - -> { - @io.send(@method, "", 1, "excess argument", chomp: true) {} - }.should.raise(ArgumentError) - end - end -end - -describe :io_each_default_separator, shared: true do - before :each do - @io = IOSpecs.io_fixture "lines.txt" - ScratchPad.record [] - suppress_warning {@sep, $/ = $/, " "} - end - - after :each do - @io.close if @io - suppress_warning {$/ = @sep} - end - - it "uses $/ as the default line separator" do - @io.send(@method) { |s| ScratchPad << s } - ScratchPad.recorded.should == IOSpecs.lines_space_separator - end -end diff --git a/spec/ruby/core/io/shared/pos.rb b/spec/ruby/core/io/shared/pos.rb index f4d0405863..450058e159 100644 --- a/spec/ruby/core/io/shared/pos.rb +++ b/spec/ruby/core/io/shared/pos.rb @@ -1,37 +1,3 @@ -describe :io_pos, shared: true do - before :each do - @fname = tmp('test.txt') - File.open(@fname, 'w') { |f| f.write "123" } - end - - after :each do - rm_r @fname - end - - it "gets the offset" do - File.open @fname do |f| - f.send(@method).should == 0 - f.read 1 - f.send(@method).should == 1 - f.read 2 - f.send(@method).should == 3 - end - end - - it "raises IOError on closed stream" do - -> { IOSpecs.closed_io.send(@method) }.should.raise(IOError) - end - - it "resets #eof?" do - open @fname do |io| - io.read 1 - io.read 1 - io.send(@method) - io.should_not.eof? - end - end -end - describe :io_set_pos, shared: true do before :each do @fname = tmp('test.txt') diff --git a/spec/ruby/core/io/shared/tty.rb b/spec/ruby/core/io/shared/tty.rb deleted file mode 100644 index 1dc0e95739..0000000000 --- a/spec/ruby/core/io/shared/tty.rb +++ /dev/null @@ -1,24 +0,0 @@ -require_relative '../fixtures/classes' - -describe :io_tty, shared: true do - platform_is_not :windows do - it "returns true if this stream is a terminal device (TTY)" do - begin - # check to enabled tty - File.open('/dev/tty') {} - rescue Errno::ENXIO - skip "workaround for not configured environment like OS X" - else - File.open('/dev/tty') { |f| f.send(@method) }.should == true - end - end - end - - it "returns false if this stream is not a terminal device (TTY)" do - File.open(__FILE__) { |f| f.send(@method) }.should == false - end - - it "raises IOError on closed stream" do - -> { IOSpecs.closed_io.send @method }.should.raise(IOError) - end -end diff --git a/spec/ruby/core/io/tell_spec.rb b/spec/ruby/core/io/tell_spec.rb index 0d6c6b02d3..a6b51adc17 100644 --- a/spec/ruby/core/io/tell_spec.rb +++ b/spec/ruby/core/io/tell_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/pos' describe "IO#tell" do - it_behaves_like :io_pos, :tell + it "is an alias of IO#pos" do + IO.instance_method(:tell).should == IO.instance_method(:pos) + end end diff --git a/spec/ruby/core/io/to_i_spec.rb b/spec/ruby/core/io/to_i_spec.rb index 1d0cf2a1f6..b271112a81 100644 --- a/spec/ruby/core/io/to_i_spec.rb +++ b/spec/ruby/core/io/to_i_spec.rb @@ -1,12 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' describe "IO#to_i" do - it "returns the numeric file descriptor of the given IO object" do - $stdout.to_i.should == 1 - end - - it "raises IOError on closed stream" do - -> { IOSpecs.closed_io.to_i }.should.raise(IOError) + it "is an alias of IO#fileno" do + IO.instance_method(:to_i).should == IO.instance_method(:fileno) end end diff --git a/spec/ruby/core/io/to_path_spec.rb b/spec/ruby/core/io/to_path_spec.rb new file mode 100644 index 0000000000..ec6dffc115 --- /dev/null +++ b/spec/ruby/core/io/to_path_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "IO#to_path" do + it "is an alias of IO#path" do + IO.instance_method(:to_path).should == IO.instance_method(:path) + end +end diff --git a/spec/ruby/core/io/tty_spec.rb b/spec/ruby/core/io/tty_spec.rb index 3b76c6d2b8..e1848a1760 100644 --- a/spec/ruby/core/io/tty_spec.rb +++ b/spec/ruby/core/io/tty_spec.rb @@ -1,6 +1,25 @@ require_relative '../../spec_helper' -require_relative 'shared/tty' +require_relative 'fixtures/classes' describe "IO#tty?" do - it_behaves_like :io_tty, :tty? + platform_is_not :windows do + it "returns true if this stream is a terminal device (TTY)" do + begin + # check to enabled tty + File.open('/dev/tty') {} + rescue Errno::ENXIO + skip "workaround for not configured environment like OS X" + else + File.open('/dev/tty') { |f| f.tty? }.should == true + end + end + end + + it "returns false if this stream is not a terminal device (TTY)" do + File.open(__FILE__) { |f| f.tty? }.should == false + end + + it "raises IOError on closed stream" do + -> { IOSpecs.closed_io.tty? }.should.raise(IOError) + end end diff --git a/spec/ruby/core/kernel/clone_spec.rb b/spec/ruby/core/kernel/clone_spec.rb index 4ddb23d6e6..80e7a78abb 100644 --- a/spec/ruby/core/kernel/clone_spec.rb +++ b/spec/ruby/core/kernel/clone_spec.rb @@ -10,12 +10,6 @@ describe "Kernel#clone" do @obj = KernelSpecs::Duplicate.new 1, :a end - it "calls #initialize_copy on the new instance" do - clone = @obj.clone - ScratchPad.recorded.should_not == @obj.object_id - ScratchPad.recorded.should == clone.object_id - end - it "uses the internal allocator and does not call #allocate" do klass = Class.new instance = klass.new diff --git a/spec/ruby/core/kernel/dup_spec.rb b/spec/ruby/core/kernel/dup_spec.rb index 99de5e3732..fa4ca71476 100644 --- a/spec/ruby/core/kernel/dup_spec.rb +++ b/spec/ruby/core/kernel/dup_spec.rb @@ -10,12 +10,6 @@ describe "Kernel#dup" do @obj = KernelSpecs::Duplicate.new 1, :a end - it "calls #initialize_copy on the new instance" do - dup = @obj.dup - ScratchPad.recorded.should_not == @obj.object_id - ScratchPad.recorded.should == dup.object_id - end - it "uses the internal allocator and does not call #allocate" do klass = Class.new instance = klass.new diff --git a/spec/ruby/core/kernel/enum_for_spec.rb b/spec/ruby/core/kernel/enum_for_spec.rb index 0092e20468..ef0fb64e63 100644 --- a/spec/ruby/core/kernel/enum_for_spec.rb +++ b/spec/ruby/core/kernel/enum_for_spec.rb @@ -1,5 +1,7 @@ require_relative '../../spec_helper' describe "Kernel#enum_for" do - it "needs to be reviewed for spec completeness" + it "is an alias of Kernel#to_enum" do + Kernel.instance_method(:enum_for).should == Kernel.instance_method(:to_enum) + end end diff --git a/spec/ruby/core/kernel/fail_spec.rb b/spec/ruby/core/kernel/fail_spec.rb index ac379b67d5..2d117d26cd 100644 --- a/spec/ruby/core/kernel/fail_spec.rb +++ b/spec/ruby/core/kernel/fail_spec.rb @@ -1,42 +1,13 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' describe "Kernel#fail" do - it "is a private method" do - Kernel.private_instance_methods(false).should.include?(:fail) - end - - it "raises a RuntimeError" do - -> { fail }.should.raise(RuntimeError) - end - - it "accepts an Object with an exception method returning an Exception" do - obj = Object.new - def obj.exception(msg) - StandardError.new msg - end - -> { fail obj, "..." }.should.raise(StandardError, "...") - end - - it "instantiates the specified exception class" do - error_class = Class.new(RuntimeError) - -> { fail error_class }.should.raise(error_class) - end - - it "uses the specified message" do - -> { - begin - fail "the duck is not irish." - rescue => e - e.message.should == "the duck is not irish." - raise - else - raise Exception - end - }.should.raise(RuntimeError) + it "is an alias of Kernel#raise" do + Kernel.instance_method(:fail).should == Kernel.instance_method(:raise) end end describe "Kernel.fail" do - it "needs to be reviewed for spec completeness" + it "is an alias of Kernel.raise" do + Kernel.method(:fail).should == Kernel.method(:raise) + end end diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb index 35c752b1ab..a311f3c3d7 100644 --- a/spec/ruby/core/kernel/format_spec.rb +++ b/spec/ruby/core/kernel/format_spec.rb @@ -1,47 +1,13 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -# NOTE: most specs are in sprintf_spec.rb, this is just an alias describe "Kernel#format" do - it "is a private method" do - Kernel.private_instance_methods(false).should.include?(:format) + it "is an alias of Kernel#sprintf" do + Kernel.instance_method(:format).should == Kernel.instance_method(:sprintf) end end describe "Kernel.format" do - it "is accessible as a module function" do - Kernel.format("%s", "hello").should == "hello" - end - - describe "when $VERBOSE is true" do - it "warns if too many arguments are passed" do - code = <<~RUBY - $VERBOSE = true - format("test", 1) - RUBY - - ruby_exe(code, args: "2>&1").should.include?("warning: too many arguments for format string") - end - - it "does not warns if too many keyword arguments are passed" do - code = <<~RUBY - $VERBOSE = true - format("test %{test}", test: 1, unused: 2) - RUBY - - ruby_exe(code, args: "2>&1").should_not.include?("warning") - end - - ruby_bug "#20593", ""..."3.4" do - it "doesn't warns if keyword arguments are passed and none are used" do - code = <<~RUBY - $VERBOSE = true - format("test", test: 1) - format("test", {}) - RUBY - - ruby_exe(code, args: "2>&1").should_not.include?("warning") - end - end + it "is an alias of Kernel.sprintf" do + Kernel.method(:format).should == Kernel.method(:sprintf) end end diff --git a/spec/ruby/core/kernel/is_a_spec.rb b/spec/ruby/core/kernel/is_a_spec.rb index bd8c96529a..ff36a769c7 100644 --- a/spec/ruby/core/kernel/is_a_spec.rb +++ b/spec/ruby/core/kernel/is_a_spec.rb @@ -1,6 +1,56 @@ require_relative '../../spec_helper' -require_relative 'shared/kind_of' +require_relative 'fixtures/classes' describe "Kernel#is_a?" do - it_behaves_like :kernel_kind_of, :is_a? + before :each do + @o = KernelSpecs::KindaClass.new + end + + it "returns true if given class is the object's class" do + @o.is_a?(KernelSpecs::KindaClass).should == true + end + + it "returns true if given class is an ancestor of the object's class" do + @o.is_a?(KernelSpecs::AncestorClass).should == true + @o.is_a?(String).should == true + @o.is_a?(Object).should == true + end + + it "returns false if the given class is not object's class nor an ancestor" do + @o.is_a?(Array).should == false + end + + it "returns true if given a Module that is included in object's class" do + @o.is_a?(KernelSpecs::MyModule).should == true + end + + it "returns true if given a Module that is included one of object's ancestors only" do + @o.is_a?(KernelSpecs::AncestorModule).should == true + end + + it "returns true if given a Module that object has been extended with" do + @o.is_a?(KernelSpecs::MyExtensionModule).should == true + end + + it "returns true if given a Module that object has been prepended with" do + @o.is_a?(KernelSpecs::MyPrependedModule).should == true + end + + it "returns false if given a Module not included nor prepended in object's class nor ancestors" do + @o.is_a?(KernelSpecs::SomeOtherModule).should == false + end + + it "raises a TypeError if given an object that is not a Class nor a Module" do + -> { @o.is_a?(1) }.should.raise(TypeError) + -> { @o.is_a?('KindaClass') }.should.raise(TypeError) + -> { @o.is_a?(:KindaClass) }.should.raise(TypeError) + -> { @o.is_a?(Object.new) }.should.raise(TypeError) + end + + it "does not take into account `class` method overriding" do + def @o.class; Integer; end + + @o.is_a?(Integer).should == false + @o.is_a?(KernelSpecs::KindaClass).should == true + end end diff --git a/spec/ruby/core/kernel/kind_of_spec.rb b/spec/ruby/core/kernel/kind_of_spec.rb index c988edccb5..7fcc72543d 100644 --- a/spec/ruby/core/kernel/kind_of_spec.rb +++ b/spec/ruby/core/kernel/kind_of_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/kind_of' describe "Kernel#kind_of?" do - it_behaves_like :kernel_kind_of, :kind_of? + it "is an alias of Kernel#is_a?" do + Kernel.instance_method(:kind_of?).should == Kernel.instance_method(:is_a?) + end end diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb index 332045b200..b3ac1fc64c 100644 --- a/spec/ruby/core/kernel/require_relative_spec.rb +++ b/spec/ruby/core/kernel/require_relative_spec.rb @@ -109,9 +109,9 @@ describe "Kernel#require_relative with a relative path" do -> { require_relative(path) - }.should(raise_error(LoadError) { |e| + }.should.raise(LoadError) { |e| e.path.should == File.expand_path(path, @abs_dir) - }) + } end it "calls #to_str on non-String objects" do @@ -311,9 +311,9 @@ describe "Kernel#require_relative with an absolute path" do -> { require_relative(path) - }.should(raise_error(LoadError) { |e| + }.should.raise(LoadError) { |e| e.path.should == File.expand_path(path, @abs_dir) - }) + } end it "calls #to_str on non-String objects" do diff --git a/spec/ruby/core/kernel/shared/kind_of.rb b/spec/ruby/core/kernel/shared/kind_of.rb deleted file mode 100644 index a5e0eab08f..0000000000 --- a/spec/ruby/core/kernel/shared/kind_of.rb +++ /dev/null @@ -1,55 +0,0 @@ -require_relative '../fixtures/classes' - -describe :kernel_kind_of, shared: true do - before :each do - @o = KernelSpecs::KindaClass.new - end - - it "returns true if given class is the object's class" do - @o.send(@method, KernelSpecs::KindaClass).should == true - end - - it "returns true if given class is an ancestor of the object's class" do - @o.send(@method, KernelSpecs::AncestorClass).should == true - @o.send(@method, String).should == true - @o.send(@method, Object).should == true - end - - it "returns false if the given class is not object's class nor an ancestor" do - @o.send(@method, Array).should == false - end - - it "returns true if given a Module that is included in object's class" do - @o.send(@method, KernelSpecs::MyModule).should == true - end - - it "returns true if given a Module that is included one of object's ancestors only" do - @o.send(@method, KernelSpecs::AncestorModule).should == true - end - - it "returns true if given a Module that object has been extended with" do - @o.send(@method, KernelSpecs::MyExtensionModule).should == true - end - - it "returns true if given a Module that object has been prepended with" do - @o.send(@method, KernelSpecs::MyPrependedModule).should == true - end - - it "returns false if given a Module not included nor prepended in object's class nor ancestors" do - @o.send(@method, KernelSpecs::SomeOtherModule).should == false - end - - it "raises a TypeError if given an object that is not a Class nor a Module" do - -> { @o.send(@method, 1) }.should.raise(TypeError) - -> { @o.send(@method, 'KindaClass') }.should.raise(TypeError) - -> { @o.send(@method, :KindaClass) }.should.raise(TypeError) - -> { @o.send(@method, Object.new) }.should.raise(TypeError) - end - - it "does not take into account `class` method overriding" do - def @o.class; Integer; end - - @o.send(@method, Integer).should == false - @o.send(@method, KernelSpecs::KindaClass).should == true - end -end diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb index b40bd95f59..e57334c5ab 100644 --- a/spec/ruby/core/kernel/shared/sprintf.rb +++ b/spec/ruby/core/kernel/shared/sprintf.rb @@ -1031,4 +1031,27 @@ describe :kernel_sprintf, shared: true do it "does not raise error when passed more arguments than needed" do sprintf("%s %d %c", "string", 2, "c", []).should == "string 2 c" end + + describe "when $VERBOSE is true" do + it "warns if too many arguments are passed" do + -> { + format("test", 1) + }.should complain(/too many arguments for format string/, verbose: true) + end + + it "does not warns if too many keyword arguments are passed" do + -> { + format("test %{test}", test: 1, unused: 2) + }.should_not complain(verbose: true) + end + + ruby_bug "#20593", ""..."3.4" do + it "doesn't warns if keyword arguments are passed and none are used" do + -> { + format("test", test: 1) + format("test", {}) + }.should_not complain(verbose: true) + end + end + end end diff --git a/spec/ruby/core/kernel/then_spec.rb b/spec/ruby/core/kernel/then_spec.rb index 8109a2960a..bda5a69662 100644 --- a/spec/ruby/core/kernel/then_spec.rb +++ b/spec/ruby/core/kernel/then_spec.rb @@ -2,5 +2,13 @@ require_relative '../../spec_helper' require_relative 'shared/then' describe "Kernel#then" do - it_behaves_like :kernel_then, :then + ruby_version_is ""..."3.4" do + it_behaves_like :kernel_then, :then + end + + ruby_version_is "3.4" do + it "is an alias of Kernel#yield_self" do + Kernel.instance_method(:then).should == Kernel.instance_method(:yield_self) + end + end end diff --git a/spec/ruby/core/kernel/to_enum_spec.rb b/spec/ruby/core/kernel/to_enum_spec.rb index 9d9945450f..1cee26b694 100644 --- a/spec/ruby/core/kernel/to_enum_spec.rb +++ b/spec/ruby/core/kernel/to_enum_spec.rb @@ -1,5 +1,59 @@ require_relative '../../spec_helper' describe "Kernel#to_enum" do - it "needs to be reviewed for spec completeness" + it "is defined in Kernel" do + Kernel.method_defined?(:to_enum).should == true + end + + it "returns a new enumerator" do + "abc".to_enum.should.instance_of?(Enumerator) + end + + it "defaults the first argument to :each" do + enum = [1,2].to_enum + enum.map { |v| v }.should == [1,2].each { |v| v } + end + + it "sets regexp matches in the caller" do + "wawa".to_enum(:scan, /./).map {|o| $& }.should == ["w", "a", "w", "a"] + a = [] + "wawa".to_enum(:scan, /./).each {|o| a << $& } + a.should == ["w", "a", "w", "a"] + end + + it "exposes multi-arg yields as an array" do + o = Object.new + def o.each + yield :a + yield :b1, :b2 + yield [:c] + yield :d1, :d2 + yield :e1, :e2, :e3 + end + + enum = o.to_enum + enum.next.should == :a + enum.next.should == [:b1, :b2] + enum.next.should == [:c] + enum.next.should == [:d1, :d2] + enum.next.should == [:e1, :e2, :e3] + end + + it "uses the passed block's value to calculate the size of the enumerator" do + Object.new.to_enum { 100 }.size.should == 100 + end + + it "defers the evaluation of the passed block until #size is called" do + ScratchPad.record [] + + enum = Object.new.to_enum do + ScratchPad << :called + 100 + end + + ScratchPad.recorded.should.empty? + + enum.size + ScratchPad.recorded.should == [:called] + end end diff --git a/spec/ruby/core/marshal/load_spec.rb b/spec/ruby/core/marshal/load_spec.rb index a5bdfbf520..f5a05f8e52 100644 --- a/spec/ruby/core/marshal/load_spec.rb +++ b/spec/ruby/core/marshal/load_spec.rb @@ -1,6 +1,1291 @@ +# encoding: binary require_relative '../../spec_helper' -require_relative 'shared/load' +require_relative 'fixtures/marshal_data' describe "Marshal.load" do - it_behaves_like :marshal_load, :load + before :all do + @num_self_class = 1 + end + + it "raises an ArgumentError when the dumped data is truncated" do + obj = {first: 1, second: 2, third: 3} + -> { Marshal.load(Marshal.dump(obj)[0, 5]) }.should.raise(ArgumentError, "marshal data too short") + end + + it "raises an ArgumentError when the argument is empty String" do + -> { Marshal.load("") }.should.raise(ArgumentError, "marshal data too short") + end + + it "raises an ArgumentError when the dumped class is missing" do + Object.send(:const_set, :KaBoom, Class.new) + kaboom = Marshal.dump(KaBoom.new) + Object.send(:remove_const, :KaBoom) + + -> { Marshal.load(kaboom) }.should.raise(ArgumentError) + end + + describe "when called with freeze: true" do + it "returns frozen strings" do + string = Marshal.load(Marshal.dump("foo"), freeze: true) + string.should == "foo" + string.should.frozen? + + utf8_string = "foo".encode(Encoding::UTF_8) + string = Marshal.load(Marshal.dump(utf8_string), freeze: true) + string.should == utf8_string + string.should.frozen? + end + + it "returns frozen arrays" do + array = Marshal.load(Marshal.dump([1, 2, 3]), freeze: true) + array.should == [1, 2, 3] + array.should.frozen? + end + + it "returns frozen hashes" do + hash = Marshal.load(Marshal.dump({foo: 42}), freeze: true) + hash.should == {foo: 42} + hash.should.frozen? + end + + it "returns frozen regexps" do + regexp = Marshal.load(Marshal.dump(/foo/), freeze: true) + regexp.should == /foo/ + regexp.should.frozen? + end + + it "returns frozen structs" do + struct = Marshal.load(Marshal.dump(MarshalSpec::StructToDump.new(1, 2)), freeze: true) + struct.should == MarshalSpec::StructToDump.new(1, 2) + struct.should.frozen? + end + + it "returns frozen objects" do + source_object = Object.new + + object = Marshal.load(Marshal.dump(source_object), freeze: true) + object.should.frozen? + end + + describe "deep freezing" do + it "returns hashes with frozen keys and values" do + key = Object.new + value = Object.new + source_object = {key => value} + + hash = Marshal.load(Marshal.dump(source_object), freeze: true) + hash.size.should == 1 + hash.keys[0].should.frozen? + hash.values[0].should.frozen? + end + + it "returns arrays with frozen elements" do + object = Object.new + source_object = [object] + + array = Marshal.load(Marshal.dump(source_object), freeze: true) + array.size.should == 1 + array[0].should.frozen? + end + + it "returns structs with frozen members" do + object1 = Object.new + object2 = Object.new + source_object = MarshalSpec::StructToDump.new(object1, object2) + + struct = Marshal.load(Marshal.dump(source_object), freeze: true) + struct.a.should.frozen? + struct.b.should.frozen? + end + + it "returns objects with frozen instance variables" do + source_object = Object.new + instance_variable = Object.new + source_object.instance_variable_set(:@a, instance_variable) + + object = Marshal.load(Marshal.dump(source_object), freeze: true) + object.instance_variable_get(:@a).should != nil + object.instance_variable_get(:@a).should.frozen? + end + + it "deduplicates frozen strings" do + source_object = ["foo" + "bar", "foobar"] + object = Marshal.load(Marshal.dump(source_object), freeze: true) + + object[0].should.equal?(object[1]) + end + end + + it "does not freeze modules" do + object = Marshal.load(Marshal.dump(Kernel), freeze: true) + object.should_not.frozen? + Kernel.should_not.frozen? + end + + it "does not freeze classes" do + object = Marshal.load(Marshal.dump(Object), freeze: true) + object.should_not.frozen? + Object.should_not.frozen? + end + + it "does freeze extended objects" do + object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", freeze: true) + object.should.frozen? + end + + it "does freeze extended objects with instance variables" do + object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x06:\n@ivarT", freeze: true) + object.should.frozen? + end + + it "returns frozen object having #_dump method" do + object = Marshal.load(Marshal.dump(UserDefined.new), freeze: true) + object.should.frozen? + end + + it "returns frozen object responding to #marshal_dump and #marshal_load" do + object = Marshal.load(Marshal.dump(UserMarshal.new), freeze: true) + object.should.frozen? + end + + it "returns frozen object extended by a module" do + object = Object.new + object.extend(MarshalSpec::ModuleToExtendBy) + + object = Marshal.load(Marshal.dump(object), freeze: true) + object.should.frozen? + end + + it "does not call freeze method" do + object = MarshalSpec::ObjectWithFreezeRaisingException.new + object = Marshal.load(Marshal.dump(object), freeze: true) + object.should.frozen? + end + + it "returns frozen object even if object does not respond to freeze method" do + object = MarshalSpec::ObjectWithoutFreeze.new + object = Marshal.load(Marshal.dump(object), freeze: true) + object.should.frozen? + end + + it "returns a frozen object when is an instance of String/Array/Regexp/Hash subclass and has instance variables" do + source_object = UserString.new + source_object.instance_variable_set(:@foo, "bar") + + object = Marshal.load(Marshal.dump(source_object), freeze: true) + object.should.frozen? + end + + describe "when called with a proc" do + it "call the proc with frozen objects" do + arr = [] + s = +'hi' + s.instance_variable_set(:@foo, 5) + st = Struct.new("Brittle", :a).new + st.instance_variable_set(:@clue, 'none') + st.a = 0.0 + h = Hash.new('def') + h['nine'] = 9 + a = [:a, :b, :c] + a.instance_variable_set(:@two, 2) + obj = [s, 10, s, s, st, a] + obj.instance_variable_set(:@zoo, 'ant') + proc = Proc.new { |o| arr << o; o} + + Marshal.load( + "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", + proc, + freeze: true, + ) + + arr.should == [ + false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st, + :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]], + ] + + arr.each do |v| + v.should.frozen? + end + + Struct.send(:remove_const, :Brittle) + end + + it "does not freeze the object returned by the proc" do + string = Marshal.load(Marshal.dump("foo"), proc { |o| o.upcase }, freeze: true) + string.should == "FOO" + string.should_not.frozen? + end + end + end + + describe "when called with a proc" do + it "call the proc with fully initialized strings" do + utf8_string = "foo".encode(Encoding::UTF_8) + Marshal.load(Marshal.dump(utf8_string), proc { |arg| + if arg.is_a?(String) + arg.should == utf8_string + arg.encoding.should == Encoding::UTF_8 + end + arg + }) + end + + it "no longer mutate the object after it was passed to the proc" do + string = Marshal.load(Marshal.dump("foo"), :freeze.to_proc) + string.should.frozen? + end + + it "call the proc with extended objects" do + objs = [] + obj = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", Proc.new { |o| objs << o; o }) + objs.should == [obj] + end + + it "returns the value of the proc" do + Marshal.load(Marshal.dump([1,2]), proc { [3,4] }).should == [3,4] + end + + it "calls the proc for recursively visited data" do + a = [1] + a << a + ret = [] + Marshal.load(Marshal.dump(a), proc { |arg| ret << arg.inspect; arg }) + ret[0].should == 1.inspect + ret[1].should == a.inspect + ret.size.should == 2 + end + + it "loads an Array with proc" do + arr = [] + s = +'hi' + s.instance_variable_set(:@foo, 5) + st = Struct.new("Brittle", :a).new + st.instance_variable_set(:@clue, 'none') + st.a = 0.0 + h = Hash.new('def') + h['nine'] = 9 + a = [:a, :b, :c] + a.instance_variable_set(:@two, 2) + obj = [s, 10, s, s, st, a] + obj.instance_variable_set(:@zoo, 'ant') + proc = Proc.new { |o| arr << o.dup; o} + + Marshal.load("\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc) + + arr.should == [ + false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st, + :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]], + ] + Struct.send(:remove_const, :Brittle) + end + end + + describe "when called with nil for the proc argument" do + it "behaves as if no proc argument was passed" do + a = [1] + a << a + b = Marshal.load(Marshal.dump(a), nil) + b.should == a + end + end + + describe "when called on objects with custom _dump methods" do + it "does not set instance variables of an object with user-defined _dump/_load" do + # this string represents: <#UserPreviouslyDefinedWithInitializedIvar @field2=7 @field1=6> + dump_str = "\004\bu:-UserPreviouslyDefinedWithInitializedIvar\a:\f@field2i\f:\f@field1i\v" + + UserPreviouslyDefinedWithInitializedIvar.should_receive(:_load).and_return(UserPreviouslyDefinedWithInitializedIvar.new) + marshaled_obj = Marshal.load(dump_str) + + marshaled_obj.should.instance_of?(UserPreviouslyDefinedWithInitializedIvar) + marshaled_obj.field1.should == nil + marshaled_obj.field2.should == nil + end + + it "loads the String in non US-ASCII and non UTF-8 encoding" do + source_object = UserDefinedString.new("a".encode("windows-1251")) + object = Marshal.load(Marshal.dump(source_object)) + object.string.should == "a".encode("windows-1251") + end + + it "loads the String in multibyte encoding" do + source_object = UserDefinedString.new("a".encode("utf-32le")) + object = Marshal.load(Marshal.dump(source_object)) + object.string.should == "a".encode("utf-32le") + end + + describe "that returns an immediate value" do + it "loads an array containing an instance of the object, followed by multiple instances of another object" do + str = "string" + + # this string represents: [<#UserDefinedImmediate A>, <#String "string">, <#String "string">] + marshaled_obj = Marshal.load("\004\b[\bu:\031UserDefinedImmediate\000\"\vstring@\a") + + marshaled_obj.should == [nil, str, str] + end + + it "loads any structure with multiple references to the same object, followed by multiple instances of another object" do + str = "string" + + # this string represents: {a: <#UserDefinedImmediate A>, b: <#UserDefinedImmediate A>, c: <#String "string">, d: <#String "string">} + hash_dump = "\x04\b{\t:\x06aIu:\x19UserDefinedImmediate\x00\x06:\x06ET:\x06b@\x06:\x06cI\"\vstring\x06;\aT:\x06d@\a" + + marshaled_obj = Marshal.load(hash_dump) + marshaled_obj.should == {a: nil, b: nil, c: str, d: str} + + # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate A>, <#String "string">, <#String "string">] + array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ET@\x06I\"\vstring\x06;\x06T@\a" + + marshaled_obj = Marshal.load(array_dump) + marshaled_obj.should == [nil, nil, str, str] + end + + it "loads an array containing references to multiple instances of the object, followed by multiple instances of another object" do + str = "string" + + # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate B>, <#String "string">, <#String "string">] + array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ETIu;\x00\x00\x06;\x06TI\"\vstring\x06;\x06T@\b" + + marshaled_obj = Marshal.load(array_dump) + marshaled_obj.should == [nil, nil, str, str] + end + end + end + + it "loads an array containing objects having _dump method, and with proc" do + arr = [] + myproc = Proc.new { |o| arr << o.dup; o } + o1 = UserDefined.new; + o2 = UserDefinedWithIvar.new + obj = [o1, o2, o1, o2] + + Marshal.load("\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc) + + arr[0].should == o1 + arr[1].should == o2 + arr[2].should == obj + arr.size.should == 3 + end + + it "loads an array containing objects having marshal_dump method, and with proc" do + arr = [] + proc = Proc.new { |o| arr << o.dup; o } + o1 = UserMarshal.new + o2 = UserMarshalWithIvar.new + + Marshal.load("\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc) + + arr[0].should == 'stuff' + arr[1].should == o1 + arr[2].should == 'my data' + arr[3].should == ['my data'] + arr[4].should == o2 + arr[5].should == [o1, o2, o1, o2] + + arr.size.should == 6 + end + + it "assigns classes to nested subclasses of Array correctly" do + arr = ArraySub.new(ArraySub.new) + arr_dump = Marshal.dump(arr) + Marshal.load(arr_dump).class.should == ArraySub + end + + it "loads subclasses of Array with overridden << and push correctly" do + arr = ArraySubPush.new + arr[0] = '1' + arr_dump = Marshal.dump(arr) + Marshal.load(arr_dump).should == arr + end + + it "raises a TypeError with bad Marshal version" do + marshal_data = +'\xff\xff' + marshal_data[0] = (Marshal::MAJOR_VERSION).chr + marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr + + -> { Marshal.load(marshal_data) }.should.raise(TypeError) + + marshal_data = +'\xff\xff' + marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr + marshal_data[1] = (Marshal::MINOR_VERSION).chr + + -> { Marshal.load(marshal_data) }.should.raise(TypeError) + end + + it "raises EOFError on loading an empty file" do + temp_file = tmp("marshal.rubyspec.tmp.#{Process.pid}") + file = File.new(temp_file, "w+") + begin + -> { Marshal.load(file) }.should.raise(EOFError) + ensure + file.close + rm_r temp_file + end + end + + # Note: Ruby 1.9 should be compatible with older marshal format + MarshalSpec::DATA.each do |description, (object, marshal, attributes)| + it "loads a #{description}" do + Marshal.load(marshal).should == object + end + end + + MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)| + it "loads a #{description}" do + Marshal.load(marshal).should == object + end + end + + describe "for an Array" do + it "loads an array containing the same objects" do + s = 'oh' + b = 'hi' + r = // + d = [b, :no, s, :go] + c = String + f = 1.0 + + o1 = UserMarshalWithIvar.new; o2 = UserMarshal.new + + obj = [:so, 'hello', 100, :so, :so, d, :so, o2, :so, :no, o2, + :go, c, nil, Struct::Pyramid.new, f, :go, :no, s, b, r, + :so, 'huh', o1, true, b, b, 99, r, b, s, :so, f, c, :no, o1, d] + + Marshal.load("\004\b[*:\aso\"\nhelloii;\000;\000[\t\"\ahi:\ano\"\aoh:\ago;\000U:\020UserMarshal\"\nstuff;\000;\006@\n;\ac\vString0S:\024Struct::Pyramid\000f\0061;\a;\006@\t@\b/\000\000;\000\"\bhuhU:\030UserMarshalWithIvar[\006\"\fmy dataT@\b@\bih@\017@\b@\t;\000@\016@\f;\006@\021@\a").should == + obj + end + + it "loads an array having ivar" do + s = +'well' + s.instance_variable_set(:@foo, 10) + obj = ['5', s, 'hi'].extend(Meths, MethsMore) + obj.instance_variable_set(:@mix, s) + new_obj = Marshal.load("\004\bI[\b\"\0065I\"\twell\006:\t@fooi\017\"\ahi\006:\t@mix@\a") + new_obj.should == obj + new_obj.instance_variable_get(:@mix).should.equal? new_obj[1] + new_obj[1].instance_variable_get(:@foo).should == 10 + end + + it "loads an extended Array object containing a user-marshaled object" do + obj = [UserMarshal.new, UserMarshal.new].extend(Meths) + dump = "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT" + new_obj = Marshal.load(dump) + + new_obj.should == obj + obj_ancestors = class << obj; ancestors[1..-1]; end + new_obj_ancestors = class << new_obj; ancestors[1..-1]; end + obj_ancestors.should == new_obj_ancestors + end + end + + describe "for a Hash" do + it "loads an extended_user_hash with a parameter to initialize" do + obj = UserHashInitParams.new(:abc).extend(Meths) + + new_obj = Marshal.load("\004\bIe:\nMethsC:\027UserHashInitParams{\000\006:\a@a:\babc") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class].should == Meths + new_obj_metaclass_ancestors[@num_self_class+1].should == UserHashInitParams + end + + it "loads an extended hash object containing a user-marshaled object" do + obj = {a: UserMarshal.new}.extend(Meths) + + new_obj = Marshal.load("\004\be:\nMeths{\006:\006aU:\020UserMarshal\"\nstuff") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class].should == Meths + new_obj_metaclass_ancestors[@num_self_class+1].should == Hash + end + + it "preserves hash ivars when hash contains a string having ivar" do + s = +'string' + s.instance_variable_set :@string_ivar, 'string ivar' + h = { key: s } + h.instance_variable_set :@hash_ivar, 'hash ivar' + + unmarshalled = Marshal.load(Marshal.dump(h)) + unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar' + unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar' + end + + it "preserves compare_by_identity behaviour" do + h = { a: 1 } + h.compare_by_identity + unmarshalled = Marshal.load(Marshal.dump(h)) + unmarshalled.should.compare_by_identity? + + h = { a: 1 } + unmarshalled = Marshal.load(Marshal.dump(h)) + unmarshalled.should_not.compare_by_identity? + end + + it "preserves compare_by_identity behaviour for a Hash subclass" do + h = UserHash.new({ a: 1 }) + h.compare_by_identity + unmarshalled = Marshal.load(Marshal.dump(h)) + unmarshalled.should.compare_by_identity? + + h = UserHash.new({ a: 1 }) + unmarshalled = Marshal.load(Marshal.dump(h)) + unmarshalled.should_not.compare_by_identity? + end + + it "allocates an instance of the proper class when Hash subclass with compare_by_identity behaviour" do + h = UserHash.new({ a: 1 }) + h.compare_by_identity + + unmarshalled = Marshal.load(Marshal.dump(h)) + unmarshalled.should.kind_of?(UserHash) + end + end + + describe "for a Symbol" do + it "loads a Symbol" do + sym = Marshal.load("\004\b:\vsymbol") + sym.should == :symbol + sym.encoding.should == Encoding::US_ASCII + end + + it "loads a big Symbol" do + sym = ('big' * 100).to_sym + Marshal.load("\004\b:\002,\001#{'big' * 100}").should == sym + end + + it "loads an encoded Symbol" do + s = "\u2192" + + sym = Marshal.load("\x04\bI:\b\xE2\x86\x92\x06:\x06ET") + sym.should == s.encode("utf-8").to_sym + sym.encoding.should == Encoding::UTF_8 + + sym = Marshal.load("\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16") + sym.should == s.encode("utf-16").to_sym + sym.encoding.should == Encoding::UTF_16 + + sym = Marshal.load("\x04\bI:\a\x92!\x06:\rencoding\"\rUTF-16LE") + sym.should == s.encode("utf-16le").to_sym + sym.encoding.should == Encoding::UTF_16LE + + sym = Marshal.load("\x04\bI:\a!\x92\x06:\rencoding\"\rUTF-16BE") + sym.should == s.encode("utf-16be").to_sym + sym.encoding.should == Encoding::UTF_16BE + + sym = Marshal.load("\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP") + sym.should == s.encode("euc-jp").to_sym + sym.encoding.should == Encoding::EUC_JP + + sym = Marshal.load("\x04\bI:\a\x81\xA8\x06:\rencoding\"\x10Windows-31J") + sym.should == s.encode("sjis").to_sym + sym.encoding.should == Encoding::SJIS + end + + it "loads a binary encoded Symbol" do + s = "\u2192".dup.force_encoding("binary").to_sym + sym = Marshal.load("\x04\b:\b\xE2\x86\x92") + sym.should == s + sym.encoding.should == Encoding::BINARY + end + + it "loads multiple Symbols sharing the same encoding" do + # Note that the encoding is a link for the second Symbol + symbol1 = "I:\t\xE2\x82\xACa\x06:\x06ET" + symbol2 = "I:\t\xE2\x82\xACb\x06;\x06T" + dump = "\x04\b[\a#{symbol1}#{symbol2}" + value = Marshal.load(dump) + value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8] + expected = [ + "€a".dup.force_encoding(Encoding::UTF_8).to_sym, + "€b".dup.force_encoding(Encoding::UTF_8).to_sym + ] + value.should == expected + + value = Marshal.load("\x04\b[\b#{symbol1}#{symbol2};\x00") + value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8] + value.should == [*expected, expected[0]] + end + + it "raises ArgumentError when end of byte sequence reached before symbol characters end" do + Marshal.dump(:hello).should == "\x04\b:\nhello" + + -> { + Marshal.load("\x04\b:\nhel") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for a String" do + it "loads a string having ivar with ref to self" do + obj = +'hi' + obj.instance_variable_set(:@self, obj) + Marshal.load("\004\bI\"\ahi\006:\n@self@\000").should == obj + end + + it "loads a string through StringIO stream" do + require 'stringio' + obj = "This is a string which should be unmarshalled through StringIO stream!" + Marshal.load(StringIO.new(Marshal.dump(obj))).should == obj + end + + it "sets binmode if it is loading through StringIO stream" do + io = StringIO.new("\004\b:\vsymbol") + def io.binmode; raise "binmode"; end + -> { Marshal.load(io) }.should.raise(RuntimeError, "binmode") + end + + it "loads a string with an ivar" do + str = Marshal.load("\x04\bI\"\x00\x06:\t@fooI\"\bbar\x06:\x06EF") + str.instance_variable_get("@foo").should == "bar" + end + + it "loads a String subclass with custom constructor" do + str = Marshal.load("\x04\bC: UserCustomConstructorString\"\x00") + str.should.instance_of?(UserCustomConstructorString) + end + + it "loads a US-ASCII String" do + str = "abc".dup.force_encoding("us-ascii") + data = "\x04\bI\"\babc\x06:\x06EF" + result = Marshal.load(data) + result.should == str + result.encoding.should.equal?(Encoding::US_ASCII) + end + + it "loads a UTF-8 String" do + str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8") + data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET" + result = Marshal.load(data) + result.should == str + result.encoding.should.equal?(Encoding::UTF_8) + end + + it "loads a String in another encoding" do + str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le") + data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE" + result = Marshal.load(data) + result.should == str + result.encoding.should.equal?(Encoding::UTF_16LE) + end + + it "loads a String as BINARY if no encoding is specified at the end" do + str = "\xC3\xB8".dup.force_encoding("BINARY") + data = "\x04\b\"\a\xC3\xB8".dup.force_encoding("UTF-8") + result = Marshal.load(data) + result.encoding.should == Encoding::BINARY + result.should == str + end + + it "raises ArgumentError when end of byte sequence reached before string characters end" do + Marshal.dump("hello").should == "\x04\b\"\nhello" + + -> { + Marshal.load("\x04\b\"\nhel") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for a Struct" do + it "loads a extended_struct having fields with same objects" do + s = 'hi' + obj = Struct.new("Extended", :a, :b).new.extend(Meths) + dump = "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0" + Marshal.load(dump).should == obj + + obj.a = [:a, s] + obj.b = [:Meths, s] + dump = "\004\be:\nMethsS:\025Struct::Extended\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a" + Marshal.load(dump).should == obj + Struct.send(:remove_const, :Extended) + end + + it "loads a struct having ivar" do + obj = Struct.new("Thick").new + obj.instance_variable_set(:@foo, 5) + reloaded = Marshal.load("\004\bIS:\022Struct::Thick\000\006:\t@fooi\n") + reloaded.should == obj + reloaded.instance_variable_get(:@foo).should == 5 + Struct.send(:remove_const, :Thick) + end + + it "loads a struct having fields" do + obj = Struct.new("Ure1", :a, :b).new + Marshal.load("\004\bS:\021Struct::Ure1\a:\006a0:\006b0").should == obj + Struct.send(:remove_const, :Ure1) + end + + it "does not call initialize on the unmarshaled struct" do + threadlocal_key = MarshalSpec::StructWithUserInitialize::THREADLOCAL_KEY + + s = MarshalSpec::StructWithUserInitialize.new('foo') + Thread.current[threadlocal_key].should == ['foo'] + s.a.should == 'foo' + + Thread.current[threadlocal_key] = nil + + dumped = Marshal.dump(s) + loaded = Marshal.load(dumped) + + Thread.current[threadlocal_key].should == nil + loaded.a.should == 'foo' + end + end + + describe "for a Data" do + it "loads a Data" do + obj = MarshalSpec::DataSpec::Measure.new(100, 'km') + dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm" + Marshal.dump(obj).should == dumped + + Marshal.load(dumped).should == obj + end + + it "loads an extended Data" do + obj = MarshalSpec::DataSpec::MeasureExtended.new(100, "km") + dumped = "\x04\bS:+MarshalSpec::DataSpec::MeasureExtended\a:\vamountii:\tunit\"\akm" + Marshal.dump(obj).should == dumped + + Marshal.load(dumped).should == obj + end + + it "returns a frozen object" do + obj = MarshalSpec::DataSpec::Measure.new(100, 'km') + dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm" + Marshal.dump(obj).should == dumped + + Marshal.load(dumped).should.frozen? + end + end + + describe "for an Exception" do + it "loads a marshalled exception with no message" do + obj = Exception.new + loaded = Marshal.load("\004\bo:\016Exception\a:\abt0:\tmesg0") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + loaded = Marshal.load("\x04\bo:\x0EException\a:\tmesg0:\abt0") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + end + + it "loads a marshalled exception with a message" do + obj = Exception.new("foo") + loaded = Marshal.load("\004\bo:\016Exception\a:\abt0:\tmesg\"\bfoo") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + loaded = Marshal.load("\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt0") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + end + + it "loads a marshalled exception with a backtrace" do + obj = Exception.new("foo") + obj.set_backtrace(["foo/bar.rb:10"]) + loaded = Marshal.load("\004\bo:\016Exception\a:\abt[\006\"\022foo/bar.rb:10:\tmesg\"\bfoo") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + loaded = Marshal.load("\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt[\x06I\"\x12foo/bar.rb:10\x06;\aF") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + end + + it "loads an marshalled exception with ivars" do + s = 'hi' + arr = [:so, :so, s, s] + obj = Exception.new("foo") + obj.instance_variable_set :@arr, arr + + loaded = Marshal.load("\x04\bo:\x0EException\b:\tmesg\"\bfoo:\abt0:\t@arr[\t:\aso;\t\"\ahi@\b") + new_arr = loaded.instance_variable_get :@arr + + loaded.message.should == obj.message + new_arr.should == arr + end + end + + describe "for an Object" do + it "loads an object" do + Marshal.load("\004\bo:\vObject\000").should.is_a?(Object) + end + + it "loads an extended Object" do + obj = Object.new.extend(Meths) + + new_obj = Marshal.load("\004\be:\nMethso:\vObject\000") + + new_obj.class.should == obj.class + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object] + end + + it "loads an object having ivar" do + s = 'hi' + arr = [:so, :so, s, s] + obj = Object.new + obj.instance_variable_set :@str, arr + + new_obj = Marshal.load("\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a") + new_str = new_obj.instance_variable_get :@str + + new_str.should == arr + end + + it "loads an Object with a non-US-ASCII instance variable" do + ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym + obj = Marshal.load("\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06") + obj.instance_variables.should == [ivar] + obj.instance_variables[0].encoding.should == Encoding::UTF_8 + obj.instance_variable_get(ivar).should == 1 + end + + it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do + -> do + Marshal.load("\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd") + end.should.raise(ArgumentError) + end + + it "raises ArgumentError when end of byte sequence reached before class name end" do + Marshal.dump(Object.new).should == "\x04\bo:\vObject\x00" + + -> { + Marshal.load("\x04\bo:\vObj") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for an object responding to #marshal_dump and #marshal_load" do + it "loads a user-marshaled object" do + obj = UserMarshal.new + obj.data = :data + value = [obj, :data] + dump = Marshal.dump(value) + dump.should == "\x04\b[\aU:\x10UserMarshal:\tdata;\x06" + reloaded = Marshal.load(dump) + reloaded.should == value + end + end + + describe "for a user object" do + it "loads a user-marshaled extended object" do + obj = UserMarshal.new.extend(Meths) + + new_obj = Marshal.load("\004\bU:\020UserMarshal\"\nstuff") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal + end + + it "loads a UserObject" do + Marshal.load("\004\bo:\017UserObject\000").should.is_a?(UserObject) + end + + describe "that extends a core type other than Object or BasicObject" do + after :each do + MarshalSpec.reset_swapped_class + end + + it "raises ArgumentError if the resulting class does not extend the same type" do + MarshalSpec.set_swapped_class(Class.new(Hash)) + data = Marshal.dump(MarshalSpec::SwappedClass.new) + + MarshalSpec.set_swapped_class(Class.new(Array)) + -> { Marshal.load(data) }.should.raise(ArgumentError) + + MarshalSpec.set_swapped_class(Class.new) + -> { Marshal.load(data) }.should.raise(ArgumentError) + end + end + end + + describe "for a Regexp" do + ruby_version_is "4.1" do + it "raises FrozenError for an extended Regexp" do + -> { + Marshal.load("\004\be:\nMethse:\016MethsMore/\n[a-z]\000") + }.should.raise(FrozenError) + end + + it "raises FrozenError when regexp has instance variables" do + -> { + Marshal.load("\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/") + }.should.raise(FrozenError) + end + end + + ruby_version_is ""..."4.1" do + it "loads an extended Regexp" do + obj = /[a-z]/.dup.extend(Meths, MethsMore) + new_obj = Marshal.load("\004\be:\nMethse:\016MethsMore/\n[a-z]\000") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 3].should == + [Meths, MethsMore, Regexp] + end + + it "restore the regexp instance variables" do + obj = Regexp.new("hello") + obj.instance_variable_set(:@regexp_ivar, [42]) + + new_obj = Marshal.load("\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/") + new_obj.instance_variables.should == [:@regexp_ivar] + new_obj.instance_variable_get(:@regexp_ivar).should == [42] + end + end + + it "loads a Regexp subclass instance variables" do + obj = UserRegexp.new('abc') + obj.instance_variable_set(:@noise, 'much') + + new_obj = Marshal.load(Marshal.dump(obj)) + + new_obj.should == obj + new_obj.instance_variable_get(:@noise).should == 'much' + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 2].should == + [UserRegexp, Regexp] + end + + it "loads a Regexp subclass instance variables when it is extended with a module" do + obj = UserRegexp.new('').extend(Meths) + obj.instance_variable_set(:@noise, 'much') + + new_obj = Marshal.load("\004\bIe:\nMethsC:\017UserRegexp/\000\000\006:\v@noise\"\tmuch") + + new_obj.should == obj + new_obj.instance_variable_get(:@noise).should == 'much' + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 3].should == + [Meths, UserRegexp, Regexp] + end + + it "preserves Regexp encoding" do + source_object = Regexp.new("a".encode("utf-32le")) + regexp = Marshal.load(Marshal.dump(source_object)) + + regexp.encoding.should == Encoding::UTF_32LE + regexp.source.should == "a".encode("utf-32le") + end + + it "raises ArgumentError when end of byte sequence reached before source string end" do + Marshal.dump(/hello world/).should == "\x04\bI/\x10hello world\x00\x06:\x06EF" + + -> { + Marshal.load("\x04\bI/\x10hel") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for a Float" do + it "loads a Float NaN" do + obj = 0.0 / 0.0 + Marshal.load("\004\bf\bnan").to_s.should == obj.to_s + end + + it "loads a Float 1.3" do + Marshal.load("\004\bf\v1.3\000\314\315").should == 1.3 + end + + it "loads a Float -5.1867345e-22" do + obj = -5.1867345e-22 + Marshal.load("\004\bf\037-5.1867345000000008e-22\000\203_").should be_close(obj, 1e-30) + end + + it "loads a Float 1.1867345e+22" do + obj = 1.1867345e+22 + Marshal.load("\004\bf\0361.1867344999999999e+22\000\344@").should == obj + end + + it "raises ArgumentError when end of byte sequence reached before float string representation end" do + Marshal.dump(1.3).should == "\x04\bf\b1.3" + + -> { + Marshal.load("\004\bf\v1") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for an Integer" do + it "loads 0" do + Marshal.load("\004\bi\000").should == 0 + Marshal.load("\004\bi\005").should == 0 + end + + it "loads an Integer 8" do + Marshal.load("\004\bi\r" ).should == 8 + end + + it "loads and Integer -8" do + Marshal.load("\004\bi\363" ).should == -8 + end + + it "loads an Integer 1234" do + Marshal.load("\004\bi\002\322\004").should == 1234 + end + + it "loads an Integer -1234" do + Marshal.load("\004\bi\376.\373").should == -1234 + end + + it "loads an Integer 4611686018427387903" do + Marshal.load("\004\bl+\t\377\377\377\377\377\377\377?").should == 4611686018427387903 + end + + it "loads an Integer -4611686018427387903" do + Marshal.load("\004\bl-\t\377\377\377\377\377\377\377?").should == -4611686018427387903 + end + + it "loads an Integer 2361183241434822606847" do + Marshal.load("\004\bl+\n\377\377\377\377\377\377\377\377\177\000").should == 2361183241434822606847 + end + + it "loads an Integer -2361183241434822606847" do + Marshal.load("\004\bl-\n\377\377\377\377\377\377\377\377\177\000").should == -2361183241434822606847 + end + + it "raises ArgumentError if the input is too short" do + ["\004\bi", + "\004\bi\001", + "\004\bi\002", + "\004\bi\002\0", + "\004\bi\003", + "\004\bi\003\0", + "\004\bi\003\0\0", + "\004\bi\004", + "\004\bi\004\0", + "\004\bi\004\0\0", + "\004\bi\004\0\0\0"].each do |invalid| + -> { Marshal.load(invalid) }.should.raise(ArgumentError) + end + end + + if 0.size == 8 # for platforms like x86_64 + it "roundtrips 4611686018427387903 from dump/load correctly" do + Marshal.load(Marshal.dump(4611686018427387903)).should == 4611686018427387903 + end + end + end + + describe "for a Rational" do + it "loads" do + r = Marshal.load(Marshal.dump(Rational(1, 3))) + r.should == Rational(1, 3) + r.should.frozen? + end + end + + describe "for a Complex" do + it "loads" do + c = Marshal.load(Marshal.dump(Complex(4, 3))) + c.should == Complex(4, 3) + c.should.frozen? + end + end + + describe "for a Bignum" do + platform_is c_long_size: 64 do + context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do + it "dumps a Fixnum" do + val = Marshal.load("\004\bl+\ab:wU") + val.should == 1433877090 + val.class.should == Integer + end + + it "dumps an array containing multiple references to the Bignum as an array of Fixnum" do + arr = Marshal.load("\004\b[\al+\a\223BwU@\006") + arr.should == [1433879187, 1433879187] + arr.each { |v| v.class.should == Integer } + end + end + end + end + + describe "for a Time" do + it "loads" do + Marshal.load(Marshal.dump(Time.at(1))).should == Time.at(1) + end + + it "loads serialized instance variables" do + t = Time.new + t.instance_variable_set(:@foo, 'bar') + + Marshal.load(Marshal.dump(t)).instance_variable_get(:@foo).should == 'bar' + end + + it "loads Time objects stored as links" do + t = Time.new + + t1, t2 = Marshal.load(Marshal.dump([t, t])) + t1.should.equal? t2 + end + + it "keeps the local zone" do + with_timezone 'AST', 3 do + t = Time.local(2012, 1, 1) + Marshal.load(Marshal.dump(t)).zone.should == t.zone + end + end + + it "keeps UTC zone" do + t = Time.now.utc + t2 = Marshal.load(Marshal.dump(t)) + t2.should.utc? + end + + it "keeps the zone" do + t = nil + + with_timezone 'AST', 4 do + t = Time.local(2012, 1, 1) + end + + with_timezone 'EET', -2 do + Marshal.load(Marshal.dump(t)).zone.should == 'AST' + end + end + + it "keeps utc offset" do + t = Time.new(2007,11,1,15,25,0, "+09:00") + t2 = Marshal.load(Marshal.dump(t)) + t2.utc_offset.should == 32400 + end + + it "keeps nanoseconds" do + t = Time.now + Marshal.load(Marshal.dump(t)).nsec.should == t.nsec + end + + it "does not add any additional instance variable" do + t = Time.now + t2 = Marshal.load(Marshal.dump(t)) + t2.instance_variables.should.empty? + end + end + + describe "for nil" do + it "loads" do + Marshal.load("\x04\b0").should == nil + end + end + + describe "for true" do + it "loads" do + Marshal.load("\x04\bT").should == true + end + end + + describe "for false" do + it "loads" do + Marshal.load("\x04\bF").should == false + end + end + + describe "for a Class" do + it "loads" do + Marshal.load("\x04\bc\vString").should == String + end + + it "raises ArgumentError if given the name of a non-Module" do + -> { Marshal.load("\x04\bc\vKernel") }.should.raise(ArgumentError) + end + + it "raises ArgumentError if given a nonexistent class" do + -> { Marshal.load("\x04\bc\vStrung") }.should.raise(ArgumentError) + end + + it "raises ArgumentError when end of byte sequence reached before class name end" do + Marshal.dump(String).should == "\x04\bc\vString" + + -> { + Marshal.load("\x04\bc\vStr") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for a Module" do + it "loads a module" do + Marshal.load("\x04\bm\vKernel").should == Kernel + end + + it "raises ArgumentError if given the name of a non-Class" do + -> { Marshal.load("\x04\bm\vString") }.should.raise(ArgumentError) + end + + it "loads an old module" do + Marshal.load("\x04\bM\vKernel").should == Kernel + end + + it "raises ArgumentError when end of byte sequence reached before module name end" do + Marshal.dump(Kernel).should == "\x04\bm\vKernel" + + -> { + Marshal.load("\x04\bm\vKer") + }.should.raise(ArgumentError, "marshal data too short") + end + end + + describe "for a wrapped C pointer" do + it "loads" do + class DumpableDir < Dir + def _dump_data + path + end + def _load_data path + initialize(path) + end + end + + data = "\x04\bd:\x10DumpableDirI\"\x06.\x06:\x06ET" + + dir = Marshal.load(data) + begin + dir.path.should == '.' + ensure + dir.close + end + end + + it "raises TypeError when the local class is missing _load_data" do + class UnloadableDumpableDir < Dir + def _dump_data + path + end + # no _load_data + end + + data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET" + + -> { Marshal.load(data) }.should.raise(TypeError) + end + + it "raises ArgumentError when the local class is a regular object" do + data = "\004\bd:\020UserDefined\0" + + -> { Marshal.load(data) }.should.raise(ArgumentError) + end + end + + describe "when a class does not exist in the namespace" do + before :each do + NamespaceTest.send(:const_set, :SameName, Class.new) + @data = Marshal.dump(NamespaceTest::SameName.new) + NamespaceTest.send(:remove_const, :SameName) + end + + it "raises an ArgumentError" do + message = "undefined class/module NamespaceTest::SameName" + -> { Marshal.load(@data) }.should.raise(ArgumentError, message) + end + end + + it "raises an ArgumentError with full constant name when the dumped constant is missing" do + NamespaceTest.send(:const_set, :KaBoom, Class.new) + @data = Marshal.dump(NamespaceTest::KaBoom.new) + NamespaceTest.send(:remove_const, :KaBoom) + + -> { Marshal.load(@data) }.should.raise(ArgumentError, /NamespaceTest::KaBoom/) + end end diff --git a/spec/ruby/core/marshal/restore_spec.rb b/spec/ruby/core/marshal/restore_spec.rb index 7e75d7dea6..3135f881d4 100644 --- a/spec/ruby/core/marshal/restore_spec.rb +++ b/spec/ruby/core/marshal/restore_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/load' describe "Marshal.restore" do - it_behaves_like :marshal_load, :restore + it "is an alias of Marshal.load" do + Marshal.method(:restore).should == Marshal.method(:load) + end end diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb deleted file mode 100644 index 02c8e7f0b1..0000000000 --- a/spec/ruby/core/marshal/shared/load.rb +++ /dev/null @@ -1,1291 +0,0 @@ -# encoding: binary -require_relative '../fixtures/marshal_data' - -describe :marshal_load, shared: true do - before :all do - @num_self_class = 1 - end - - it "raises an ArgumentError when the dumped data is truncated" do - obj = {first: 1, second: 2, third: 3} - -> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should.raise(ArgumentError, "marshal data too short") - end - - it "raises an ArgumentError when the argument is empty String" do - -> { Marshal.send(@method, "") }.should.raise(ArgumentError, "marshal data too short") - end - - it "raises an ArgumentError when the dumped class is missing" do - Object.send(:const_set, :KaBoom, Class.new) - kaboom = Marshal.dump(KaBoom.new) - Object.send(:remove_const, :KaBoom) - - -> { Marshal.send(@method, kaboom) }.should.raise(ArgumentError) - end - - describe "when called with freeze: true" do - it "returns frozen strings" do - string = Marshal.send(@method, Marshal.dump("foo"), freeze: true) - string.should == "foo" - string.should.frozen? - - utf8_string = "foo".encode(Encoding::UTF_8) - string = Marshal.send(@method, Marshal.dump(utf8_string), freeze: true) - string.should == utf8_string - string.should.frozen? - end - - it "returns frozen arrays" do - array = Marshal.send(@method, Marshal.dump([1, 2, 3]), freeze: true) - array.should == [1, 2, 3] - array.should.frozen? - end - - it "returns frozen hashes" do - hash = Marshal.send(@method, Marshal.dump({foo: 42}), freeze: true) - hash.should == {foo: 42} - hash.should.frozen? - end - - it "returns frozen regexps" do - regexp = Marshal.send(@method, Marshal.dump(/foo/), freeze: true) - regexp.should == /foo/ - regexp.should.frozen? - end - - it "returns frozen structs" do - struct = Marshal.send(@method, Marshal.dump(MarshalSpec::StructToDump.new(1, 2)), freeze: true) - struct.should == MarshalSpec::StructToDump.new(1, 2) - struct.should.frozen? - end - - it "returns frozen objects" do - source_object = Object.new - - object = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - object.should.frozen? - end - - describe "deep freezing" do - it "returns hashes with frozen keys and values" do - key = Object.new - value = Object.new - source_object = {key => value} - - hash = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - hash.size.should == 1 - hash.keys[0].should.frozen? - hash.values[0].should.frozen? - end - - it "returns arrays with frozen elements" do - object = Object.new - source_object = [object] - - array = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - array.size.should == 1 - array[0].should.frozen? - end - - it "returns structs with frozen members" do - object1 = Object.new - object2 = Object.new - source_object = MarshalSpec::StructToDump.new(object1, object2) - - struct = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - struct.a.should.frozen? - struct.b.should.frozen? - end - - it "returns objects with frozen instance variables" do - source_object = Object.new - instance_variable = Object.new - source_object.instance_variable_set(:@a, instance_variable) - - object = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - object.instance_variable_get(:@a).should != nil - object.instance_variable_get(:@a).should.frozen? - end - - it "deduplicates frozen strings" do - source_object = ["foo" + "bar", "foobar"] - object = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - - object[0].should.equal?(object[1]) - end - end - - it "does not freeze modules" do - object = Marshal.send(@method, Marshal.dump(Kernel), freeze: true) - object.should_not.frozen? - Kernel.should_not.frozen? - end - - it "does not freeze classes" do - object = Marshal.send(@method, Marshal.dump(Object), freeze: true) - object.should_not.frozen? - Object.should_not.frozen? - end - - it "does freeze extended objects" do - object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", freeze: true) - object.should.frozen? - end - - it "does freeze extended objects with instance variables" do - object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x06:\n@ivarT", freeze: true) - object.should.frozen? - end - - it "returns frozen object having #_dump method" do - object = Marshal.send(@method, Marshal.dump(UserDefined.new), freeze: true) - object.should.frozen? - end - - it "returns frozen object responding to #marshal_dump and #marshal_load" do - object = Marshal.send(@method, Marshal.dump(UserMarshal.new), freeze: true) - object.should.frozen? - end - - it "returns frozen object extended by a module" do - object = Object.new - object.extend(MarshalSpec::ModuleToExtendBy) - - object = Marshal.send(@method, Marshal.dump(object), freeze: true) - object.should.frozen? - end - - it "does not call freeze method" do - object = MarshalSpec::ObjectWithFreezeRaisingException.new - object = Marshal.send(@method, Marshal.dump(object), freeze: true) - object.should.frozen? - end - - it "returns frozen object even if object does not respond to freeze method" do - object = MarshalSpec::ObjectWithoutFreeze.new - object = Marshal.send(@method, Marshal.dump(object), freeze: true) - object.should.frozen? - end - - it "returns a frozen object when is an instance of String/Array/Regexp/Hash subclass and has instance variables" do - source_object = UserString.new - source_object.instance_variable_set(:@foo, "bar") - - object = Marshal.send(@method, Marshal.dump(source_object), freeze: true) - object.should.frozen? - end - - describe "when called with a proc" do - it "call the proc with frozen objects" do - arr = [] - s = +'hi' - s.instance_variable_set(:@foo, 5) - st = Struct.new("Brittle", :a).new - st.instance_variable_set(:@clue, 'none') - st.a = 0.0 - h = Hash.new('def') - h['nine'] = 9 - a = [:a, :b, :c] - a.instance_variable_set(:@two, 2) - obj = [s, 10, s, s, st, a] - obj.instance_variable_set(:@zoo, 'ant') - proc = Proc.new { |o| arr << o; o} - - Marshal.send( - @method, - "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", - proc, - freeze: true, - ) - - arr.should == [ - false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st, - :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]], - ] - - arr.each do |v| - v.should.frozen? - end - - Struct.send(:remove_const, :Brittle) - end - - it "does not freeze the object returned by the proc" do - string = Marshal.send(@method, Marshal.dump("foo"), proc { |o| o.upcase }, freeze: true) - string.should == "FOO" - string.should_not.frozen? - end - end - end - - describe "when called with a proc" do - it "call the proc with fully initialized strings" do - utf8_string = "foo".encode(Encoding::UTF_8) - Marshal.send(@method, Marshal.dump(utf8_string), proc { |arg| - if arg.is_a?(String) - arg.should == utf8_string - arg.encoding.should == Encoding::UTF_8 - end - arg - }) - end - - it "no longer mutate the object after it was passed to the proc" do - string = Marshal.load(Marshal.dump("foo"), :freeze.to_proc) - string.should.frozen? - end - - it "call the proc with extended objects" do - objs = [] - obj = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", Proc.new { |o| objs << o; o }) - objs.should == [obj] - end - - it "returns the value of the proc" do - Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4] - end - - it "calls the proc for recursively visited data" do - a = [1] - a << a - ret = [] - Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg.inspect; arg }) - ret[0].should == 1.inspect - ret[1].should == a.inspect - ret.size.should == 2 - end - - it "loads an Array with proc" do - arr = [] - s = +'hi' - s.instance_variable_set(:@foo, 5) - st = Struct.new("Brittle", :a).new - st.instance_variable_set(:@clue, 'none') - st.a = 0.0 - h = Hash.new('def') - h['nine'] = 9 - a = [:a, :b, :c] - a.instance_variable_set(:@two, 2) - obj = [s, 10, s, s, st, a] - obj.instance_variable_set(:@zoo, 'ant') - proc = Proc.new { |o| arr << o.dup; o} - - Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc) - - arr.should == [ - false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st, - :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]], - ] - Struct.send(:remove_const, :Brittle) - end - end - - describe "when called with nil for the proc argument" do - it "behaves as if no proc argument was passed" do - a = [1] - a << a - b = Marshal.send(@method, Marshal.dump(a), nil) - b.should == a - end - end - - describe "when called on objects with custom _dump methods" do - it "does not set instance variables of an object with user-defined _dump/_load" do - # this string represents: <#UserPreviouslyDefinedWithInitializedIvar @field2=7 @field1=6> - dump_str = "\004\bu:-UserPreviouslyDefinedWithInitializedIvar\a:\f@field2i\f:\f@field1i\v" - - UserPreviouslyDefinedWithInitializedIvar.should_receive(:_load).and_return(UserPreviouslyDefinedWithInitializedIvar.new) - marshaled_obj = Marshal.send(@method, dump_str) - - marshaled_obj.should.instance_of?(UserPreviouslyDefinedWithInitializedIvar) - marshaled_obj.field1.should == nil - marshaled_obj.field2.should == nil - end - - it "loads the String in non US-ASCII and non UTF-8 encoding" do - source_object = UserDefinedString.new("a".encode("windows-1251")) - object = Marshal.send(@method, Marshal.dump(source_object)) - object.string.should == "a".encode("windows-1251") - end - - it "loads the String in multibyte encoding" do - source_object = UserDefinedString.new("a".encode("utf-32le")) - object = Marshal.send(@method, Marshal.dump(source_object)) - object.string.should == "a".encode("utf-32le") - end - - describe "that returns an immediate value" do - it "loads an array containing an instance of the object, followed by multiple instances of another object" do - str = "string" - - # this string represents: [<#UserDefinedImmediate A>, <#String "string">, <#String "string">] - marshaled_obj = Marshal.send(@method, "\004\b[\bu:\031UserDefinedImmediate\000\"\vstring@\a") - - marshaled_obj.should == [nil, str, str] - end - - it "loads any structure with multiple references to the same object, followed by multiple instances of another object" do - str = "string" - - # this string represents: {a: <#UserDefinedImmediate A>, b: <#UserDefinedImmediate A>, c: <#String "string">, d: <#String "string">} - hash_dump = "\x04\b{\t:\x06aIu:\x19UserDefinedImmediate\x00\x06:\x06ET:\x06b@\x06:\x06cI\"\vstring\x06;\aT:\x06d@\a" - - marshaled_obj = Marshal.send(@method, hash_dump) - marshaled_obj.should == {a: nil, b: nil, c: str, d: str} - - # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate A>, <#String "string">, <#String "string">] - array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ET@\x06I\"\vstring\x06;\x06T@\a" - - marshaled_obj = Marshal.send(@method, array_dump) - marshaled_obj.should == [nil, nil, str, str] - end - - it "loads an array containing references to multiple instances of the object, followed by multiple instances of another object" do - str = "string" - - # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate B>, <#String "string">, <#String "string">] - array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ETIu;\x00\x00\x06;\x06TI\"\vstring\x06;\x06T@\b" - - marshaled_obj = Marshal.send(@method, array_dump) - marshaled_obj.should == [nil, nil, str, str] - end - end - end - - it "loads an array containing objects having _dump method, and with proc" do - arr = [] - myproc = Proc.new { |o| arr << o.dup; o } - o1 = UserDefined.new; - o2 = UserDefinedWithIvar.new - obj = [o1, o2, o1, o2] - - Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc) - - arr[0].should == o1 - arr[1].should == o2 - arr[2].should == obj - arr.size.should == 3 - end - - it "loads an array containing objects having marshal_dump method, and with proc" do - arr = [] - proc = Proc.new { |o| arr << o.dup; o } - o1 = UserMarshal.new - o2 = UserMarshalWithIvar.new - - Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc) - - arr[0].should == 'stuff' - arr[1].should == o1 - arr[2].should == 'my data' - arr[3].should == ['my data'] - arr[4].should == o2 - arr[5].should == [o1, o2, o1, o2] - - arr.size.should == 6 - end - - it "assigns classes to nested subclasses of Array correctly" do - arr = ArraySub.new(ArraySub.new) - arr_dump = Marshal.dump(arr) - Marshal.send(@method, arr_dump).class.should == ArraySub - end - - it "loads subclasses of Array with overridden << and push correctly" do - arr = ArraySubPush.new - arr[0] = '1' - arr_dump = Marshal.dump(arr) - Marshal.send(@method, arr_dump).should == arr - end - - it "raises a TypeError with bad Marshal version" do - marshal_data = +'\xff\xff' - marshal_data[0] = (Marshal::MAJOR_VERSION).chr - marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr - - -> { Marshal.send(@method, marshal_data) }.should.raise(TypeError) - - marshal_data = +'\xff\xff' - marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr - marshal_data[1] = (Marshal::MINOR_VERSION).chr - - -> { Marshal.send(@method, marshal_data) }.should.raise(TypeError) - end - - it "raises EOFError on loading an empty file" do - temp_file = tmp("marshal.rubyspec.tmp.#{Process.pid}") - file = File.new(temp_file, "w+") - begin - -> { Marshal.send(@method, file) }.should.raise(EOFError) - ensure - file.close - rm_r temp_file - end - end - - # Note: Ruby 1.9 should be compatible with older marshal format - MarshalSpec::DATA.each do |description, (object, marshal, attributes)| - it "loads a #{description}" do - Marshal.send(@method, marshal).should == object - end - end - - MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)| - it "loads a #{description}" do - Marshal.send(@method, marshal).should == object - end - end - - describe "for an Array" do - it "loads an array containing the same objects" do - s = 'oh' - b = 'hi' - r = // - d = [b, :no, s, :go] - c = String - f = 1.0 - - o1 = UserMarshalWithIvar.new; o2 = UserMarshal.new - - obj = [:so, 'hello', 100, :so, :so, d, :so, o2, :so, :no, o2, - :go, c, nil, Struct::Pyramid.new, f, :go, :no, s, b, r, - :so, 'huh', o1, true, b, b, 99, r, b, s, :so, f, c, :no, o1, d] - - Marshal.send(@method, "\004\b[*:\aso\"\nhelloii;\000;\000[\t\"\ahi:\ano\"\aoh:\ago;\000U:\020UserMarshal\"\nstuff;\000;\006@\n;\ac\vString0S:\024Struct::Pyramid\000f\0061;\a;\006@\t@\b/\000\000;\000\"\bhuhU:\030UserMarshalWithIvar[\006\"\fmy dataT@\b@\bih@\017@\b@\t;\000@\016@\f;\006@\021@\a").should == - obj - end - - it "loads an array having ivar" do - s = +'well' - s.instance_variable_set(:@foo, 10) - obj = ['5', s, 'hi'].extend(Meths, MethsMore) - obj.instance_variable_set(:@mix, s) - new_obj = Marshal.send(@method, "\004\bI[\b\"\0065I\"\twell\006:\t@fooi\017\"\ahi\006:\t@mix@\a") - new_obj.should == obj - new_obj.instance_variable_get(:@mix).should.equal? new_obj[1] - new_obj[1].instance_variable_get(:@foo).should == 10 - end - - it "loads an extended Array object containing a user-marshaled object" do - obj = [UserMarshal.new, UserMarshal.new].extend(Meths) - dump = "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT" - new_obj = Marshal.send(@method, dump) - - new_obj.should == obj - obj_ancestors = class << obj; ancestors[1..-1]; end - new_obj_ancestors = class << new_obj; ancestors[1..-1]; end - obj_ancestors.should == new_obj_ancestors - end - end - - describe "for a Hash" do - it "loads an extended_user_hash with a parameter to initialize" do - obj = UserHashInitParams.new(:abc).extend(Meths) - - new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\027UserHashInitParams{\000\006:\a@a:\babc") - - new_obj.should == obj - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class].should == Meths - new_obj_metaclass_ancestors[@num_self_class+1].should == UserHashInitParams - end - - it "loads an extended hash object containing a user-marshaled object" do - obj = {a: UserMarshal.new}.extend(Meths) - - new_obj = Marshal.send(@method, "\004\be:\nMeths{\006:\006aU:\020UserMarshal\"\nstuff") - - new_obj.should == obj - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class].should == Meths - new_obj_metaclass_ancestors[@num_self_class+1].should == Hash - end - - it "preserves hash ivars when hash contains a string having ivar" do - s = +'string' - s.instance_variable_set :@string_ivar, 'string ivar' - h = { key: s } - h.instance_variable_set :@hash_ivar, 'hash ivar' - - unmarshalled = Marshal.send(@method, Marshal.dump(h)) - unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar' - unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar' - end - - it "preserves compare_by_identity behaviour" do - h = { a: 1 } - h.compare_by_identity - unmarshalled = Marshal.send(@method, Marshal.dump(h)) - unmarshalled.should.compare_by_identity? - - h = { a: 1 } - unmarshalled = Marshal.send(@method, Marshal.dump(h)) - unmarshalled.should_not.compare_by_identity? - end - - it "preserves compare_by_identity behaviour for a Hash subclass" do - h = UserHash.new({ a: 1 }) - h.compare_by_identity - unmarshalled = Marshal.send(@method, Marshal.dump(h)) - unmarshalled.should.compare_by_identity? - - h = UserHash.new({ a: 1 }) - unmarshalled = Marshal.send(@method, Marshal.dump(h)) - unmarshalled.should_not.compare_by_identity? - end - - it "allocates an instance of the proper class when Hash subclass with compare_by_identity behaviour" do - h = UserHash.new({ a: 1 }) - h.compare_by_identity - - unmarshalled = Marshal.send(@method, Marshal.dump(h)) - unmarshalled.should.kind_of?(UserHash) - end - end - - describe "for a Symbol" do - it "loads a Symbol" do - sym = Marshal.send(@method, "\004\b:\vsymbol") - sym.should == :symbol - sym.encoding.should == Encoding::US_ASCII - end - - it "loads a big Symbol" do - sym = ('big' * 100).to_sym - Marshal.send(@method, "\004\b:\002,\001#{'big' * 100}").should == sym - end - - it "loads an encoded Symbol" do - s = "\u2192" - - sym = Marshal.send(@method, "\x04\bI:\b\xE2\x86\x92\x06:\x06ET") - sym.should == s.encode("utf-8").to_sym - sym.encoding.should == Encoding::UTF_8 - - sym = Marshal.send(@method, "\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16") - sym.should == s.encode("utf-16").to_sym - sym.encoding.should == Encoding::UTF_16 - - sym = Marshal.send(@method, "\x04\bI:\a\x92!\x06:\rencoding\"\rUTF-16LE") - sym.should == s.encode("utf-16le").to_sym - sym.encoding.should == Encoding::UTF_16LE - - sym = Marshal.send(@method, "\x04\bI:\a!\x92\x06:\rencoding\"\rUTF-16BE") - sym.should == s.encode("utf-16be").to_sym - sym.encoding.should == Encoding::UTF_16BE - - sym = Marshal.send(@method, "\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP") - sym.should == s.encode("euc-jp").to_sym - sym.encoding.should == Encoding::EUC_JP - - sym = Marshal.send(@method, "\x04\bI:\a\x81\xA8\x06:\rencoding\"\x10Windows-31J") - sym.should == s.encode("sjis").to_sym - sym.encoding.should == Encoding::SJIS - end - - it "loads a binary encoded Symbol" do - s = "\u2192".dup.force_encoding("binary").to_sym - sym = Marshal.send(@method, "\x04\b:\b\xE2\x86\x92") - sym.should == s - sym.encoding.should == Encoding::BINARY - end - - it "loads multiple Symbols sharing the same encoding" do - # Note that the encoding is a link for the second Symbol - symbol1 = "I:\t\xE2\x82\xACa\x06:\x06ET" - symbol2 = "I:\t\xE2\x82\xACb\x06;\x06T" - dump = "\x04\b[\a#{symbol1}#{symbol2}" - value = Marshal.send(@method, dump) - value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8] - expected = [ - "€a".dup.force_encoding(Encoding::UTF_8).to_sym, - "€b".dup.force_encoding(Encoding::UTF_8).to_sym - ] - value.should == expected - - value = Marshal.send(@method, "\x04\b[\b#{symbol1}#{symbol2};\x00") - value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8] - value.should == [*expected, expected[0]] - end - - it "raises ArgumentError when end of byte sequence reached before symbol characters end" do - Marshal.dump(:hello).should == "\x04\b:\nhello" - - -> { - Marshal.send(@method, "\x04\b:\nhel") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for a String" do - it "loads a string having ivar with ref to self" do - obj = +'hi' - obj.instance_variable_set(:@self, obj) - Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj - end - - it "loads a string through StringIO stream" do - require 'stringio' - obj = "This is a string which should be unmarshalled through StringIO stream!" - Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj - end - - it "sets binmode if it is loading through StringIO stream" do - io = StringIO.new("\004\b:\vsymbol") - def io.binmode; raise "binmode"; end - -> { Marshal.load(io) }.should.raise(RuntimeError, "binmode") - end - - it "loads a string with an ivar" do - str = Marshal.send(@method, "\x04\bI\"\x00\x06:\t@fooI\"\bbar\x06:\x06EF") - str.instance_variable_get("@foo").should == "bar" - end - - it "loads a String subclass with custom constructor" do - str = Marshal.send(@method, "\x04\bC: UserCustomConstructorString\"\x00") - str.should.instance_of?(UserCustomConstructorString) - end - - it "loads a US-ASCII String" do - str = "abc".dup.force_encoding("us-ascii") - data = "\x04\bI\"\babc\x06:\x06EF" - result = Marshal.send(@method, data) - result.should == str - result.encoding.should.equal?(Encoding::US_ASCII) - end - - it "loads a UTF-8 String" do - str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8") - data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET" - result = Marshal.send(@method, data) - result.should == str - result.encoding.should.equal?(Encoding::UTF_8) - end - - it "loads a String in another encoding" do - str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le") - data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE" - result = Marshal.send(@method, data) - result.should == str - result.encoding.should.equal?(Encoding::UTF_16LE) - end - - it "loads a String as BINARY if no encoding is specified at the end" do - str = "\xC3\xB8".dup.force_encoding("BINARY") - data = "\x04\b\"\a\xC3\xB8".dup.force_encoding("UTF-8") - result = Marshal.send(@method, data) - result.encoding.should == Encoding::BINARY - result.should == str - end - - it "raises ArgumentError when end of byte sequence reached before string characters end" do - Marshal.dump("hello").should == "\x04\b\"\nhello" - - -> { - Marshal.send(@method, "\x04\b\"\nhel") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for a Struct" do - it "loads a extended_struct having fields with same objects" do - s = 'hi' - obj = Struct.new("Extended", :a, :b).new.extend(Meths) - dump = "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0" - Marshal.send(@method, dump).should == obj - - obj.a = [:a, s] - obj.b = [:Meths, s] - dump = "\004\be:\nMethsS:\025Struct::Extended\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a" - Marshal.send(@method, dump).should == obj - Struct.send(:remove_const, :Extended) - end - - it "loads a struct having ivar" do - obj = Struct.new("Thick").new - obj.instance_variable_set(:@foo, 5) - reloaded = Marshal.send(@method, "\004\bIS:\022Struct::Thick\000\006:\t@fooi\n") - reloaded.should == obj - reloaded.instance_variable_get(:@foo).should == 5 - Struct.send(:remove_const, :Thick) - end - - it "loads a struct having fields" do - obj = Struct.new("Ure1", :a, :b).new - Marshal.send(@method, "\004\bS:\021Struct::Ure1\a:\006a0:\006b0").should == obj - Struct.send(:remove_const, :Ure1) - end - - it "does not call initialize on the unmarshaled struct" do - threadlocal_key = MarshalSpec::StructWithUserInitialize::THREADLOCAL_KEY - - s = MarshalSpec::StructWithUserInitialize.new('foo') - Thread.current[threadlocal_key].should == ['foo'] - s.a.should == 'foo' - - Thread.current[threadlocal_key] = nil - - dumped = Marshal.dump(s) - loaded = Marshal.send(@method, dumped) - - Thread.current[threadlocal_key].should == nil - loaded.a.should == 'foo' - end - end - - describe "for a Data" do - it "loads a Data" do - obj = MarshalSpec::DataSpec::Measure.new(100, 'km') - dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm" - Marshal.dump(obj).should == dumped - - Marshal.send(@method, dumped).should == obj - end - - it "loads an extended Data" do - obj = MarshalSpec::DataSpec::MeasureExtended.new(100, "km") - dumped = "\x04\bS:+MarshalSpec::DataSpec::MeasureExtended\a:\vamountii:\tunit\"\akm" - Marshal.dump(obj).should == dumped - - Marshal.send(@method, dumped).should == obj - end - - it "returns a frozen object" do - obj = MarshalSpec::DataSpec::Measure.new(100, 'km') - dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm" - Marshal.dump(obj).should == dumped - - Marshal.send(@method, dumped).should.frozen? - end - end - - describe "for an Exception" do - it "loads a marshalled exception with no message" do - obj = Exception.new - loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg0") - loaded.message.should == obj.message - loaded.backtrace.should == obj.backtrace - loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesg0:\abt0") - loaded.message.should == obj.message - loaded.backtrace.should == obj.backtrace - end - - it "loads a marshalled exception with a message" do - obj = Exception.new("foo") - loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg\"\bfoo") - loaded.message.should == obj.message - loaded.backtrace.should == obj.backtrace - loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt0") - loaded.message.should == obj.message - loaded.backtrace.should == obj.backtrace - end - - it "loads a marshalled exception with a backtrace" do - obj = Exception.new("foo") - obj.set_backtrace(["foo/bar.rb:10"]) - loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt[\006\"\022foo/bar.rb:10:\tmesg\"\bfoo") - loaded.message.should == obj.message - loaded.backtrace.should == obj.backtrace - loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt[\x06I\"\x12foo/bar.rb:10\x06;\aF") - loaded.message.should == obj.message - loaded.backtrace.should == obj.backtrace - end - - it "loads an marshalled exception with ivars" do - s = 'hi' - arr = [:so, :so, s, s] - obj = Exception.new("foo") - obj.instance_variable_set :@arr, arr - - loaded = Marshal.send(@method, "\x04\bo:\x0EException\b:\tmesg\"\bfoo:\abt0:\t@arr[\t:\aso;\t\"\ahi@\b") - new_arr = loaded.instance_variable_get :@arr - - loaded.message.should == obj.message - new_arr.should == arr - end - end - - describe "for an Object" do - it "loads an object" do - Marshal.send(@method, "\004\bo:\vObject\000").should.is_a?(Object) - end - - it "loads an extended Object" do - obj = Object.new.extend(Meths) - - new_obj = Marshal.send(@method, "\004\be:\nMethso:\vObject\000") - - new_obj.class.should == obj.class - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object] - end - - it "loads an object having ivar" do - s = 'hi' - arr = [:so, :so, s, s] - obj = Object.new - obj.instance_variable_set :@str, arr - - new_obj = Marshal.send(@method, "\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a") - new_str = new_obj.instance_variable_get :@str - - new_str.should == arr - end - - it "loads an Object with a non-US-ASCII instance variable" do - ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym - obj = Marshal.send(@method, "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06") - obj.instance_variables.should == [ivar] - obj.instance_variables[0].encoding.should == Encoding::UTF_8 - obj.instance_variable_get(ivar).should == 1 - end - - it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do - -> do - Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd") - end.should.raise(ArgumentError) - end - - it "raises ArgumentError when end of byte sequence reached before class name end" do - Marshal.dump(Object.new).should == "\x04\bo:\vObject\x00" - - -> { - Marshal.send(@method, "\x04\bo:\vObj") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for an object responding to #marshal_dump and #marshal_load" do - it "loads a user-marshaled object" do - obj = UserMarshal.new - obj.data = :data - value = [obj, :data] - dump = Marshal.dump(value) - dump.should == "\x04\b[\aU:\x10UserMarshal:\tdata;\x06" - reloaded = Marshal.load(dump) - reloaded.should == value - end - end - - describe "for a user object" do - it "loads a user-marshaled extended object" do - obj = UserMarshal.new.extend(Meths) - - new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff") - - new_obj.should == obj - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal - end - - it "loads a UserObject" do - Marshal.send(@method, "\004\bo:\017UserObject\000").should.is_a?(UserObject) - end - - describe "that extends a core type other than Object or BasicObject" do - after :each do - MarshalSpec.reset_swapped_class - end - - it "raises ArgumentError if the resulting class does not extend the same type" do - MarshalSpec.set_swapped_class(Class.new(Hash)) - data = Marshal.dump(MarshalSpec::SwappedClass.new) - - MarshalSpec.set_swapped_class(Class.new(Array)) - -> { Marshal.send(@method, data) }.should.raise(ArgumentError) - - MarshalSpec.set_swapped_class(Class.new) - -> { Marshal.send(@method, data) }.should.raise(ArgumentError) - end - end - end - - describe "for a Regexp" do - ruby_version_is "4.1" do - it "raises FrozenError for an extended Regexp" do - -> { - Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000") - }.should.raise(FrozenError) - end - - it "raises FrozenError when regexp has instance variables" do - -> { - Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/") - }.should.raise(FrozenError) - end - end - - ruby_version_is ""..."4.1" do - it "loads an extended Regexp" do - obj = /[a-z]/.dup.extend(Meths, MethsMore) - new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000") - - new_obj.should == obj - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class, 3].should == - [Meths, MethsMore, Regexp] - end - - it "restore the regexp instance variables" do - obj = Regexp.new("hello") - obj.instance_variable_set(:@regexp_ivar, [42]) - - new_obj = Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/") - new_obj.instance_variables.should == [:@regexp_ivar] - new_obj.instance_variable_get(:@regexp_ivar).should == [42] - end - end - - it "loads a Regexp subclass instance variables" do - obj = UserRegexp.new('abc') - obj.instance_variable_set(:@noise, 'much') - - new_obj = Marshal.send(@method, Marshal.dump(obj)) - - new_obj.should == obj - new_obj.instance_variable_get(:@noise).should == 'much' - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class, 2].should == - [UserRegexp, Regexp] - end - - it "loads a Regexp subclass instance variables when it is extended with a module" do - obj = UserRegexp.new('').extend(Meths) - obj.instance_variable_set(:@noise, 'much') - - new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\017UserRegexp/\000\000\006:\v@noise\"\tmuch") - - new_obj.should == obj - new_obj.instance_variable_get(:@noise).should == 'much' - new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class, 3].should == - [Meths, UserRegexp, Regexp] - end - - it "preserves Regexp encoding" do - source_object = Regexp.new("a".encode("utf-32le")) - regexp = Marshal.send(@method, Marshal.dump(source_object)) - - regexp.encoding.should == Encoding::UTF_32LE - regexp.source.should == "a".encode("utf-32le") - end - - it "raises ArgumentError when end of byte sequence reached before source string end" do - Marshal.dump(/hello world/).should == "\x04\bI/\x10hello world\x00\x06:\x06EF" - - -> { - Marshal.send(@method, "\x04\bI/\x10hel") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for a Float" do - it "loads a Float NaN" do - obj = 0.0 / 0.0 - Marshal.send(@method, "\004\bf\bnan").to_s.should == obj.to_s - end - - it "loads a Float 1.3" do - Marshal.send(@method, "\004\bf\v1.3\000\314\315").should == 1.3 - end - - it "loads a Float -5.1867345e-22" do - obj = -5.1867345e-22 - Marshal.send(@method, "\004\bf\037-5.1867345000000008e-22\000\203_").should be_close(obj, 1e-30) - end - - it "loads a Float 1.1867345e+22" do - obj = 1.1867345e+22 - Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj - end - - it "raises ArgumentError when end of byte sequence reached before float string representation end" do - Marshal.dump(1.3).should == "\x04\bf\b1.3" - - -> { - Marshal.send(@method, "\004\bf\v1") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for an Integer" do - it "loads 0" do - Marshal.send(@method, "\004\bi\000").should == 0 - Marshal.send(@method, "\004\bi\005").should == 0 - end - - it "loads an Integer 8" do - Marshal.send(@method, "\004\bi\r" ).should == 8 - end - - it "loads and Integer -8" do - Marshal.send(@method, "\004\bi\363" ).should == -8 - end - - it "loads an Integer 1234" do - Marshal.send(@method, "\004\bi\002\322\004").should == 1234 - end - - it "loads an Integer -1234" do - Marshal.send(@method, "\004\bi\376.\373").should == -1234 - end - - it "loads an Integer 4611686018427387903" do - Marshal.send(@method, "\004\bl+\t\377\377\377\377\377\377\377?").should == 4611686018427387903 - end - - it "loads an Integer -4611686018427387903" do - Marshal.send(@method, "\004\bl-\t\377\377\377\377\377\377\377?").should == -4611686018427387903 - end - - it "loads an Integer 2361183241434822606847" do - Marshal.send(@method, "\004\bl+\n\377\377\377\377\377\377\377\377\177\000").should == 2361183241434822606847 - end - - it "loads an Integer -2361183241434822606847" do - Marshal.send(@method, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000").should == -2361183241434822606847 - end - - it "raises ArgumentError if the input is too short" do - ["\004\bi", - "\004\bi\001", - "\004\bi\002", - "\004\bi\002\0", - "\004\bi\003", - "\004\bi\003\0", - "\004\bi\003\0\0", - "\004\bi\004", - "\004\bi\004\0", - "\004\bi\004\0\0", - "\004\bi\004\0\0\0"].each do |invalid| - -> { Marshal.send(@method, invalid) }.should.raise(ArgumentError) - end - end - - if 0.size == 8 # for platforms like x86_64 - it "roundtrips 4611686018427387903 from dump/load correctly" do - Marshal.send(@method, Marshal.dump(4611686018427387903)).should == 4611686018427387903 - end - end - end - - describe "for a Rational" do - it "loads" do - r = Marshal.send(@method, Marshal.dump(Rational(1, 3))) - r.should == Rational(1, 3) - r.should.frozen? - end - end - - describe "for a Complex" do - it "loads" do - c = Marshal.send(@method, Marshal.dump(Complex(4, 3))) - c.should == Complex(4, 3) - c.should.frozen? - end - end - - describe "for a Bignum" do - platform_is c_long_size: 64 do - context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do - it "dumps a Fixnum" do - val = Marshal.send(@method, "\004\bl+\ab:wU") - val.should == 1433877090 - val.class.should == Integer - end - - it "dumps an array containing multiple references to the Bignum as an array of Fixnum" do - arr = Marshal.send(@method, "\004\b[\al+\a\223BwU@\006") - arr.should == [1433879187, 1433879187] - arr.each { |v| v.class.should == Integer } - end - end - end - end - - describe "for a Time" do - it "loads" do - Marshal.send(@method, Marshal.dump(Time.at(1))).should == Time.at(1) - end - - it "loads serialized instance variables" do - t = Time.new - t.instance_variable_set(:@foo, 'bar') - - Marshal.send(@method, Marshal.dump(t)).instance_variable_get(:@foo).should == 'bar' - end - - it "loads Time objects stored as links" do - t = Time.new - - t1, t2 = Marshal.send(@method, Marshal.dump([t, t])) - t1.should.equal? t2 - end - - it "keeps the local zone" do - with_timezone 'AST', 3 do - t = Time.local(2012, 1, 1) - Marshal.send(@method, Marshal.dump(t)).zone.should == t.zone - end - end - - it "keeps UTC zone" do - t = Time.now.utc - t2 = Marshal.send(@method, Marshal.dump(t)) - t2.should.utc? - end - - it "keeps the zone" do - t = nil - - with_timezone 'AST', 4 do - t = Time.local(2012, 1, 1) - end - - with_timezone 'EET', -2 do - Marshal.send(@method, Marshal.dump(t)).zone.should == 'AST' - end - end - - it "keeps utc offset" do - t = Time.new(2007,11,1,15,25,0, "+09:00") - t2 = Marshal.send(@method, Marshal.dump(t)) - t2.utc_offset.should == 32400 - end - - it "keeps nanoseconds" do - t = Time.now - Marshal.send(@method, Marshal.dump(t)).nsec.should == t.nsec - end - - it "does not add any additional instance variable" do - t = Time.now - t2 = Marshal.send(@method, Marshal.dump(t)) - t2.instance_variables.should.empty? - end - end - - describe "for nil" do - it "loads" do - Marshal.send(@method, "\x04\b0").should == nil - end - end - - describe "for true" do - it "loads" do - Marshal.send(@method, "\x04\bT").should == true - end - end - - describe "for false" do - it "loads" do - Marshal.send(@method, "\x04\bF").should == false - end - end - - describe "for a Class" do - it "loads" do - Marshal.send(@method, "\x04\bc\vString").should == String - end - - it "raises ArgumentError if given the name of a non-Module" do - -> { Marshal.send(@method, "\x04\bc\vKernel") }.should.raise(ArgumentError) - end - - it "raises ArgumentError if given a nonexistent class" do - -> { Marshal.send(@method, "\x04\bc\vStrung") }.should.raise(ArgumentError) - end - - it "raises ArgumentError when end of byte sequence reached before class name end" do - Marshal.dump(String).should == "\x04\bc\vString" - - -> { - Marshal.send(@method, "\x04\bc\vStr") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for a Module" do - it "loads a module" do - Marshal.send(@method, "\x04\bm\vKernel").should == Kernel - end - - it "raises ArgumentError if given the name of a non-Class" do - -> { Marshal.send(@method, "\x04\bm\vString") }.should.raise(ArgumentError) - end - - it "loads an old module" do - Marshal.send(@method, "\x04\bM\vKernel").should == Kernel - end - - it "raises ArgumentError when end of byte sequence reached before module name end" do - Marshal.dump(Kernel).should == "\x04\bm\vKernel" - - -> { - Marshal.send(@method, "\x04\bm\vKer") - }.should.raise(ArgumentError, "marshal data too short") - end - end - - describe "for a wrapped C pointer" do - it "loads" do - class DumpableDir < Dir - def _dump_data - path - end - def _load_data path - initialize(path) - end - end - - data = "\x04\bd:\x10DumpableDirI\"\x06.\x06:\x06ET" - - dir = Marshal.send(@method, data) - begin - dir.path.should == '.' - ensure - dir.close - end - end - - it "raises TypeError when the local class is missing _load_data" do - class UnloadableDumpableDir < Dir - def _dump_data - path - end - # no _load_data - end - - data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET" - - -> { Marshal.send(@method, data) }.should.raise(TypeError) - end - - it "raises ArgumentError when the local class is a regular object" do - data = "\004\bd:\020UserDefined\0" - - -> { Marshal.send(@method, data) }.should.raise(ArgumentError) - end - end - - describe "when a class does not exist in the namespace" do - before :each do - NamespaceTest.send(:const_set, :SameName, Class.new) - @data = Marshal.dump(NamespaceTest::SameName.new) - NamespaceTest.send(:remove_const, :SameName) - end - - it "raises an ArgumentError" do - message = "undefined class/module NamespaceTest::SameName" - -> { Marshal.send(@method, @data) }.should.raise(ArgumentError, message) - end - end - - it "raises an ArgumentError with full constant name when the dumped constant is missing" do - NamespaceTest.send(:const_set, :KaBoom, Class.new) - @data = Marshal.dump(NamespaceTest::KaBoom.new) - NamespaceTest.send(:remove_const, :KaBoom) - - -> { Marshal.send(@method, @data) }.should.raise(ArgumentError, /NamespaceTest::KaBoom/) - end -end diff --git a/spec/ruby/core/matchdata/captures_spec.rb b/spec/ruby/core/matchdata/captures_spec.rb index f829a25481..fdbc1c4346 100644 --- a/spec/ruby/core/matchdata/captures_spec.rb +++ b/spec/ruby/core/matchdata/captures_spec.rb @@ -1,6 +1,13 @@ require_relative '../../spec_helper' -require_relative 'shared/captures' +require_relative 'fixtures/classes' describe "MatchData#captures" do - it_behaves_like :matchdata_captures, :captures + it "returns an array of the match captures" do + /(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"] + end + + it "returns instances of String when given a String subclass" do + str = MatchDataSpecs::MyString.new("THX1138: The Movie") + /(.)(.)(\d+)(\d)/.match(str).captures.each { |c| c.should.instance_of?(String) } + end end diff --git a/spec/ruby/core/matchdata/deconstruct_spec.rb b/spec/ruby/core/matchdata/deconstruct_spec.rb index c55095665d..13ebd1848f 100644 --- a/spec/ruby/core/matchdata/deconstruct_spec.rb +++ b/spec/ruby/core/matchdata/deconstruct_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/captures' describe "MatchData#deconstruct" do - it_behaves_like :matchdata_captures, :deconstruct + it "is an alias of MatchData#captures" do + MatchData.instance_method(:deconstruct).should == MatchData.instance_method(:captures) + end end diff --git a/spec/ruby/core/matchdata/eql_spec.rb b/spec/ruby/core/matchdata/eql_spec.rb index 1d9666ebe1..937c4424aa 100644 --- a/spec/ruby/core/matchdata/eql_spec.rb +++ b/spec/ruby/core/matchdata/eql_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' describe "MatchData#eql?" do - it_behaves_like :matchdata_eql, :eql? + it "is an alias of MatchData#==" do + MatchData.instance_method(:eql?).should == MatchData.instance_method(:==) + end end diff --git a/spec/ruby/core/matchdata/equal_value_spec.rb b/spec/ruby/core/matchdata/equal_value_spec.rb index a58f1277e4..74d3a5adcd 100644 --- a/spec/ruby/core/matchdata/equal_value_spec.rb +++ b/spec/ruby/core/matchdata/equal_value_spec.rb @@ -1,6 +1,26 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' describe "MatchData#==" do - it_behaves_like :matchdata_eql, :== + it "returns true if both operands have equal target strings, patterns, and match positions" do + a = 'haystack'.match(/hay/) + b = 'haystack'.match(/hay/) + a.==(b).should == true + end + + it "returns false if the operands have different target strings" do + a = 'hay'.match(/hay/) + b = 'haystack'.match(/hay/) + a.==(b).should == false + end + + it "returns false if the operands have different patterns" do + a = 'haystack'.match(/h.y/) + b = 'haystack'.match(/hay/) + a.==(b).should == false + end + + it "returns false if the argument is not a MatchData object" do + a = 'haystack'.match(/hay/) + a.==(Object.new).should == false + end end diff --git a/spec/ruby/core/matchdata/length_spec.rb b/spec/ruby/core/matchdata/length_spec.rb index 39df36df4b..72d4d80cec 100644 --- a/spec/ruby/core/matchdata/length_spec.rb +++ b/spec/ruby/core/matchdata/length_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "MatchData#length" do - it_behaves_like :matchdata_length, :length + it "is an alias of MatchData#size" do + MatchData.instance_method(:length).should == MatchData.instance_method(:size) + end end diff --git a/spec/ruby/core/matchdata/shared/captures.rb b/spec/ruby/core/matchdata/shared/captures.rb deleted file mode 100644 index de5870f543..0000000000 --- a/spec/ruby/core/matchdata/shared/captures.rb +++ /dev/null @@ -1,13 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :matchdata_captures, shared: true do - it "returns an array of the match captures" do - /(.)(.)(\d+)(\d)/.match("THX1138.").send(@method).should == ["H","X","113","8"] - end - - it "returns instances of String when given a String subclass" do - str = MatchDataSpecs::MyString.new("THX1138: The Movie") - /(.)(.)(\d+)(\d)/.match(str).send(@method).each { |c| c.should.instance_of?(String) } - end -end diff --git a/spec/ruby/core/matchdata/shared/eql.rb b/spec/ruby/core/matchdata/shared/eql.rb deleted file mode 100644 index e4bb8797b7..0000000000 --- a/spec/ruby/core/matchdata/shared/eql.rb +++ /dev/null @@ -1,26 +0,0 @@ -require_relative '../../../spec_helper' - -describe :matchdata_eql, shared: true do - it "returns true if both operands have equal target strings, patterns, and match positions" do - a = 'haystack'.match(/hay/) - b = 'haystack'.match(/hay/) - a.send(@method, b).should == true - end - - it "returns false if the operands have different target strings" do - a = 'hay'.match(/hay/) - b = 'haystack'.match(/hay/) - a.send(@method, b).should == false - end - - it "returns false if the operands have different patterns" do - a = 'haystack'.match(/h.y/) - b = 'haystack'.match(/hay/) - a.send(@method, b).should == false - end - - it "returns false if the argument is not a MatchData object" do - a = 'haystack'.match(/hay/) - a.send(@method, Object.new).should == false - end -end diff --git a/spec/ruby/core/matchdata/shared/length.rb b/spec/ruby/core/matchdata/shared/length.rb deleted file mode 100644 index 6312a7ed4c..0000000000 --- a/spec/ruby/core/matchdata/shared/length.rb +++ /dev/null @@ -1,5 +0,0 @@ -describe :matchdata_length, shared: true do - it "length should return the number of elements in the match array" do - /(.)(.)(\d+)(\d)/.match("THX1138.").send(@method).should == 5 - end -end diff --git a/spec/ruby/core/matchdata/size_spec.rb b/spec/ruby/core/matchdata/size_spec.rb index b4965db3b8..f93ded72cb 100644 --- a/spec/ruby/core/matchdata/size_spec.rb +++ b/spec/ruby/core/matchdata/size_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "MatchData#size" do - it_behaves_like :matchdata_length, :size + it "should return the number of elements in the match array" do + /(.)(.)(\d+)(\d)/.match("THX1138.").size.should == 5 + end end diff --git a/spec/ruby/core/method/call_spec.rb b/spec/ruby/core/method/call_spec.rb index 6d997325fa..cb11545e91 100644 --- a/spec/ruby/core/method/call_spec.rb +++ b/spec/ruby/core/method/call_spec.rb @@ -1,7 +1,54 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/call' describe "Method#call" do - it_behaves_like :method_call, :call + it "invokes the method with the specified arguments, returning the method's return value" do + m = 12.method("+") + m.call(3).should == 15 + m.call(20).should == 32 + + m = MethodSpecs::Methods.new.method(:attr=) + m.call(42).should == 42 + end + + it "raises an ArgumentError when given incorrect number of arguments" do + -> { + MethodSpecs::Methods.new.method(:two_req).call(1, 2, 3) + }.should.raise(ArgumentError) + -> { + MethodSpecs::Methods.new.method(:two_req).call(1) + }.should.raise(ArgumentError) + end + + describe "for a Method generated by respond_to_missing?" do + it "invokes method_missing with the specified arguments and returns the result" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + meth.call(:argument).should == [:argument] + end + + it "invokes method_missing with the method name and the specified arguments" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + + @m.should_receive(:method_missing).with(:handled_via_method_missing, :argument) + meth.call(:argument) + end + + it "invokes method_missing dynamically" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + + def @m.method_missing(*); :changed; end + meth.call(:argument).should == :changed + end + + it "does not call the original method name even if it now exists" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + + def @m.handled_via_method_missing(*); :not_called; end + meth.call(:argument).should == [:argument] + end + end end diff --git a/spec/ruby/core/method/case_compare_spec.rb b/spec/ruby/core/method/case_compare_spec.rb index a78953e8ad..771fea1ce5 100644 --- a/spec/ruby/core/method/case_compare_spec.rb +++ b/spec/ruby/core/method/case_compare_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/call' describe "Method#===" do - it_behaves_like :method_call, :=== + it "is an alias of Method#call" do + Method.instance_method(:===).should == Method.instance_method(:call) + end end diff --git a/spec/ruby/core/method/element_reference_spec.rb b/spec/ruby/core/method/element_reference_spec.rb index aa6c54d1cb..65c13cf32b 100644 --- a/spec/ruby/core/method/element_reference_spec.rb +++ b/spec/ruby/core/method/element_reference_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/call' describe "Method#[]" do - it_behaves_like :method_call, :[] + it "is an alias of Method#call" do + Method.instance_method(:[]).should == Method.instance_method(:call) + end end diff --git a/spec/ruby/core/method/eql_spec.rb b/spec/ruby/core/method/eql_spec.rb index b97c9e4db0..81fd086bd5 100644 --- a/spec/ruby/core/method/eql_spec.rb +++ b/spec/ruby/core/method/eql_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' describe "Method#eql?" do - it_behaves_like :method_equal, :eql? + it "is an alias of Method#==" do + Method.instance_method(:eql?).should == Method.instance_method(:==) + end end diff --git a/spec/ruby/core/method/equal_value_spec.rb b/spec/ruby/core/method/equal_value_spec.rb index 0431d0c5f6..ca9ef9f108 100644 --- a/spec/ruby/core/method/equal_value_spec.rb +++ b/spec/ruby/core/method/equal_value_spec.rb @@ -1,6 +1,94 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' +require_relative 'fixtures/classes' describe "Method#==" do - it_behaves_like :method_equal, :== + before :each do + @m = MethodSpecs::Methods.new + @m_foo = @m.method(:foo) + @m2 = MethodSpecs::Methods.new + @a = MethodSpecs::A.new + end + + it "returns true if methods are the same" do + m2 = @m.method(:foo) + + (@m_foo == @m_foo).should == true + (@m_foo == m2).should == true + end + + it "returns true on aliased methods" do + m_bar = @m.method(:bar) + + (m_bar == @m_foo).should == true + end + + it "returns true if the two core methods are aliases" do + s = "hello" + a = s.method(:size) + b = s.method(:length) + (a == b).should == true + end + + it "returns false on a method which is neither aliased nor the same method" do + m2 = @m.method(:zero) + + (@m_foo == m2).should == false + end + + it "returns false for a method which is not bound to the same object" do + m2_foo = @m2.method(:foo) + a_baz = @a.method(:baz) + + (@m_foo == m2_foo).should == false + (@m_foo == a_baz).should == false + end + + it "returns false if the two methods are bound to the same object but were defined independently" do + m2 = @m.method(:same_as_foo) + (@m_foo == m2).should == false + end + + it "returns true if a method was defined using the other one" do + MethodSpecs::Methods.send :define_method, :defined_foo, MethodSpecs::Methods.instance_method(:foo) + m2 = @m.method(:defined_foo) + (@m_foo == m2).should == true + end + + it "returns false if comparing a method defined via define_method and def" do + defn = @m.method(:zero) + defined = @m.method(:zero_defined_method) + + (defn == defined).should == false + (defined == defn).should == false + end + + describe 'missing methods' do + it "returns true for the same method missing" do + miss1 = @m.method(:handled_via_method_missing) + miss1bis = @m.method(:handled_via_method_missing) + miss2 = @m.method(:also_handled) + + (miss1 == miss1bis).should == true + (miss1 == miss2).should == false + end + + it 'calls respond_to_missing? with true to include private methods' do + @m.should_receive(:respond_to_missing?).with(:some_missing_method, true).and_return(true) + @m.method(:some_missing_method) + end + end + + it "returns false if the two methods are bound to different objects, have the same names, and identical bodies" do + a = MethodSpecs::Eql.instance_method(:same_body) + b = MethodSpecs::Eql2.instance_method(:same_body) + (a == b).should == false + end + + it "returns false if the argument is not a Method object" do + (String.instance_method(:size) == 7).should == false + end + + it "returns false if the argument is an unbound version of self" do + (method(:load) == method(:load).unbind).should == false + end end diff --git a/spec/ruby/core/method/inspect_spec.rb b/spec/ruby/core/method/inspect_spec.rb index 97ff2d8c11..5eb8850ff3 100644 --- a/spec/ruby/core/method/inspect_spec.rb +++ b/spec/ruby/core/method/inspect_spec.rb @@ -1,8 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' -require_relative 'shared/aliased_inspect' describe "Method#inspect" do - it_behaves_like :method_to_s, :inspect - it_behaves_like :method_to_s_aliased, :inspect, -> meth { meth } + it "is an alias of Method#to_s" do + Method.instance_method(:inspect).should == Method.instance_method(:to_s) + end end diff --git a/spec/ruby/core/method/shared/call.rb b/spec/ruby/core/method/shared/call.rb deleted file mode 100644 index 41ee2b06cb..0000000000 --- a/spec/ruby/core/method/shared/call.rb +++ /dev/null @@ -1,51 +0,0 @@ -describe :method_call, shared: true do - it "invokes the method with the specified arguments, returning the method's return value" do - m = 12.method("+") - m.send(@method, 3).should == 15 - m.send(@method, 20).should == 32 - - m = MethodSpecs::Methods.new.method(:attr=) - m.send(@method, 42).should == 42 - end - - it "raises an ArgumentError when given incorrect number of arguments" do - -> { - MethodSpecs::Methods.new.method(:two_req).send(@method, 1, 2, 3) - }.should.raise(ArgumentError) - -> { - MethodSpecs::Methods.new.method(:two_req).send(@method, 1) - }.should.raise(ArgumentError) - end - - describe "for a Method generated by respond_to_missing?" do - it "invokes method_missing with the specified arguments and returns the result" do - @m = MethodSpecs::Methods.new - meth = @m.method(:handled_via_method_missing) - meth.send(@method, :argument).should == [:argument] - end - - it "invokes method_missing with the method name and the specified arguments" do - @m = MethodSpecs::Methods.new - meth = @m.method(:handled_via_method_missing) - - @m.should_receive(:method_missing).with(:handled_via_method_missing, :argument) - meth.send(@method, :argument) - end - - it "invokes method_missing dynamically" do - @m = MethodSpecs::Methods.new - meth = @m.method(:handled_via_method_missing) - - def @m.method_missing(*); :changed; end - meth.send(@method, :argument).should == :changed - end - - it "does not call the original method name even if it now exists" do - @m = MethodSpecs::Methods.new - meth = @m.method(:handled_via_method_missing) - - def @m.handled_via_method_missing(*); :not_called; end - meth.send(@method, :argument).should == [:argument] - end - end -end diff --git a/spec/ruby/core/method/shared/eql.rb b/spec/ruby/core/method/shared/eql.rb deleted file mode 100644 index 3c340202b7..0000000000 --- a/spec/ruby/core/method/shared/eql.rb +++ /dev/null @@ -1,94 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :method_equal, shared: true do - before :each do - @m = MethodSpecs::Methods.new - @m_foo = @m.method(:foo) - @m2 = MethodSpecs::Methods.new - @a = MethodSpecs::A.new - end - - it "returns true if methods are the same" do - m2 = @m.method(:foo) - - @m_foo.send(@method, @m_foo).should == true - @m_foo.send(@method, m2).should == true - end - - it "returns true on aliased methods" do - m_bar = @m.method(:bar) - - m_bar.send(@method, @m_foo).should == true - end - - it "returns true if the two core methods are aliases" do - s = "hello" - a = s.method(:size) - b = s.method(:length) - a.send(@method, b).should == true - end - - it "returns false on a method which is neither aliased nor the same method" do - m2 = @m.method(:zero) - - @m_foo.send(@method, m2).should == false - end - - it "returns false for a method which is not bound to the same object" do - m2_foo = @m2.method(:foo) - a_baz = @a.method(:baz) - - @m_foo.send(@method, m2_foo).should == false - @m_foo.send(@method, a_baz).should == false - end - - it "returns false if the two methods are bound to the same object but were defined independently" do - m2 = @m.method(:same_as_foo) - @m_foo.send(@method, m2).should == false - end - - it "returns true if a method was defined using the other one" do - MethodSpecs::Methods.send :define_method, :defined_foo, MethodSpecs::Methods.instance_method(:foo) - m2 = @m.method(:defined_foo) - @m_foo.send(@method, m2).should == true - end - - it "returns false if comparing a method defined via define_method and def" do - defn = @m.method(:zero) - defined = @m.method(:zero_defined_method) - - defn.send(@method, defined).should == false - defined.send(@method, defn).should == false - end - - describe 'missing methods' do - it "returns true for the same method missing" do - miss1 = @m.method(:handled_via_method_missing) - miss1bis = @m.method(:handled_via_method_missing) - miss2 = @m.method(:also_handled) - - miss1.send(@method, miss1bis).should == true - miss1.send(@method, miss2).should == false - end - - it 'calls respond_to_missing? with true to include private methods' do - @m.should_receive(:respond_to_missing?).with(:some_missing_method, true).and_return(true) - @m.method(:some_missing_method) - end - end - - it "returns false if the two methods are bound to different objects, have the same names, and identical bodies" do - a = MethodSpecs::Eql.instance_method(:same_body) - b = MethodSpecs::Eql2.instance_method(:same_body) - a.send(@method, b).should == false - end - - it "returns false if the argument is not a Method object" do - String.instance_method(:size).send(@method, 7).should == false - end - - it "returns false if the argument is an unbound version of self" do - method(:load).send(@method, method(:load).unbind).should == false - end -end diff --git a/spec/ruby/core/module/class_eval_spec.rb b/spec/ruby/core/module/class_eval_spec.rb index c6665d5aff..0c190ceaff 100644 --- a/spec/ruby/core/module/class_eval_spec.rb +++ b/spec/ruby/core/module/class_eval_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/class_eval' describe "Module#class_eval" do - it_behaves_like :module_class_eval, :class_eval + it "is an alias of Module#module_eval" do + Module.instance_method(:class_eval).should == Module.instance_method(:module_eval) + end end diff --git a/spec/ruby/core/module/class_exec_spec.rb b/spec/ruby/core/module/class_exec_spec.rb index 4acd0169ad..d47a6ba982 100644 --- a/spec/ruby/core/module/class_exec_spec.rb +++ b/spec/ruby/core/module/class_exec_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/class_exec' describe "Module#class_exec" do - it_behaves_like :module_class_exec, :class_exec + it "is an alias of Module#module_exec" do + Module.instance_method(:class_exec).should == Module.instance_method(:module_exec) + end end diff --git a/spec/ruby/core/module/inspect_spec.rb b/spec/ruby/core/module/inspect_spec.rb new file mode 100644 index 0000000000..68c8494c96 --- /dev/null +++ b/spec/ruby/core/module/inspect_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Module#inspect" do + it "is an alias of Module#to_s" do + Module.instance_method(:inspect).should == Module.instance_method(:to_s) + end +end diff --git a/spec/ruby/core/module/module_eval_spec.rb b/spec/ruby/core/module/module_eval_spec.rb index e9e9fda28d..bcd51ca19d 100644 --- a/spec/ruby/core/module/module_eval_spec.rb +++ b/spec/ruby/core/module/module_eval_spec.rb @@ -1,7 +1,175 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/class_eval' describe "Module#module_eval" do - it_behaves_like :module_class_eval, :module_eval + # TODO: This should probably be replaced with a "should behave like" that uses + # the many scoping/binding specs from kernel/eval_spec, since most of those + # behaviors are the same for instance_eval. See also module_eval/class_eval. + + it "evaluates a given string in the context of self" do + ModuleSpecs.module_eval("self").should == ModuleSpecs + ModuleSpecs.module_eval("1 + 1").should == 2 + end + + it "does not add defined methods to other classes" do + FalseClass.module_eval do + def foo + 'foo' + end + end + -> {42.foo}.should.raise(NoMethodError) + end + + it "resolves constants in the caller scope" do + ModuleSpecs::ClassEvalTest.get_constant_from_scope.should == ModuleSpecs::Lookup + end + + it "resolves constants in the caller scope ignoring send" do + ModuleSpecs::ClassEvalTest.get_constant_from_scope_with_send(:module_eval).should == ModuleSpecs::Lookup + end + + it "resolves constants in the receiver's scope" do + ModuleSpecs.module_eval("Lookup").should == ModuleSpecs::Lookup + ModuleSpecs.module_eval("Lookup::LOOKIE").should == ModuleSpecs::Lookup::LOOKIE + end + + it "defines constants in the receiver's scope" do + ModuleSpecs.module_eval("module NewEvaluatedModule;end") + ModuleSpecs.const_defined?(:NewEvaluatedModule, false).should == true + end + + it "evaluates a given block in the context of self" do + ModuleSpecs.module_eval { self }.should == ModuleSpecs + ModuleSpecs.module_eval { 1 + 1 }.should == 2 + end + + it "passes the module as the first argument of the block" do + given = nil + ModuleSpecs.module_eval do |block_parameter| + given = block_parameter + end + given.should.equal? ModuleSpecs + end + + it "uses the optional filename and lineno parameters for error messages" do + ModuleSpecs.module_eval("[__FILE__, __LINE__]", "test", 102).should == ["test", 102] + end + + it "uses the caller location as default filename" do + ModuleSpecs.module_eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + + it "converts a non-string filename to a string using to_str" do + (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) + ModuleSpecs.module_eval("1+1", file) + + (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) + ModuleSpecs.module_eval("1+1", file, 15) + end + + it "raises a TypeError when the given filename can't be converted to string using to_str" do + (file = mock('123')).should_receive(:to_str).and_return(123) + -> { ModuleSpecs.module_eval("1+1", file) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) + end + + it "converts non string eval-string to string using to_str" do + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.module_eval(o).should == 2 + + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.module_eval(o, "file.rb").should == 2 + + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.module_eval(o, "file.rb", 15).should == 2 + end + + it "raises a TypeError when the given eval-string can't be converted to string using to_str" do + o = mock('x') + -> { ModuleSpecs.module_eval(o) }.should.raise(TypeError, "no implicit conversion of MockObject into String") + + (o = mock('123')).should_receive(:to_str).and_return(123) + -> { ModuleSpecs.module_eval(o) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) + end + + it "raises an ArgumentError when no arguments and no block are given" do + -> { ModuleSpecs.module_eval }.should.raise(ArgumentError, "wrong number of arguments (given 0, expected 1..3)") + end + + it "raises an ArgumentError when more than 3 arguments are given" do + -> { + ModuleSpecs.module_eval("1 + 1", "some file", 0, "bogus") + }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") + end + + it "raises an ArgumentError when a block and normal arguments are given" do + -> { + ModuleSpecs.module_eval("1 + 1") { 1 + 1 } + }.should.raise(ArgumentError, "wrong number of arguments (given 1, expected 0)") + end + + # This case was found because Rubinius was caching the compiled + # version of the string and not duping the methods within the + # eval, causing the method addition to change the static scope + # of the shared CompiledCode. + it "adds methods respecting the lexical constant scope" do + code = "def self.attribute; C; end" + + a = Class.new do + self::C = "A" + end + + b = Class.new do + self::C = "B" + end + + a.module_eval(code) + b.module_eval(code) + + a.attribute.should == "A" + b.attribute.should == "B" + end + + it "activates refinements from the eval scope" do + refinery = Module.new do + refine ModuleSpecs::NamedClass do + def foo + "bar" + end + end + end + + mid = :module_eval + result = nil + + Class.new do + using refinery + + result = send(mid, "ModuleSpecs::NamedClass.new.foo") + end + + result.should == "bar" + end + + it "activates refinements from the eval scope with block" do + refinery = Module.new do + refine ModuleSpecs::NamedClass do + def foo + "bar" + end + end + end + + mid = :module_eval + result = nil + + Class.new do + using refinery + + result = send(mid) do + ModuleSpecs::NamedClass.new.foo + end + end + + result.should == "bar" + end end diff --git a/spec/ruby/core/module/module_exec_spec.rb b/spec/ruby/core/module/module_exec_spec.rb index 47cdf7ef52..6b36e8a02f 100644 --- a/spec/ruby/core/module/module_exec_spec.rb +++ b/spec/ruby/core/module/module_exec_spec.rb @@ -1,7 +1,38 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/class_exec' describe "Module#module_exec" do - it_behaves_like :module_class_exec, :module_exec + it "does not add defined methods to other classes" do + FalseClass.module_exec do + def foo + 'foo' + end + end + -> {42.foo}.should.raise(NoMethodError) + end + + it "defines method in the receiver's scope" do + ModuleSpecs::Subclass.module_exec { def foo; end } + ModuleSpecs::Subclass.new.respond_to?(:foo).should == true + end + + it "evaluates a given block in the context of self" do + ModuleSpecs::Subclass.module_exec { self }.should == ModuleSpecs::Subclass + ModuleSpecs::Subclass.new.module_exec { 1 + 1 }.should == 2 + end + + it "raises a LocalJumpError when no block is given" do + -> { ModuleSpecs::Subclass.module_exec }.should.raise(LocalJumpError) + end + + it "passes arguments to the block" do + a = ModuleSpecs::Subclass + a.module_exec(1) { |b| b }.should.equal?(1) + end + + describe "with optional argument" do + it "does not destructure a single array argument" do + ModuleSpecs::Subclass.module_exec([1, 2, 3]) { |a = 99| a }.should == [1, 2, 3] + end + end end diff --git a/spec/ruby/core/module/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb deleted file mode 100644 index ee2860449a..0000000000 --- a/spec/ruby/core/module/shared/class_eval.rb +++ /dev/null @@ -1,172 +0,0 @@ -describe :module_class_eval, shared: true do - # TODO: This should probably be replaced with a "should behave like" that uses - # the many scoping/binding specs from kernel/eval_spec, since most of those - # behaviors are the same for instance_eval. See also module_eval/class_eval. - - it "evaluates a given string in the context of self" do - ModuleSpecs.send(@method, "self").should == ModuleSpecs - ModuleSpecs.send(@method, "1 + 1").should == 2 - end - - it "does not add defined methods to other classes" do - FalseClass.send(@method) do - def foo - 'foo' - end - end - -> {42.foo}.should.raise(NoMethodError) - end - - it "resolves constants in the caller scope" do - ModuleSpecs::ClassEvalTest.get_constant_from_scope.should == ModuleSpecs::Lookup - end - - it "resolves constants in the caller scope ignoring send" do - ModuleSpecs::ClassEvalTest.get_constant_from_scope_with_send(@method).should == ModuleSpecs::Lookup - end - - it "resolves constants in the receiver's scope" do - ModuleSpecs.send(@method, "Lookup").should == ModuleSpecs::Lookup - ModuleSpecs.send(@method, "Lookup::LOOKIE").should == ModuleSpecs::Lookup::LOOKIE - end - - it "defines constants in the receiver's scope" do - ModuleSpecs.send(@method, "module NewEvaluatedModule;end") - ModuleSpecs.const_defined?(:NewEvaluatedModule, false).should == true - end - - it "evaluates a given block in the context of self" do - ModuleSpecs.send(@method) { self }.should == ModuleSpecs - ModuleSpecs.send(@method) { 1 + 1 }.should == 2 - end - - it "passes the module as the first argument of the block" do - given = nil - ModuleSpecs.send(@method) do |block_parameter| - given = block_parameter - end - given.should.equal? ModuleSpecs - end - - it "uses the optional filename and lineno parameters for error messages" do - ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102] - end - - it "uses the caller location as default filename" do - ModuleSpecs.send(@method, "[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] - end - - it "converts a non-string filename to a string using to_str" do - (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) - ModuleSpecs.send(@method, "1+1", file) - - (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) - ModuleSpecs.send(@method, "1+1", file, 15) - end - - it "raises a TypeError when the given filename can't be converted to string using to_str" do - (file = mock('123')).should_receive(:to_str).and_return(123) - -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) - end - - it "converts non string eval-string to string using to_str" do - (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") - ModuleSpecs.send(@method, o).should == 2 - - (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") - ModuleSpecs.send(@method, o, "file.rb").should == 2 - - (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") - ModuleSpecs.send(@method, o, "file.rb", 15).should == 2 - end - - it "raises a TypeError when the given eval-string can't be converted to string using to_str" do - o = mock('x') - -> { ModuleSpecs.send(@method, o) }.should.raise(TypeError, "no implicit conversion of MockObject into String") - - (o = mock('123')).should_receive(:to_str).and_return(123) - -> { ModuleSpecs.send(@method, o) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) - end - - it "raises an ArgumentError when no arguments and no block are given" do - -> { ModuleSpecs.send(@method) }.should.raise(ArgumentError, "wrong number of arguments (given 0, expected 1..3)") - end - - it "raises an ArgumentError when more than 3 arguments are given" do - -> { - ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus") - }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") - end - - it "raises an ArgumentError when a block and normal arguments are given" do - -> { - ModuleSpecs.send(@method, "1 + 1") { 1 + 1 } - }.should.raise(ArgumentError, "wrong number of arguments (given 1, expected 0)") - end - - # This case was found because Rubinius was caching the compiled - # version of the string and not duping the methods within the - # eval, causing the method addition to change the static scope - # of the shared CompiledCode. - it "adds methods respecting the lexical constant scope" do - code = "def self.attribute; C; end" - - a = Class.new do - self::C = "A" - end - - b = Class.new do - self::C = "B" - end - - a.send @method, code - b.send @method, code - - a.attribute.should == "A" - b.attribute.should == "B" - end - - it "activates refinements from the eval scope" do - refinery = Module.new do - refine ModuleSpecs::NamedClass do - def foo - "bar" - end - end - end - - mid = @method - result = nil - - Class.new do - using refinery - - result = send(mid, "ModuleSpecs::NamedClass.new.foo") - end - - result.should == "bar" - end - - it "activates refinements from the eval scope with block" do - refinery = Module.new do - refine ModuleSpecs::NamedClass do - def foo - "bar" - end - end - end - - mid = @method - result = nil - - Class.new do - using refinery - - result = send(mid) do - ModuleSpecs::NamedClass.new.foo - end - end - - result.should == "bar" - end -end diff --git a/spec/ruby/core/module/shared/class_exec.rb b/spec/ruby/core/module/shared/class_exec.rb deleted file mode 100644 index e51af1966d..0000000000 --- a/spec/ruby/core/module/shared/class_exec.rb +++ /dev/null @@ -1,35 +0,0 @@ -describe :module_class_exec, shared: true do - it "does not add defined methods to other classes" do - FalseClass.send(@method) do - def foo - 'foo' - end - end - -> {42.foo}.should.raise(NoMethodError) - end - - it "defines method in the receiver's scope" do - ModuleSpecs::Subclass.send(@method) { def foo; end } - ModuleSpecs::Subclass.new.respond_to?(:foo).should == true - end - - it "evaluates a given block in the context of self" do - ModuleSpecs::Subclass.send(@method) { self }.should == ModuleSpecs::Subclass - ModuleSpecs::Subclass.new.send(@method) { 1 + 1 }.should == 2 - end - - it "raises a LocalJumpError when no block is given" do - -> { ModuleSpecs::Subclass.send(@method) }.should.raise(LocalJumpError) - end - - it "passes arguments to the block" do - a = ModuleSpecs::Subclass - a.send(@method, 1) { |b| b }.should.equal?(1) - end - - describe "with optional argument" do - it "does not destructure a single array argument" do - ModuleSpecs::Subclass.send(@method, [1, 2, 3]) { |a = 99| a }.should == [1, 2, 3] - end - end -end diff --git a/spec/ruby/core/nil/xor_spec.rb b/spec/ruby/core/nil/xor_spec.rb index b45da9d443..31ce33e971 100644 --- a/spec/ruby/core/nil/xor_spec.rb +++ b/spec/ruby/core/nil/xor_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' describe "NilClass#^" do - it "returns false if other is nil or false, otherwise true" do - (nil ^ nil).should == false - (nil ^ true).should == true - (nil ^ false).should == false - (nil ^ "").should == true - (nil ^ mock('x')).should == true + it "is an alias of NilClass#|" do + nil.method(:^).should == nil.method(:|) end end diff --git a/spec/ruby/core/numeric/abs_spec.rb b/spec/ruby/core/numeric/abs_spec.rb index 8bec50e337..4b16e06c97 100644 --- a/spec/ruby/core/numeric/abs_spec.rb +++ b/spec/ruby/core/numeric/abs_spec.rb @@ -1,6 +1,19 @@ require_relative '../../spec_helper' -require_relative 'shared/abs' +require_relative 'fixtures/classes' describe "Numeric#abs" do - it_behaves_like :numeric_abs, :abs + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns self when self is greater than 0" do + @obj.should_receive(:<).with(0).and_return(false) + @obj.abs.should == @obj + end + + it "returns self\#@- when self is less than 0" do + @obj.should_receive(:<).with(0).and_return(true) + @obj.should_receive(:-@).and_return(:absolute_value) + @obj.abs.should == :absolute_value + end end diff --git a/spec/ruby/core/numeric/angle_spec.rb b/spec/ruby/core/numeric/angle_spec.rb index bb38165777..25d2834a52 100644 --- a/spec/ruby/core/numeric/angle_spec.rb +++ b/spec/ruby/core/numeric/angle_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Numeric#angle" do - it_behaves_like :numeric_arg, :angle + it "is an alias of Numeric#arg" do + Numeric.instance_method(:angle).should == Numeric.instance_method(:arg) + end end diff --git a/spec/ruby/core/numeric/arg_spec.rb b/spec/ruby/core/numeric/arg_spec.rb index ba3b57c687..4fd059d7fc 100644 --- a/spec/ruby/core/numeric/arg_spec.rb +++ b/spec/ruby/core/numeric/arg_spec.rb @@ -1,6 +1,38 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Numeric#arg" do - it_behaves_like :numeric_arg, :arg + before :each do + @numbers = [ + 20, + Rational(3, 4), + bignum_value, + infinity_value + ] + end + + it "returns 0 if positive" do + @numbers.each do |number| + number.arg.should == 0 + end + end + + it "returns Pi if negative" do + @numbers.each do |number| + (0-number).arg.should == Math::PI + end + end + + describe "with a Numeric subclass" do + it "returns 0 if self#<(0) returns false" do + numeric = mock_numeric('positive') + numeric.should_receive(:<).with(0).and_return(false) + numeric.arg.should == 0 + end + + it "returns Pi if self#<(0) returns true" do + numeric = mock_numeric('positive') + numeric.should_receive(:<).with(0).and_return(true) + numeric.arg.should == Math::PI + end + end end diff --git a/spec/ruby/core/numeric/conj_spec.rb b/spec/ruby/core/numeric/conj_spec.rb index 7d4777ca60..f376a0d4b1 100644 --- a/spec/ruby/core/numeric/conj_spec.rb +++ b/spec/ruby/core/numeric/conj_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/conj' describe "Numeric#conj" do - it_behaves_like :numeric_conj, :conj + it "is an alias of Numeric#conjugate" do + Numeric.instance_method(:conj).should == Numeric.instance_method(:conjugate) + end end diff --git a/spec/ruby/core/numeric/conjugate_spec.rb b/spec/ruby/core/numeric/conjugate_spec.rb index 99854766e7..ea4731991d 100644 --- a/spec/ruby/core/numeric/conjugate_spec.rb +++ b/spec/ruby/core/numeric/conjugate_spec.rb @@ -1,6 +1,20 @@ require_relative '../../spec_helper' -require_relative 'shared/conj' describe "Numeric#conjugate" do - it_behaves_like :numeric_conj, :conjugate + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + bignum_value, + infinity_value, + nan_value + ] + end + + it "returns self" do + @numbers.each do |number| + number.conjugate.should.equal?(number) + end + end end diff --git a/spec/ruby/core/numeric/imag_spec.rb b/spec/ruby/core/numeric/imag_spec.rb index b9e343cee9..761d6b0dbe 100644 --- a/spec/ruby/core/numeric/imag_spec.rb +++ b/spec/ruby/core/numeric/imag_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/imag' describe "Numeric#imag" do - it_behaves_like :numeric_imag, :imag + it "is an alias of Numeric#imaginary" do + Numeric.instance_method(:imag).should == Numeric.instance_method(:imaginary) + end end diff --git a/spec/ruby/core/numeric/imaginary_spec.rb b/spec/ruby/core/numeric/imaginary_spec.rb index ec708cb505..7b5d94cc75 100644 --- a/spec/ruby/core/numeric/imaginary_spec.rb +++ b/spec/ruby/core/numeric/imaginary_spec.rb @@ -1,6 +1,26 @@ require_relative '../../spec_helper' -require_relative 'shared/imag' describe "Numeric#imaginary" do - it_behaves_like :numeric_imag, :imaginary + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + bignum_value, # Bignum + infinity_value, + nan_value + ].map{|n| [n,-n]}.flatten + end + + it "returns 0" do + @numbers.each do |number| + number.imaginary.should == 0 + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + -> { number.imaginary(number) }.should.raise(ArgumentError) + end + end end diff --git a/spec/ruby/core/numeric/magnitude_spec.rb b/spec/ruby/core/numeric/magnitude_spec.rb index 1371dff21f..ea4dbd166f 100644 --- a/spec/ruby/core/numeric/magnitude_spec.rb +++ b/spec/ruby/core/numeric/magnitude_spec.rb @@ -1,6 +1,7 @@ require_relative "../../spec_helper" -require_relative 'shared/abs' describe "Numeric#magnitude" do - it_behaves_like :numeric_abs, :magnitude + it "is an alias of Numeric#abs" do + Numeric.instance_method(:magnitude).should == Numeric.instance_method(:abs) + end end diff --git a/spec/ruby/core/numeric/modulo_spec.rb b/spec/ruby/core/numeric/modulo_spec.rb index e3dc7e56f3..0baf96dc18 100644 --- a/spec/ruby/core/numeric/modulo_spec.rb +++ b/spec/ruby/core/numeric/modulo_spec.rb @@ -1,7 +1,12 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -describe :numeric_modulo_19, shared: true do +describe "Numeric#modulo" do + it "is an alias of Numeric#%" do + Numeric.instance_method(:modulo).should == Numeric.instance_method(:%) + end +end + +describe "Numeric#%" do it "returns self - other * self.div(other)" do s = mock_numeric('self') o = mock_numeric('other') @@ -11,14 +16,6 @@ describe :numeric_modulo_19, shared: true do s.should_receive(:div).with(o).and_return(n3) o.should_receive(:*).with(n3).and_return(n4) s.should_receive(:-).with(n4).and_return(n5) - s.send(@method, o).should == n5 + (s % o).should == n5 end end - -describe "Numeric#modulo" do - it_behaves_like :numeric_modulo_19, :modulo -end - -describe "Numeric#%" do - it_behaves_like :numeric_modulo_19, :% -end diff --git a/spec/ruby/core/numeric/phase_spec.rb b/spec/ruby/core/numeric/phase_spec.rb index bc1995303f..3abe8f2e02 100644 --- a/spec/ruby/core/numeric/phase_spec.rb +++ b/spec/ruby/core/numeric/phase_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/arg' describe "Numeric#phase" do - it_behaves_like :numeric_arg, :phase + it "is an alias of Numeric#arg" do + Numeric.instance_method(:phase).should == Numeric.instance_method(:arg) + end end diff --git a/spec/ruby/core/numeric/rect_spec.rb b/spec/ruby/core/numeric/rect_spec.rb index 79a144c5a4..65cdcc5229 100644 --- a/spec/ruby/core/numeric/rect_spec.rb +++ b/spec/ruby/core/numeric/rect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/rect' describe "Numeric#rect" do - it_behaves_like :numeric_rect, :rect + it "is an alias of Numeric#rectangular" do + Numeric.instance_method(:rect).should == Numeric.instance_method(:rectangular) + end end diff --git a/spec/ruby/core/numeric/rectangular_spec.rb b/spec/ruby/core/numeric/rectangular_spec.rb index 2c68985a16..81afccc12d 100644 --- a/spec/ruby/core/numeric/rectangular_spec.rb +++ b/spec/ruby/core/numeric/rectangular_spec.rb @@ -1,6 +1,48 @@ require_relative '../../spec_helper' -require_relative 'shared/rect' describe "Numeric#rectangular" do - it_behaves_like :numeric_rect, :rectangular + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + 99999999**99, # Bignum + infinity_value, + nan_value + ] + end + + it "returns an Array" do + @numbers.each do |number| + number.rectangular.should.instance_of?(Array) + end + end + + it "returns a two-element Array" do + @numbers.each do |number| + number.rectangular.size.should == 2 + end + end + + it "returns self as the first element" do + @numbers.each do |number| + if Float === number and number.nan? + number.rectangular.first.nan?.should == true + else + number.rectangular.first.should == number + end + end + end + + it "returns 0 as the last element" do + @numbers.each do |number| + number.rectangular.last.should == 0 + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + -> { number.rectangular(number) }.should.raise(ArgumentError) + end + end end diff --git a/spec/ruby/core/numeric/shared/abs.rb b/spec/ruby/core/numeric/shared/abs.rb deleted file mode 100644 index c3dadccfd6..0000000000 --- a/spec/ruby/core/numeric/shared/abs.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :numeric_abs, shared: true do - before :each do - @obj = NumericSpecs::Subclass.new - end - - it "returns self when self is greater than 0" do - @obj.should_receive(:<).with(0).and_return(false) - @obj.send(@method).should == @obj - end - - it "returns self\#@- when self is less than 0" do - @obj.should_receive(:<).with(0).and_return(true) - @obj.should_receive(:-@).and_return(:absolute_value) - @obj.send(@method).should == :absolute_value - end -end diff --git a/spec/ruby/core/numeric/shared/arg.rb b/spec/ruby/core/numeric/shared/arg.rb deleted file mode 100644 index c8e7ad8333..0000000000 --- a/spec/ruby/core/numeric/shared/arg.rb +++ /dev/null @@ -1,38 +0,0 @@ -require_relative '../../../spec_helper' - -describe :numeric_arg, shared: true do - before :each do - @numbers = [ - 20, - Rational(3, 4), - bignum_value, - infinity_value - ] - end - - it "returns 0 if positive" do - @numbers.each do |number| - number.send(@method).should == 0 - end - end - - it "returns Pi if negative" do - @numbers.each do |number| - (0-number).send(@method).should == Math::PI - end - end - - describe "with a Numeric subclass" do - it "returns 0 if self#<(0) returns false" do - numeric = mock_numeric('positive') - numeric.should_receive(:<).with(0).and_return(false) - numeric.send(@method).should == 0 - end - - it "returns Pi if self#<(0) returns true" do - numeric = mock_numeric('positive') - numeric.should_receive(:<).with(0).and_return(true) - numeric.send(@method).should == Math::PI - end - end -end diff --git a/spec/ruby/core/numeric/shared/conj.rb b/spec/ruby/core/numeric/shared/conj.rb deleted file mode 100644 index 1a661fbbe8..0000000000 --- a/spec/ruby/core/numeric/shared/conj.rb +++ /dev/null @@ -1,20 +0,0 @@ -require_relative '../../../spec_helper' - -describe :numeric_conj, shared: true do - before :each do - @numbers = [ - 20, # Integer - 398.72, # Float - Rational(3, 4), # Rational - bignum_value, - infinity_value, - nan_value - ] - end - - it "returns self" do - @numbers.each do |number| - number.send(@method).should.equal?(number) - end - end -end diff --git a/spec/ruby/core/numeric/shared/imag.rb b/spec/ruby/core/numeric/shared/imag.rb deleted file mode 100644 index 605a23d8c6..0000000000 --- a/spec/ruby/core/numeric/shared/imag.rb +++ /dev/null @@ -1,26 +0,0 @@ -require_relative '../../../spec_helper' - -describe :numeric_imag, shared: true do - before :each do - @numbers = [ - 20, # Integer - 398.72, # Float - Rational(3, 4), # Rational - bignum_value, # Bignum - infinity_value, - nan_value - ].map{|n| [n,-n]}.flatten - end - - it "returns 0" do - @numbers.each do |number| - number.send(@method).should == 0 - end - end - - it "raises an ArgumentError if given any arguments" do - @numbers.each do |number| - -> { number.send(@method, number) }.should.raise(ArgumentError) - end - end -end diff --git a/spec/ruby/core/numeric/shared/rect.rb b/spec/ruby/core/numeric/shared/rect.rb deleted file mode 100644 index 32d03e45d2..0000000000 --- a/spec/ruby/core/numeric/shared/rect.rb +++ /dev/null @@ -1,48 +0,0 @@ -require_relative '../../../spec_helper' - -describe :numeric_rect, shared: true do - before :each do - @numbers = [ - 20, # Integer - 398.72, # Float - Rational(3, 4), # Rational - 99999999**99, # Bignum - infinity_value, - nan_value - ] - end - - it "returns an Array" do - @numbers.each do |number| - number.send(@method).should.instance_of?(Array) - end - end - - it "returns a two-element Array" do - @numbers.each do |number| - number.send(@method).size.should == 2 - end - end - - it "returns self as the first element" do - @numbers.each do |number| - if Float === number and number.nan? - number.send(@method).first.nan?.should == true - else - number.send(@method).first.should == number - end - end - end - - it "returns 0 as the last element" do - @numbers.each do |number| - number.send(@method).last.should == 0 - end - end - - it "raises an ArgumentError if given any arguments" do - @numbers.each do |number| - -> { number.send(@method, number) }.should.raise(ArgumentError) - end - end -end diff --git a/spec/ruby/core/objectspace/weakmap/each_pair_spec.rb b/spec/ruby/core/objectspace/weakmap/each_pair_spec.rb index ea29edbd2f..272669ad0a 100644 --- a/spec/ruby/core/objectspace/weakmap/each_pair_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/each_pair_spec.rb @@ -1,11 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/members' -require_relative 'shared/each' describe "ObjectSpace::WeakMap#each_pair" do - it_behaves_like :weakmap_members, -> map { a = []; map.each_pair{ |k,v| a << "#{k}#{v}" }; a }, %w[Ax By] -end - -describe "ObjectSpace::WeakMap#each_key" do - it_behaves_like :weakmap_each, :each_pair + it "is an alias of ObjectSpace::WeakMap#each" do + ObjectSpace::WeakMap.instance_method(:each_pair).should == + ObjectSpace::WeakMap.instance_method(:each) + end end diff --git a/spec/ruby/core/objectspace/weakmap/each_spec.rb b/spec/ruby/core/objectspace/weakmap/each_spec.rb index 46fcb66a6f..8493a36158 100644 --- a/spec/ruby/core/objectspace/weakmap/each_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/each_spec.rb @@ -6,6 +6,6 @@ describe "ObjectSpace::WeakMap#each" do it_behaves_like :weakmap_members, -> map { a = []; map.each{ |k,v| a << "#{k}#{v}" }; a }, %w[Ax By] end -describe "ObjectSpace::WeakMap#each_key" do +describe "ObjectSpace::WeakMap#each" do it_behaves_like :weakmap_each, :each end diff --git a/spec/ruby/core/objectspace/weakmap/each_value_spec.rb b/spec/ruby/core/objectspace/weakmap/each_value_spec.rb index 65a1a7f6fe..89f2f6ae98 100644 --- a/spec/ruby/core/objectspace/weakmap/each_value_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/each_value_spec.rb @@ -6,6 +6,6 @@ describe "ObjectSpace::WeakMap#each_value" do it_behaves_like :weakmap_members, -> map { a = []; map.each_value{ |k| a << k }; a }, %w[x y] end -describe "ObjectSpace::WeakMap#each_key" do +describe "ObjectSpace::WeakMap#each_value" do it_behaves_like :weakmap_each, :each_value end diff --git a/spec/ruby/core/objectspace/weakmap/include_spec.rb b/spec/ruby/core/objectspace/weakmap/include_spec.rb index 54ca6b3030..1affaef907 100644 --- a/spec/ruby/core/objectspace/weakmap/include_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/include_spec.rb @@ -1,6 +1,32 @@ require_relative '../../../spec_helper' -require_relative 'shared/include' describe "ObjectSpace::WeakMap#include?" do - it_behaves_like :weakmap_include?, :include? + it "recognizes keys in use" do + map = ObjectSpace::WeakMap.new + key1, key2 = %w[a b].map(&:upcase) + ref1, ref2 = %w[x y] + + map[key1] = ref1 + map.include?(key1).should == true + map[key1] = ref1 + map.include?(key1).should == true + map[key2] = ref2 + map.include?(key2).should == true + end + + it "matches using identity semantics" do + map = ObjectSpace::WeakMap.new + key1, key2 = %w[a a].map(&:upcase) + ref = "x" + map[key1] = ref + map.include?(key2).should == false + end + + it "reports true if the pair exists and the value is nil" do + map = ObjectSpace::WeakMap.new + key = Object.new + map[key] = nil + map.size.should == 1 + map.include?(key).should == true + end end diff --git a/spec/ruby/core/objectspace/weakmap/key_spec.rb b/spec/ruby/core/objectspace/weakmap/key_spec.rb index 999685ff95..5d38f1fa5f 100644 --- a/spec/ruby/core/objectspace/weakmap/key_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/key_spec.rb @@ -1,6 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/include' describe "ObjectSpace::WeakMap#key?" do - it_behaves_like :weakmap_include?, :key? + it "is an alias of ObjectSpace::WeakMap#include?" do + ObjectSpace::WeakMap.instance_method(:key?).should == + ObjectSpace::WeakMap.instance_method(:include?) + end end diff --git a/spec/ruby/core/objectspace/weakmap/length_spec.rb b/spec/ruby/core/objectspace/weakmap/length_spec.rb index 3a935648b1..8ad47aa9d6 100644 --- a/spec/ruby/core/objectspace/weakmap/length_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/length_spec.rb @@ -1,6 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/size' describe "ObjectSpace::WeakMap#length" do - it_behaves_like :weakmap_size, :length + it "is an alias of ObjectSpace::WeakMap#size" do + ObjectSpace::WeakMap.instance_method(:length).should == + ObjectSpace::WeakMap.instance_method(:size) + end end diff --git a/spec/ruby/core/objectspace/weakmap/member_spec.rb b/spec/ruby/core/objectspace/weakmap/member_spec.rb index cefb190ce7..eaf9a76285 100644 --- a/spec/ruby/core/objectspace/weakmap/member_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/member_spec.rb @@ -1,6 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/include' describe "ObjectSpace::WeakMap#member?" do - it_behaves_like :weakmap_include?, :member? + it "is an alias of ObjectSpace::WeakMap#include?" do + ObjectSpace::WeakMap.instance_method(:member?).should == + ObjectSpace::WeakMap.instance_method(:include?) + end end diff --git a/spec/ruby/core/objectspace/weakmap/shared/include.rb b/spec/ruby/core/objectspace/weakmap/shared/include.rb deleted file mode 100644 index 1770eeac8b..0000000000 --- a/spec/ruby/core/objectspace/weakmap/shared/include.rb +++ /dev/null @@ -1,30 +0,0 @@ -describe :weakmap_include?, shared: true do - it "recognizes keys in use" do - map = ObjectSpace::WeakMap.new - key1, key2 = %w[a b].map(&:upcase) - ref1, ref2 = %w[x y] - - map[key1] = ref1 - map.send(@method, key1).should == true - map[key1] = ref1 - map.send(@method, key1).should == true - map[key2] = ref2 - map.send(@method, key2).should == true - end - - it "matches using identity semantics" do - map = ObjectSpace::WeakMap.new - key1, key2 = %w[a a].map(&:upcase) - ref = "x" - map[key1] = ref - map.send(@method, key2).should == false - end - - it "reports true if the pair exists and the value is nil" do - map = ObjectSpace::WeakMap.new - key = Object.new - map[key] = nil - map.size.should == 1 - map.send(@method, key).should == true - end -end diff --git a/spec/ruby/core/objectspace/weakmap/shared/size.rb b/spec/ruby/core/objectspace/weakmap/shared/size.rb deleted file mode 100644 index 1064f99d1b..0000000000 --- a/spec/ruby/core/objectspace/weakmap/shared/size.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe :weakmap_size, shared: true do - it "is correct" do - map = ObjectSpace::WeakMap.new - key1, key2 = %w[a b].map(&:upcase) - ref1, ref2 = %w[x y] - map.send(@method).should == 0 - map[key1] = ref1 - map.send(@method).should == 1 - map[key1] = ref1 - map.send(@method).should == 1 - map[key2] = ref2 - map.send(@method).should == 2 - end -end diff --git a/spec/ruby/core/objectspace/weakmap/size_spec.rb b/spec/ruby/core/objectspace/weakmap/size_spec.rb index 1446abaa24..d301750c62 100644 --- a/spec/ruby/core/objectspace/weakmap/size_spec.rb +++ b/spec/ruby/core/objectspace/weakmap/size_spec.rb @@ -1,6 +1,16 @@ require_relative '../../../spec_helper' -require_relative 'shared/size' describe "ObjectSpace::WeakMap#size" do - it_behaves_like :weakmap_size, :size + it "is correct" do + map = ObjectSpace::WeakMap.new + key1, key2 = %w[a b].map(&:upcase) + ref1, ref2 = %w[x y] + map.size.should == 0 + map[key1] = ref1 + map.size.should == 1 + map[key1] = ref1 + map.size.should == 1 + map[key2] = ref2 + map.size.should == 2 + end end diff --git a/spec/ruby/core/proc/call_spec.rb b/spec/ruby/core/proc/call_spec.rb index 6ec2fc8682..8b65be97c9 100644 --- a/spec/ruby/core/proc/call_spec.rb +++ b/spec/ruby/core/proc/call_spec.rb @@ -1,16 +1,138 @@ require_relative '../../spec_helper' -require_relative 'shared/call' -require_relative 'shared/call_arguments' +require_relative 'fixtures/common' +require_relative 'fixtures/proc_call' +require_relative 'fixtures/proc_call_frozen' describe "Proc#call" do - it_behaves_like :proc_call, :call - it_behaves_like :proc_call_block_args, :call -end + it "invokes self" do + Proc.new { "test!" }.call.should == "test!" + -> { "test!" }.call.should == "test!" + proc { "test!" }.call.should == "test!" + end -describe "Proc#call on a Proc created with Proc.new" do - it_behaves_like :proc_call_on_proc_new, :call -end + it "sets self's parameters to the given values" do + Proc.new { |a, b| a + b }.call(1, 2).should == 3 + Proc.new { |*args| args }.call(1, 2, 3, 4).should == [1, 2, 3, 4] + Proc.new { |_, *args| args }.call(1, 2, 3).should == [2, 3] + + -> a, b { a + b }.call(1, 2).should == 3 + -> *args { args }.call(1, 2, 3, 4).should == [1, 2, 3, 4] + -> _, *args { args }.call(1, 2, 3).should == [2, 3] + + proc { |a, b| a + b }.call(1, 2).should == 3 + proc { |*args| args }.call(1, 2, 3, 4).should == [1, 2, 3, 4] + proc { |_, *args| args }.call(1, 2, 3).should == [2, 3] + end + + it "can receive block arguments" do + Proc.new {|&b| b.call}.call {1 + 1}.should == 2 + -> &b { b.call}.call {1 + 1}.should == 2 + proc {|&b| b.call}.call {1 + 1}.should == 2 + end + + it "yields to the block given at declaration and not to the block argument" do + proc_creator = Object.new + def proc_creator.create + Proc.new do |&b| + yield + end + end + a_proc = proc_creator.create { 7 } + a_proc.call { 3 }.should == 7 + end + + it "can call its block argument declared with a block argument" do + proc_creator = Object.new + def proc_creator.create(method_name) + Proc.new do |&b| + yield + b.send(method_name) + end + end + a_proc = proc_creator.create(:call) { 7 } + a_proc.call { 3 }.should == 10 + end + + describe "on a Proc created with frozen_string_literal: true/false" do + it "doesn't duplicate frozen strings" do + ProcCallSpecs.call.frozen?.should == false + ProcCallSpecs.call_freeze.frozen?.should == true + ProcCallFrozenSpecs.call.frozen?.should == true + ProcCallFrozenSpecs.call_freeze.frozen?.should == true + end + end + + context "on a Proc created with Proc.new" do + it "replaces missing arguments with nil" do + Proc.new { |a, b| [a, b] }.call.should == [nil, nil] + Proc.new { |a, b| [a, b] }.call(1).should == [1, nil] + end + + it "silently ignores extra arguments" do + Proc.new { |a, b| a + b }.call(1, 2, 5).should == 3 + end + + it "auto-explodes a single Array argument" do + p = Proc.new { |a, b| [a, b] } + p.call(1, 2).should == [1, 2] + p.call([1, 2]).should == [1, 2] + p.call([1, 2, 3]).should == [1, 2] + p.call([1, 2, 3], 4).should == [[1, 2, 3], 4] + end + end + + context "on a Proc created with Kernel#lambda or Kernel#proc" do + it "ignores excess arguments when self is a proc" do + a = proc {|x| x}.call(1, 2) + a.should == 1 + + a = proc {|x| x}.call(1, 2, 3) + a.should == 1 + + a = proc {|x:| x}.call(2, x: 1) + a.should == 1 + end + + it "will call #to_ary on argument and return self if return is nil" do + argument = ProcSpecs::ToAryAsNil.new + result = proc { |x, _| x }.call(argument) + result.should == argument + end + + it "substitutes nil for missing arguments when self is a proc" do + proc {|x,y| [x,y]}.call.should == [nil,nil] + + a = proc {|x,y| [x, y]}.call(1) + a.should == [1,nil] + end + + it "raises an ArgumentError on excess arguments when self is a lambda" do + -> { + -> x { x }.call(1, 2) + }.should.raise(ArgumentError) + + -> { + -> x { x }.call(1, 2, 3) + }.should.raise(ArgumentError) + end + + it "raises an ArgumentError on missing arguments when self is a lambda" do + -> { + -> x { x }.call + }.should.raise(ArgumentError) + + -> { + -> x, y { [x,y] }.call(1) + }.should.raise(ArgumentError) + end + + it "treats a single Array argument as a single argument when self is a lambda" do + -> a { a }.call([1, 2]).should == [1, 2] + -> a, b { [a, b] }.call([1, 2], 3).should == [[1,2], 3] + end -describe "Proc#call on a Proc created with Kernel#lambda or Kernel#proc" do - it_behaves_like :proc_call_on_proc_or_lambda, :call + it "treats a single Array argument as a single argument when self is a proc" do + proc { |a| a }.call([1, 2]).should == [1, 2] + proc { |a, b| [a, b] }.call([1, 2], 3).should == [[1,2], 3] + end + end end diff --git a/spec/ruby/core/proc/case_compare_spec.rb b/spec/ruby/core/proc/case_compare_spec.rb index f11513cdb9..421afb24f0 100644 --- a/spec/ruby/core/proc/case_compare_spec.rb +++ b/spec/ruby/core/proc/case_compare_spec.rb @@ -1,16 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/call' -require_relative 'shared/call_arguments' describe "Proc#===" do - it_behaves_like :proc_call, :=== - it_behaves_like :proc_call_block_args, :=== -end - -describe "Proc#=== on a Proc created with Proc.new" do - it_behaves_like :proc_call_on_proc_new, :=== -end - -describe "Proc#=== on a Proc created with Kernel#lambda or Kernel#proc" do - it_behaves_like :proc_call_on_proc_or_lambda, :=== + it "is an alias of Proc#call" do + Proc.instance_method(:===).should == Proc.instance_method(:call) + end end diff --git a/spec/ruby/core/proc/element_reference_spec.rb b/spec/ruby/core/proc/element_reference_spec.rb index ea3a915a11..ac14292464 100644 --- a/spec/ruby/core/proc/element_reference_spec.rb +++ b/spec/ruby/core/proc/element_reference_spec.rb @@ -1,27 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/call' -require_relative 'shared/call_arguments' -require_relative 'fixtures/proc_aref' -require_relative 'fixtures/proc_aref_frozen' describe "Proc#[]" do - it_behaves_like :proc_call, :[] - it_behaves_like :proc_call_block_args, :[] -end - -describe "Proc#call on a Proc created with Proc.new" do - it_behaves_like :proc_call_on_proc_new, :call -end - -describe "Proc#call on a Proc created with Kernel#lambda or Kernel#proc" do - it_behaves_like :proc_call_on_proc_or_lambda, :call -end - -describe "Proc#[] with frozen_string_literal: true/false" do - it "doesn't duplicate frozen strings" do - ProcArefSpecs.aref.frozen?.should == false - ProcArefSpecs.aref_freeze.frozen?.should == true - ProcArefFrozenSpecs.aref.frozen?.should == true - ProcArefFrozenSpecs.aref_freeze.frozen?.should == true + it "is an alias of Proc#call" do + Proc.instance_method(:[]).should == Proc.instance_method(:call) end end diff --git a/spec/ruby/core/proc/eql_spec.rb b/spec/ruby/core/proc/eql_spec.rb index ad8f6749fc..1a5fb42a0e 100644 --- a/spec/ruby/core/proc/eql_spec.rb +++ b/spec/ruby/core/proc/eql_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/equal' describe "Proc#eql?" do - it_behaves_like :proc_equal, :eql? + it "is an alias of Proc#==" do + Proc.instance_method(:eql?).should == Proc.instance_method(:==) + end end diff --git a/spec/ruby/core/proc/equal_value_spec.rb b/spec/ruby/core/proc/equal_value_spec.rb index ec7f274732..92e462152e 100644 --- a/spec/ruby/core/proc/equal_value_spec.rb +++ b/spec/ruby/core/proc/equal_value_spec.rb @@ -1,6 +1,83 @@ require_relative '../../spec_helper' -require_relative 'shared/equal' +require_relative 'fixtures/common' describe "Proc#==" do - it_behaves_like :proc_equal, :== + it "is a public method" do + Proc.public_instance_methods(false).should.include?(:==) + end + + it "returns true if self and other are the same object" do + p = proc { :foo } + (p == p).should == true + + p = Proc.new { :foo } + (p == p).should == true + + p = -> { :foo } + (p == p).should == true + end + + it "returns true if other is a dup of the original" do + p = proc { :foo } + (p == p.dup).should == true + + p = Proc.new { :foo } + (p == p.dup).should == true + + p = -> { :foo } + (p == p.dup).should == true + end + + # identical here means the same method invocation. + it "returns false when bodies are the same but capture env is not identical" do + a = ProcSpecs.proc_for_1 + b = ProcSpecs.proc_for_1 + + (a == b).should == false + end + + it "returns false if procs are distinct but have the same body and environment" do + p = proc { :foo } + p2 = proc { :foo } + (p == p2).should == false + end + + it "returns false if lambdas are distinct but have same body and environment" do + x = -> { :foo } + x2 = -> { :foo } + (x == x2).should == false + end + + it "returns false if using comparing lambda to proc, even with the same body and env" do + p = -> { :foo } + p2 = proc { :foo } + (p == p2).should == false + + x = proc { :bar } + x2 = -> { :bar } + (x == x2).should == false + end + + it "returns false if other is not a Proc" do + p = proc { :foo } + (p == []).should == false + + p = Proc.new { :foo } + (p == Object.new).should == false + + p = -> { :foo } + (p == :foo).should == false + end + + it "returns false if self and other are both procs but have different bodies" do + p = proc { :bar } + p2 = proc { :foo } + (p == p2).should == false + end + + it "returns false if self and other are both lambdas but have different bodies" do + p = -> { :foo } + p2 = -> { :bar } + (p == p2).should == false + end end diff --git a/spec/ruby/core/proc/fixtures/proc_aref.rb b/spec/ruby/core/proc/fixtures/proc_aref.rb deleted file mode 100644 index 8ee355b14c..0000000000 --- a/spec/ruby/core/proc/fixtures/proc_aref.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: false -module ProcArefSpecs - def self.aref - proc {|a| a }["sometext"] - end - - def self.aref_freeze - proc {|a| a }["sometext".freeze] - end -end diff --git a/spec/ruby/core/proc/fixtures/proc_aref_frozen.rb b/spec/ruby/core/proc/fixtures/proc_aref_frozen.rb deleted file mode 100644 index 50a330ba4f..0000000000 --- a/spec/ruby/core/proc/fixtures/proc_aref_frozen.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -module ProcArefFrozenSpecs - def self.aref - proc {|a| a }["sometext"] - end - - def self.aref_freeze - proc {|a| a }["sometext".freeze] - end -end diff --git a/spec/ruby/core/proc/fixtures/proc_call.rb b/spec/ruby/core/proc/fixtures/proc_call.rb new file mode 100644 index 0000000000..32048f5319 --- /dev/null +++ b/spec/ruby/core/proc/fixtures/proc_call.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: false +module ProcCallSpecs + def self.call + proc {|a| a }.call("sometext") + end + + def self.call_freeze + proc {|a| a }.call("sometext".freeze) + end +end diff --git a/spec/ruby/core/proc/fixtures/proc_call_frozen.rb b/spec/ruby/core/proc/fixtures/proc_call_frozen.rb new file mode 100644 index 0000000000..29ffc3c4c9 --- /dev/null +++ b/spec/ruby/core/proc/fixtures/proc_call_frozen.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +module ProcCallFrozenSpecs + def self.call + proc {|a| a }.call("sometext") + end + + def self.call_freeze + proc {|a| a }.call("sometext".freeze) + end +end diff --git a/spec/ruby/core/proc/inspect_spec.rb b/spec/ruby/core/proc/inspect_spec.rb index f53d34116f..96995ec410 100644 --- a/spec/ruby/core/proc/inspect_spec.rb +++ b/spec/ruby/core/proc/inspect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' describe "Proc#inspect" do - it_behaves_like :proc_to_s, :inspect + it "is an alias of Proc#to_s" do + Proc.instance_method(:inspect).should == Proc.instance_method(:to_s) + end end diff --git a/spec/ruby/core/proc/shared/call.rb b/spec/ruby/core/proc/shared/call.rb deleted file mode 100644 index fae2331b68..0000000000 --- a/spec/ruby/core/proc/shared/call.rb +++ /dev/null @@ -1,99 +0,0 @@ -require_relative '../fixtures/common' - -describe :proc_call, shared: true do - it "invokes self" do - Proc.new { "test!" }.send(@method).should == "test!" - -> { "test!" }.send(@method).should == "test!" - proc { "test!" }.send(@method).should == "test!" - end - - it "sets self's parameters to the given values" do - Proc.new { |a, b| a + b }.send(@method, 1, 2).should == 3 - Proc.new { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4] - Proc.new { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3] - - -> a, b { a + b }.send(@method, 1, 2).should == 3 - -> *args { args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4] - -> _, *args { args }.send(@method, 1, 2, 3).should == [2, 3] - - proc { |a, b| a + b }.send(@method, 1, 2).should == 3 - proc { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4] - proc { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3] - end -end - - -describe :proc_call_on_proc_new, shared: true do - it "replaces missing arguments with nil" do - Proc.new { |a, b| [a, b] }.send(@method).should == [nil, nil] - Proc.new { |a, b| [a, b] }.send(@method, 1).should == [1, nil] - end - - it "silently ignores extra arguments" do - Proc.new { |a, b| a + b }.send(@method, 1, 2, 5).should == 3 - end - - it "auto-explodes a single Array argument" do - p = Proc.new { |a, b| [a, b] } - p.send(@method, 1, 2).should == [1, 2] - p.send(@method, [1, 2]).should == [1, 2] - p.send(@method, [1, 2, 3]).should == [1, 2] - p.send(@method, [1, 2, 3], 4).should == [[1, 2, 3], 4] - end -end - -describe :proc_call_on_proc_or_lambda, shared: true do - it "ignores excess arguments when self is a proc" do - a = proc {|x| x}.send(@method, 1, 2) - a.should == 1 - - a = proc {|x| x}.send(@method, 1, 2, 3) - a.should == 1 - - a = proc {|x:| x}.send(@method, 2, x: 1) - a.should == 1 - end - - it "will call #to_ary on argument and return self if return is nil" do - argument = ProcSpecs::ToAryAsNil.new - result = proc { |x, _| x }.send(@method, argument) - result.should == argument - end - - it "substitutes nil for missing arguments when self is a proc" do - proc {|x,y| [x,y]}.send(@method).should == [nil,nil] - - a = proc {|x,y| [x, y]}.send(@method, 1) - a.should == [1,nil] - end - - it "raises an ArgumentError on excess arguments when self is a lambda" do - -> { - -> x { x }.send(@method, 1, 2) - }.should.raise(ArgumentError) - - -> { - -> x { x }.send(@method, 1, 2, 3) - }.should.raise(ArgumentError) - end - - it "raises an ArgumentError on missing arguments when self is a lambda" do - -> { - -> x { x }.send(@method) - }.should.raise(ArgumentError) - - -> { - -> x, y { [x,y] }.send(@method, 1) - }.should.raise(ArgumentError) - end - - it "treats a single Array argument as a single argument when self is a lambda" do - -> a { a }.send(@method, [1, 2]).should == [1, 2] - -> a, b { [a, b] }.send(@method, [1, 2], 3).should == [[1,2], 3] - end - - it "treats a single Array argument as a single argument when self is a proc" do - proc { |a| a }.send(@method, [1, 2]).should == [1, 2] - proc { |a, b| [a, b] }.send(@method, [1, 2], 3).should == [[1,2], 3] - end -end diff --git a/spec/ruby/core/proc/shared/call_arguments.rb b/spec/ruby/core/proc/shared/call_arguments.rb deleted file mode 100644 index 91ada3439e..0000000000 --- a/spec/ruby/core/proc/shared/call_arguments.rb +++ /dev/null @@ -1,29 +0,0 @@ -describe :proc_call_block_args, shared: true do - it "can receive block arguments" do - Proc.new {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2 - -> &b { b.send(@method)}.send(@method) {1 + 1}.should == 2 - proc {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2 - end - - it "yields to the block given at declaration and not to the block argument" do - proc_creator = Object.new - def proc_creator.create - Proc.new do |&b| - yield - end - end - a_proc = proc_creator.create { 7 } - a_proc.send(@method) { 3 }.should == 7 - end - - it "can call its block argument declared with a block argument" do - proc_creator = Object.new - def proc_creator.create(method_name) - Proc.new do |&b| - yield + b.send(method_name) - end - end - a_proc = proc_creator.create(@method) { 7 } - a_proc.call { 3 }.should == 10 - end -end diff --git a/spec/ruby/core/proc/shared/equal.rb b/spec/ruby/core/proc/shared/equal.rb deleted file mode 100644 index 4f6f6c41be..0000000000 --- a/spec/ruby/core/proc/shared/equal.rb +++ /dev/null @@ -1,83 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/common' - -describe :proc_equal, shared: true do - it "is a public method" do - Proc.public_instance_methods(false).should.include?(@method) - end - - it "returns true if self and other are the same object" do - p = proc { :foo } - p.send(@method, p).should == true - - p = Proc.new { :foo } - p.send(@method, p).should == true - - p = -> { :foo } - p.send(@method, p).should == true - end - - it "returns true if other is a dup of the original" do - p = proc { :foo } - p.send(@method, p.dup).should == true - - p = Proc.new { :foo } - p.send(@method, p.dup).should == true - - p = -> { :foo } - p.send(@method, p.dup).should == true - end - - # identical here means the same method invocation. - it "returns false when bodies are the same but capture env is not identical" do - a = ProcSpecs.proc_for_1 - b = ProcSpecs.proc_for_1 - - a.send(@method, b).should == false - end - - it "returns false if procs are distinct but have the same body and environment" do - p = proc { :foo } - p2 = proc { :foo } - p.send(@method, p2).should == false - end - - it "returns false if lambdas are distinct but have same body and environment" do - x = -> { :foo } - x2 = -> { :foo } - x.send(@method, x2).should == false - end - - it "returns false if using comparing lambda to proc, even with the same body and env" do - p = -> { :foo } - p2 = proc { :foo } - p.send(@method, p2).should == false - - x = proc { :bar } - x2 = -> { :bar } - x.send(@method, x2).should == false - end - - it "returns false if other is not a Proc" do - p = proc { :foo } - p.send(@method, []).should == false - - p = Proc.new { :foo } - p.send(@method, Object.new).should == false - - p = -> { :foo } - p.send(@method, :foo).should == false - end - - it "returns false if self and other are both procs but have different bodies" do - p = proc { :bar } - p2 = proc { :foo } - p.send(@method, p2).should == false - end - - it "returns false if self and other are both lambdas but have different bodies" do - p = -> { :foo } - p2 = -> { :bar } - p.send(@method, p2).should == false - end -end diff --git a/spec/ruby/core/proc/shared/to_s.rb b/spec/ruby/core/proc/shared/to_s.rb deleted file mode 100644 index a52688a89f..0000000000 --- a/spec/ruby/core/proc/shared/to_s.rb +++ /dev/null @@ -1,60 +0,0 @@ -describe :proc_to_s, shared: true do - describe "for a proc created with Proc.new" do - it "returns a description including file and line number" do - Proc.new { "hello" }.send(@method).should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ }>$/ - end - - it "has a binary encoding" do - Proc.new { "hello" }.send(@method).encoding.should == Encoding::BINARY - end - end - - describe "for a proc created with lambda" do - it "returns a description including '(lambda)' and including file and line number" do - -> { "hello" }.send(@method).should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ } \(lambda\)>$/ - end - - it "has a binary encoding" do - -> { "hello" }.send(@method).encoding.should == Encoding::BINARY - end - end - - describe "for a proc created with proc" do - it "returns a description including file and line number" do - proc { "hello" }.send(@method).should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ }>$/ - end - - it "has a binary encoding" do - proc { "hello" }.send(@method).encoding.should == Encoding::BINARY - end - end - - describe "for a proc created with UnboundMethod#to_proc" do - it "returns a description including '(lambda)' and optionally including file and line number" do - def hello; end - s = method("hello").to_proc.send(@method) - if s.include? __FILE__ - s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ - 3} \(lambda\)>$/ - else - s.should =~ /^#<Proc:([^ ]*?) \(lambda\)>$/ - end - end - - it "has a binary encoding" do - def hello; end - method("hello").to_proc.send(@method).encoding.should == Encoding::BINARY - end - end - - describe "for a proc created with Symbol#to_proc" do - it "returns a description including '(&:symbol)'" do - proc = :foobar.to_proc - proc.send(@method).should.include?('(&:foobar)') - end - - it "has a binary encoding" do - proc = :foobar.to_proc - proc.send(@method).encoding.should == Encoding::BINARY - end - end -end diff --git a/spec/ruby/core/proc/to_s_spec.rb b/spec/ruby/core/proc/to_s_spec.rb index 5e9c46b6b8..58a9aa76fb 100644 --- a/spec/ruby/core/proc/to_s_spec.rb +++ b/spec/ruby/core/proc/to_s_spec.rb @@ -1,6 +1,62 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' describe "Proc#to_s" do - it_behaves_like :proc_to_s, :to_s + describe "for a proc created with Proc.new" do + it "returns a description including file and line number" do + Proc.new { "hello" }.to_s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ }>$/ + end + + it "has a binary encoding" do + Proc.new { "hello" }.to_s.encoding.should == Encoding::BINARY + end + end + + describe "for a proc created with lambda" do + it "returns a description including '(lambda)' and including file and line number" do + -> { "hello" }.to_s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ } \(lambda\)>$/ + end + + it "has a binary encoding" do + -> { "hello" }.to_s.encoding.should == Encoding::BINARY + end + end + + describe "for a proc created with proc" do + it "returns a description including file and line number" do + proc { "hello" }.to_s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ }>$/ + end + + it "has a binary encoding" do + proc { "hello" }.to_s.encoding.should == Encoding::BINARY + end + end + + describe "for a proc created with UnboundMethod#to_proc" do + it "returns a description including '(lambda)' and optionally including file and line number" do + def hello; end + s = method("hello").to_proc.to_s + if s.include? __FILE__ + s.should =~ /^#<Proc:([^ ]*?) #{Regexp.escape __FILE__}:#{__LINE__ - 3} \(lambda\)>$/ + else + s.should =~ /^#<Proc:([^ ]*?) \(lambda\)>$/ + end + end + + it "has a binary encoding" do + def hello; end + method("hello").to_proc.to_s.encoding.should == Encoding::BINARY + end + end + + describe "for a proc created with Symbol#to_proc" do + it "returns a description including '(&:symbol)'" do + proc = :foobar.to_proc + proc.to_s.should.include?('(&:foobar)') + end + + it "has a binary encoding" do + proc = :foobar.to_proc + proc.to_s.encoding.should == Encoding::BINARY + end + end end diff --git a/spec/ruby/core/proc/yield_spec.rb b/spec/ruby/core/proc/yield_spec.rb index 365d5b04bd..e6ee2d5eff 100644 --- a/spec/ruby/core/proc/yield_spec.rb +++ b/spec/ruby/core/proc/yield_spec.rb @@ -1,16 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/call' -require_relative 'shared/call_arguments' describe "Proc#yield" do - it_behaves_like :proc_call, :yield - it_behaves_like :proc_call_block_args, :yield -end - -describe "Proc#yield on a Proc created with Proc.new" do - it_behaves_like :proc_call_on_proc_new, :yield -end - -describe "Proc#yield on a Proc created with Kernel#lambda or Kernel#proc" do - it_behaves_like :proc_call_on_proc_or_lambda, :yield + it "is an alias of Proc#call" do + Proc.instance_method(:yield).should == Proc.instance_method(:call) + end end diff --git a/spec/ruby/core/process/daemon_spec.rb b/spec/ruby/core/process/daemon_spec.rb index 9b7eba1411..7198dfa6ee 100644 --- a/spec/ruby/core/process/daemon_spec.rb +++ b/spec/ruby/core/process/daemon_spec.rb @@ -1,10 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/common' -platform_is_not :windows do - # macOS 15 is not working this examples - return if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion` - +guard -> { + Process.respond_to?(:fork) and + # macOS 15 is not working for these examples + !(/darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion`) +} do describe :process_daemon_keep_stdio_open_false, shared: true do it "redirects stdout to /dev/null" do @daemon.invoke("keep_stdio_open_false_stdout", @object).should == "" @@ -107,8 +108,12 @@ platform_is_not :windows do end end -platform_is :windows do +guard_not -> { Process.respond_to?(:fork) } do describe "Process.daemon" do + it "returns false from #respond_to?" do + Process.respond_to?(:daemon).should == false + end + it "raises a NotImplementedError" do -> { Process.daemon diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb index 33c674394e..862768a909 100644 --- a/spec/ruby/core/process/detach_spec.rb +++ b/spec/ruby/core/process/detach_spec.rb @@ -1,81 +1,82 @@ require_relative '../../spec_helper' +require_relative 'fixtures/common' describe "Process.detach" do - platform_is_not :windows do - it "returns a thread" do - pid = Process.fork { Process.exit! } - thr = Process.detach(pid) - thr.should.is_a?(Thread) - thr.join - end + ProcessSpecs.use_system_ruby(self) - it "produces the exit Process::Status as the thread value" do - pid = Process.fork { Process.exit! } - thr = Process.detach(pid) - thr.join + it "returns a thread" do + pid = Process.spawn(*ruby_exe, "-e", "exit") + thr = Process.detach(pid) + thr.should.is_a?(Thread) + thr.join + end - status = thr.value - status.should.is_a?(Process::Status) - status.pid.should == pid - end + it "produces the exit Process::Status as the thread value" do + pid = Process.spawn(*ruby_exe, "-e", "exit") + thr = Process.detach(pid) + thr.join + + status = thr.value + status.should.is_a?(Process::Status) + status.pid.should == pid + end - platform_is_not :openbsd do - it "reaps the child process's status automatically" do - pid = Process.fork { Process.exit! } - Process.detach(pid).join - -> { Process.waitpid(pid) }.should.raise(Errno::ECHILD) - end + platform_is_not :openbsd do + it "reaps the child process's status automatically" do + pid = Process.spawn(*ruby_exe, "-e", "exit") + Process.detach(pid).join + -> { Process.waitpid(pid) }.should.raise(Errno::ECHILD) end + end - it "sets the :pid thread-local to the PID" do - pid = Process.fork { Process.exit! } - thr = Process.detach(pid) - thr.join + it "sets the :pid thread-local to the PID" do + pid = Process.spawn(*ruby_exe, "-e", "exit") + thr = Process.detach(pid) + thr.join - thr[:pid].should == pid - end + thr[:pid].should == pid + end - it "provides a #pid method on the returned thread which returns the PID" do - pid = Process.fork { Process.exit! } - thr = Process.detach(pid) - thr.join + it "provides a #pid method on the returned thread which returns the PID" do + pid = Process.spawn(*ruby_exe, "-e", "exit") + thr = Process.detach(pid) + thr.join - thr.pid.should == pid - end + thr.pid.should == pid + end - it "tolerates not existing child process pid" do - # Use a value that is close to the INT_MAX (pid usually is signed int). - # It should (at least) be greater than allowed pid limit value that depends on OS. - pid_not_existing = 2.pow(30) + it "tolerates not existing child process pid" do + # Use a value that is close to the INT_MAX (pid usually is signed int). + # It should (at least) be greater than allowed pid limit value that depends on OS. + pid_not_existing = 2.pow(30) - # Check that there is no a child process with this hardcoded pid. - # Command `kill 0 pid`: - # - returns "1" if a process exists and - # - raises Errno::ESRCH otherwise - -> { Process.kill(0, pid_not_existing) }.should.raise(Errno::ESRCH) + # Check that there is no a child process with this hardcoded pid. + # Command `kill 0 pid`: + # - returns "1" if a process exists and + # - raises Errno::ESRCH otherwise + -> { Process.kill(0, pid_not_existing) }.should.raise(Errno::ESRCH) - thr = Process.detach(pid_not_existing) - thr.join + thr = Process.detach(pid_not_existing) + thr.join - thr.should.is_a?(Thread) - end + thr.should.is_a?(Thread) + end - it "calls #to_int to implicitly convert non-Integer pid to Integer" do - pid = MockObject.new('mock-enumerable') - pid.should_receive(:to_int).and_return(100500) + it "calls #to_int to implicitly convert non-Integer pid to Integer" do + pid = MockObject.new('mock-enumerable') + pid.should_receive(:to_int).and_return(100500) - Process.detach(pid).join - end + Process.detach(pid).join + end - it "raises TypeError when pid argument does not have #to_int method" do - -> { Process.detach(Object.new) }.should.raise(TypeError, "no implicit conversion of Object into Integer") - end + it "raises TypeError when pid argument does not have #to_int method" do + -> { Process.detach(Object.new) }.should.raise(TypeError, "no implicit conversion of Object into Integer") + end - it "raises TypeError when #to_int returns non-Integer value" do - pid = MockObject.new('mock-enumerable') - pid.should_receive(:to_int).and_return(:symbol) + it "raises TypeError when #to_int returns non-Integer value" do + pid = MockObject.new('mock-enumerable') + pid.should_receive(:to_int).and_return(:symbol) - -> { Process.detach(pid) }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_int gives Symbol)") - end + -> { Process.detach(pid) }.should raise_consistent_error(TypeError, "can't convert MockObject into Integer (MockObject#to_int gives Symbol)") end end diff --git a/spec/ruby/core/process/setpgid_spec.rb b/spec/ruby/core/process/setpgid_spec.rb index be724e9007..1442d7f99c 100644 --- a/spec/ruby/core/process/setpgid_spec.rb +++ b/spec/ruby/core/process/setpgid_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' describe "Process.setpgid" do - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do # Must use fork as setpgid(2) gives EACCESS after execve() it "sets the process group id of the specified process" do rd, wr = IO.pipe diff --git a/spec/ruby/core/process/setpgrp_spec.rb b/spec/ruby/core/process/setpgrp_spec.rb index 800668008d..7c4344f115 100644 --- a/spec/ruby/core/process/setpgrp_spec.rb +++ b/spec/ruby/core/process/setpgrp_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' # TODO: put these in the right files. describe "Process.setpgrp and Process.getpgrp" do - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do it "sets and gets the process group ID of the calling process" do # there are two synchronization points here: # One for the child to let the parent know that it has finished diff --git a/spec/ruby/core/process/status/wait_spec.rb b/spec/ruby/core/process/status/wait_spec.rb index 18ecc14f6f..8bd7fc6b43 100644 --- a/spec/ruby/core/process/status/wait_spec.rb +++ b/spec/ruby/core/process/status/wait_spec.rb @@ -70,25 +70,27 @@ describe "Process::Status.wait" do end # This spec is probably system-dependent. - it "doesn't block if no child is available when WNOHANG is used" do - read, write = IO.pipe - pid = Process.fork do - read.close - Signal.trap("TERM") { Process.exit! } - write << 1 + guard -> { Process.respond_to?(:fork) } do + it "doesn't block if no child is available when WNOHANG is used" do + read, write = IO.pipe + pid = Process.fork do + read.close + Signal.trap("TERM") { Process.exit! } + write << 1 + write.close + sleep + end + + Process::Status.wait(pid, Process::WNOHANG).should == nil + + # wait for the child to setup its TERM handler write.close - sleep - end - - Process::Status.wait(pid, Process::WNOHANG).should == nil - - # wait for the child to setup its TERM handler - write.close - read.read(1) - read.close + read.read(1) + read.close - Process.kill("TERM", pid) - Process::Status.wait.pid.should == pid + Process.kill("TERM", pid) + Process::Status.wait.pid.should == pid + end end it "always accepts flags=0" do diff --git a/spec/ruby/core/process/wait2_spec.rb b/spec/ruby/core/process/wait2_spec.rb index 5c57dd40fb..1fa6f76151 100644 --- a/spec/ruby/core/process/wait2_spec.rb +++ b/spec/ruby/core/process/wait2_spec.rb @@ -1,6 +1,9 @@ require_relative '../../spec_helper' +require_relative 'fixtures/common' describe "Process.wait2" do + ProcessSpecs.use_system_ruby(self) + before :all do # HACK: this kludge is temporarily necessary because some # misbehaving spec somewhere else does not clear processes @@ -18,15 +21,13 @@ describe "Process.wait2" do end end - platform_is_not :windows do - it "returns the pid and status of child process" do - pidf = Process.fork { Process.exit! 99 } - results = Process.wait2 - results.size.should == 2 - pidw, status = results - pidf.should == pidw - status.exitstatus.should == 99 - end + it "returns the pid and status of child process" do + pidf = Process.spawn(*ruby_exe, "-e", "exit 99") + results = Process.wait2 + results.size.should == 2 + pidw, status = results + pidf.should == pidw + status.exitstatus.should == 99 end it "raises a StandardError if no child processes exist" do diff --git a/spec/ruby/core/process/wait_spec.rb b/spec/ruby/core/process/wait_spec.rb index 0b2e715f65..5a1889487c 100644 --- a/spec/ruby/core/process/wait_spec.rb +++ b/spec/ruby/core/process/wait_spec.rb @@ -17,19 +17,30 @@ describe "Process.wait" do -> { Process.wait }.should.raise(Errno::ECHILD) end - platform_is_not :windows do - it "returns its child pid" do - pid = Process.spawn(ruby_cmd('exit')) - Process.wait.should == pid - end + it "returns its child pid" do + pid = Process.spawn(ruby_cmd('exit')) + Process.wait.should == pid + end - it "sets $? to a Process::Status" do - pid = Process.spawn(ruby_cmd('exit')) - Process.wait - $?.should.is_a?(Process::Status) - $?.pid.should == pid + it "returns nil when the process has not yet completed and WNOHANG is specified" do + cmd = platform_is(:windows) ? "timeout" : "sleep" + pid = spawn("#{cmd} 5") + begin + Process.wait(pid, Process::WNOHANG).should == nil + Process.kill("KILL", pid) + ensure + Process.wait(pid) end + end + + it "sets $? to a Process::Status" do + pid = Process.spawn(ruby_cmd('exit')) + Process.wait + $?.should.is_a?(Process::Status) + $?.pid.should == pid + end + platform_is_not :windows do it "waits for any child process if no pid is given" do pid = Process.spawn(ruby_cmd('exit')) Process.wait.should == pid @@ -59,8 +70,10 @@ describe "Process.wait" do Process.wait(0).should == pid2 Process.wait.should == pid1 end + end - # This spec is probably system-dependent. + # This spec is probably system-dependent. + guard -> { Process.respond_to?(:fork) } do it "doesn't block if no child is available when WNOHANG is used" do read, write = IO.pipe pid = Process.fork do @@ -81,7 +94,9 @@ describe "Process.wait" do Process.kill("TERM", pid) Process.wait.should == pid end + end + platform_is_not :windows do it "always accepts flags=0" do pid = Process.spawn(ruby_cmd('exit')) Process.wait(-1, 0).should == pid diff --git a/spec/ruby/core/process/waitall_spec.rb b/spec/ruby/core/process/waitall_spec.rb index a77873f553..f5fbce71c1 100644 --- a/spec/ruby/core/process/waitall_spec.rb +++ b/spec/ruby/core/process/waitall_spec.rb @@ -1,6 +1,9 @@ require_relative '../../spec_helper' +require_relative 'fixtures/common' describe "Process.waitall" do + ProcessSpecs.use_system_ruby(self) + before :all do begin Process.waitall @@ -17,23 +20,16 @@ describe "Process.waitall" do end platform_is_not :windows do - it "waits for all children" do + it "waits for all children and returns an array of pid/status pairs" do pids = [] - pids << Process.fork { Process.exit! 2 } - pids << Process.fork { Process.exit! 1 } - pids << Process.fork { Process.exit! 0 } - Process.waitall + pids << Process.spawn(ruby_cmd('exit 2')) + pids << Process.spawn(ruby_cmd('exit 1')) + pids << Process.spawn(ruby_cmd('exit 0')) + a = Process.waitall pids.each { |pid| -> { Process.kill(0, pid) }.should.raise(Errno::ESRCH) } - end - it "returns an array of pid/status pairs" do - pids = [] - pids << Process.fork { Process.exit! 2 } - pids << Process.fork { Process.exit! 1 } - pids << Process.fork { Process.exit! 0 } - a = Process.waitall a.should.is_a?(Array) a.size.should == 3 pids.each { |pid| diff --git a/spec/ruby/core/process/waitpid2_spec.rb b/spec/ruby/core/process/waitpid2_spec.rb index 45513af667..70fe0fbeee 100644 --- a/spec/ruby/core/process/waitpid2_spec.rb +++ b/spec/ruby/core/process/waitpid2_spec.rb @@ -1,5 +1,7 @@ require_relative '../../spec_helper' describe "Process.waitpid2" do - it "needs to be reviewed for spec completeness" + it "is an alias of Process.wait2" do + Process.method(:waitpid2).should == Process.method(:wait2) + end end diff --git a/spec/ruby/core/process/waitpid_spec.rb b/spec/ruby/core/process/waitpid_spec.rb index a02147b663..9b2f49e7cf 100644 --- a/spec/ruby/core/process/waitpid_spec.rb +++ b/spec/ruby/core/process/waitpid_spec.rb @@ -1,14 +1,7 @@ require_relative '../../spec_helper' describe "Process.waitpid" do - it "returns nil when the process has not yet completed and WNOHANG is specified" do - cmd = platform_is(:windows) ? "timeout" : "sleep" - pid = spawn("#{cmd} 5") - begin - Process.waitpid(pid, Process::WNOHANG).should == nil - Process.kill("KILL", pid) - ensure - Process.wait(pid) - end + it "is an alias of Process.wait" do + Process.method(:waitpid).should == Process.method(:wait) end end diff --git a/spec/ruby/core/queue/deq_spec.rb b/spec/ruby/core/queue/deq_spec.rb index a2784e6a63..374611366e 100644 --- a/spec/ruby/core/queue/deq_spec.rb +++ b/spec/ruby/core/queue/deq_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/deque' -require_relative '../../shared/types/rb_num2dbl_fails' describe "Queue#deq" do - it_behaves_like :queue_deq, :deq, -> { Queue.new } -end - -describe "Queue operations with timeout" do - it_behaves_like :rb_num2dbl_fails, nil, -> v { q = Queue.new; q.push(1); q.deq(timeout: v) } + it "is an alias of Queue#pop" do + Queue.instance_method(:deq).should == Queue.instance_method(:pop) + end end diff --git a/spec/ruby/core/queue/enq_spec.rb b/spec/ruby/core/queue/enq_spec.rb index c69c496fbc..76ecf0ca5f 100644 --- a/spec/ruby/core/queue/enq_spec.rb +++ b/spec/ruby/core/queue/enq_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/enque' describe "Queue#enq" do - it_behaves_like :queue_enq, :enq, -> { Queue.new } + it "is an alias of Queue#<<" do + Queue.instance_method(:enq).should == Queue.instance_method(:<<) + end end diff --git a/spec/ruby/core/queue/length_spec.rb b/spec/ruby/core/queue/length_spec.rb index 25399b2b76..0566b1d547 100644 --- a/spec/ruby/core/queue/length_spec.rb +++ b/spec/ruby/core/queue/length_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/length' describe "Queue#length" do - it_behaves_like :queue_length, :length, -> { Queue.new } + it "is an alias of Queue#size" do + Queue.instance_method(:length).should == Queue.instance_method(:size) + end end diff --git a/spec/ruby/core/queue/push_spec.rb b/spec/ruby/core/queue/push_spec.rb index e936f9d282..ef622ac89d 100644 --- a/spec/ruby/core/queue/push_spec.rb +++ b/spec/ruby/core/queue/push_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/enque' describe "Queue#push" do - it_behaves_like :queue_enq, :push, -> { Queue.new } + it "is an alias of Queue#<<" do + Queue.instance_method(:push).should == Queue.instance_method(:<<) + end end diff --git a/spec/ruby/core/queue/shift_spec.rb b/spec/ruby/core/queue/shift_spec.rb index c105da74b2..074332359c 100644 --- a/spec/ruby/core/queue/shift_spec.rb +++ b/spec/ruby/core/queue/shift_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/deque' -require_relative '../../shared/types/rb_num2dbl_fails' describe "Queue#shift" do - it_behaves_like :queue_deq, :shift, -> { Queue.new } -end - -describe "Queue operations with timeout" do - it_behaves_like :rb_num2dbl_fails, nil, -> v { q = Queue.new; q.push(1); q.shift(timeout: v) } + it "is an alias of Queue#pop" do + Queue.instance_method(:shift).should == Queue.instance_method(:pop) + end end diff --git a/spec/ruby/core/range/entries_spec.rb b/spec/ruby/core/range/entries_spec.rb new file mode 100644 index 0000000000..29296711dc --- /dev/null +++ b/spec/ruby/core/range/entries_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Range#entries" do + it "is an alias of Range#to_a" do + Range.instance_method(:entries).should == Range.instance_method(:to_a) + end +end diff --git a/spec/ruby/core/range/include_spec.rb b/spec/ruby/core/range/include_spec.rb index 66a049a90d..e5cc0dc234 100644 --- a/spec/ruby/core/range/include_spec.rb +++ b/spec/ruby/core/range/include_spec.rb @@ -1,12 +1,96 @@ # encoding: binary require_relative '../../spec_helper' +require_relative 'fixtures/classes' require_relative 'shared/cover_and_include' -require_relative 'shared/include' -require_relative 'shared/cover' describe "Range#include?" do it_behaves_like :range_cover_and_include, :include? - it_behaves_like :range_include, :include? + + describe "on string elements" do + it "returns true if other is matched by element.succ" do + ('a'..'c').include?('b').should == true + ('a'...'c').include?('b').should == true + end + + it "returns false if other is not matched by element.succ" do + ('a'..'c').include?('bc').should == false + ('a'...'c').include?('bc').should == false + end + end + + describe "with weird succ" do + describe "when included end value" do + before :each do + @range = RangeSpecs::TenfoldSucc.new(1)..RangeSpecs::TenfoldSucc.new(99) + end + + it "returns false if other is less than first element" do + @range.include?(RangeSpecs::TenfoldSucc.new(0)).should == false + end + + it "returns true if other is equal as first element" do + @range.include?(RangeSpecs::TenfoldSucc.new(1)).should == true + end + + it "returns true if other is matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(10)).should == true + end + + it "returns false if other is not matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(2)).should == false + end + + it "returns false if other is equal as last element but not matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(99)).should == false + end + + it "returns false if other is greater than last element but matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(100)).should == false + end + end + + describe "when excluded end value" do + before :each do + @range = RangeSpecs::TenfoldSucc.new(1)...RangeSpecs::TenfoldSucc.new(99) + end + + it "returns false if other is less than first element" do + @range.include?(RangeSpecs::TenfoldSucc.new(0)).should == false + end + + it "returns true if other is equal as first element" do + @range.include?(RangeSpecs::TenfoldSucc.new(1)).should == true + end + + it "returns true if other is matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(10)).should == true + end + + it "returns false if other is not matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(2)).should == false + end + + it "returns false if other is equal as last element but not matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(99)).should == false + end + + it "returns false if other is greater than last element but matched by element.succ" do + @range.include?(RangeSpecs::TenfoldSucc.new(100)).should == false + end + end + end + + describe "with Time endpoints" do + it "uses cover? logic" do + now = Time.now + range = (now..(now + 60)) + + range.include?(now).should == true + range.include?(now - 1).should == false + range.include?(now + 60).should == true + range.include?(now + 61).should == false + end + end it "does not include U+9995 in the range U+0999..U+9999" do ("\u{999}".."\u{9999}").include?("\u{9995}").should == false diff --git a/spec/ruby/core/range/member_spec.rb b/spec/ruby/core/range/member_spec.rb index 78299ae9e5..98835e4cf3 100644 --- a/spec/ruby/core/range/member_spec.rb +++ b/spec/ruby/core/range/member_spec.rb @@ -1,10 +1,7 @@ -# encoding: binary require_relative '../../spec_helper' -require_relative 'shared/cover_and_include' -require_relative 'shared/include' -require_relative 'shared/cover' describe "Range#member?" do - it_behaves_like :range_cover_and_include, :member? - it_behaves_like :range_include, :member? + it "is an alias of Range#include?" do + Range.instance_method(:member?).should == Range.instance_method(:include?) + end end diff --git a/spec/ruby/core/range/shared/include.rb b/spec/ruby/core/range/shared/include.rb deleted file mode 100644 index 5f0db48008..0000000000 --- a/spec/ruby/core/range/shared/include.rb +++ /dev/null @@ -1,91 +0,0 @@ -# encoding: binary -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :range_include, shared: true do - describe "on string elements" do - it "returns true if other is matched by element.succ" do - ('a'..'c').send(@method, 'b').should == true - ('a'...'c').send(@method, 'b').should == true - end - - it "returns false if other is not matched by element.succ" do - ('a'..'c').send(@method, 'bc').should == false - ('a'...'c').send(@method, 'bc').should == false - end - end - - describe "with weird succ" do - describe "when included end value" do - before :each do - @range = RangeSpecs::TenfoldSucc.new(1)..RangeSpecs::TenfoldSucc.new(99) - end - - it "returns false if other is less than first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should == false - end - - it "returns true if other is equal as first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should == true - end - - it "returns true if other is matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should == true - end - - it "returns false if other is not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should == false - end - - it "returns false if other is equal as last element but not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should == false - end - - it "returns false if other is greater than last element but matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should == false - end - end - - describe "when excluded end value" do - before :each do - @range = RangeSpecs::TenfoldSucc.new(1)...RangeSpecs::TenfoldSucc.new(99) - end - - it "returns false if other is less than first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should == false - end - - it "returns true if other is equal as first element" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should == true - end - - it "returns true if other is matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should == true - end - - it "returns false if other is not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should == false - end - - it "returns false if other is equal as last element but not matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should == false - end - - it "returns false if other is greater than last element but matched by element.succ" do - @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should == false - end - end - end - - describe "with Time endpoints" do - it "uses cover? logic" do - now = Time.now - range = (now..(now + 60)) - - range.include?(now).should == true - range.include?(now - 1).should == false - range.include?(now + 60).should == true - range.include?(now + 61).should == false - end - end -end diff --git a/spec/ruby/core/rational/abs_spec.rb b/spec/ruby/core/rational/abs_spec.rb index 54099aa14d..6bb4a0fbef 100644 --- a/spec/ruby/core/rational/abs_spec.rb +++ b/spec/ruby/core/rational/abs_spec.rb @@ -1,6 +1,11 @@ require_relative "../../spec_helper" -require_relative 'shared/abs' describe "Rational#abs" do - it_behaves_like :rational_abs, :abs + it "returns self's absolute value" do + Rational(3, 4).abs.should == Rational(3, 4) + Rational(-3, 4).abs.should == Rational(3, 4) + Rational(3, -4).abs.should == Rational(3, 4) + + Rational(bignum_value, -bignum_value).abs.should == Rational(bignum_value, bignum_value) + end end diff --git a/spec/ruby/core/rational/magnitude_spec.rb b/spec/ruby/core/rational/magnitude_spec.rb index f5f667edb1..0df637df7a 100644 --- a/spec/ruby/core/rational/magnitude_spec.rb +++ b/spec/ruby/core/rational/magnitude_spec.rb @@ -1,6 +1,7 @@ require_relative "../../spec_helper" -require_relative 'shared/abs' -describe "Rational#abs" do - it_behaves_like :rational_abs, :magnitude +describe "Rational#magnitude" do + it "is an alias of Rational#abs" do + Rational.instance_method(:magnitude).should == Rational.instance_method(:abs) + end end diff --git a/spec/ruby/core/rational/quo_spec.rb b/spec/ruby/core/rational/quo_spec.rb index 907898ad34..62178f403b 100644 --- a/spec/ruby/core/rational/quo_spec.rb +++ b/spec/ruby/core/rational/quo_spec.rb @@ -1,25 +1,7 @@ require_relative "../../spec_helper" describe "Rational#quo" do - it "calls #coerce on the passed argument with self" do - rational = Rational(3, 4) - obj = mock("Object") - obj.should_receive(:coerce).with(rational).and_return([1, 2]) - - rational.quo(obj) - end - - it "calls #/ on the coerced Rational with the coerced Object" do - rational = Rational(3, 4) - - coerced_rational = mock("Coerced Rational") - coerced_rational.should_receive(:/).and_return(:result) - - coerced_obj = mock("Coerced Object") - - obj = mock("Object") - obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) - - rational.quo(obj).should == :result + it "is an alias of Rational#/" do + Rational.instance_method(:quo).should == Rational.instance_method(:/) end end diff --git a/spec/ruby/core/rational/shared/abs.rb b/spec/ruby/core/rational/shared/abs.rb deleted file mode 100644 index 3d64bcc1a0..0000000000 --- a/spec/ruby/core/rational/shared/abs.rb +++ /dev/null @@ -1,11 +0,0 @@ -require_relative '../../../spec_helper' - -describe :rational_abs, shared: true do - it "returns self's absolute value" do - Rational(3, 4).send(@method).should == Rational(3, 4) - Rational(-3, 4).send(@method).should == Rational(3, 4) - Rational(3, -4).send(@method).should == Rational(3, 4) - - Rational(bignum_value, -bignum_value).send(@method).should == Rational(bignum_value, bignum_value) - end -end diff --git a/spec/ruby/core/refinement/refined_class_spec.rb b/spec/ruby/core/refinement/refined_class_spec.rb index b532d9a773..90f8d963d8 100644 --- a/spec/ruby/core/refinement/refined_class_spec.rb +++ b/spec/ruby/core/refinement/refined_class_spec.rb @@ -1,5 +1,4 @@ require_relative "../../spec_helper" -require_relative 'shared/target' describe "Refinement#refined_class" do ruby_version_is ""..."3.4" do diff --git a/spec/ruby/core/refinement/shared/target.rb b/spec/ruby/core/refinement/shared/target.rb deleted file mode 100644 index 79557bea0b..0000000000 --- a/spec/ruby/core/refinement/shared/target.rb +++ /dev/null @@ -1,13 +0,0 @@ -describe :refinement_target, shared: true do - it "returns the class refined by the receiver" do - refinement_int = nil - - Module.new do - refine Integer do - refinement_int = self - end - end - - refinement_int.send(@method).should == Integer - end -end diff --git a/spec/ruby/core/refinement/target_spec.rb b/spec/ruby/core/refinement/target_spec.rb index 8bd816aea6..eaee71e8c6 100644 --- a/spec/ruby/core/refinement/target_spec.rb +++ b/spec/ruby/core/refinement/target_spec.rb @@ -1,6 +1,15 @@ require_relative "../../spec_helper" -require_relative 'shared/target' describe "Refinement#target" do - it_behaves_like :refinement_target, :target + it "returns the class refined by the receiver" do + refinement_int = nil + + Module.new do + refine Integer do + refinement_int = self + end + end + + refinement_int.target.should == Integer + end end diff --git a/spec/ruby/core/regexp/eql_spec.rb b/spec/ruby/core/regexp/eql_spec.rb index bd5ae43eb2..5924333fbd 100644 --- a/spec/ruby/core/regexp/eql_spec.rb +++ b/spec/ruby/core/regexp/eql_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/equal_value' describe "Regexp#eql?" do - it_behaves_like :regexp_eql, :eql? + it "is an alias of Regexp#==" do + Regexp.instance_method(:eql?).should == Regexp.instance_method(:==) + end end diff --git a/spec/ruby/core/regexp/equal_value_spec.rb b/spec/ruby/core/regexp/equal_value_spec.rb index 5455a30598..ad8dc33222 100644 --- a/spec/ruby/core/regexp/equal_value_spec.rb +++ b/spec/ruby/core/regexp/equal_value_spec.rb @@ -1,6 +1,33 @@ require_relative '../../spec_helper' -require_relative 'shared/equal_value' describe "Regexp#==" do - it_behaves_like :regexp_eql, :== + it "is true if self and other have the same pattern" do + (/abc/ == /abc/).should == true + (/abc/ == /abd/).should == false + end + + not_supported_on :opal do + it "is true if self and other have the same character set code" do + (/abc/ == /abc/x).should == false + (/abc/x == /abc/x).should == true + (/abc/u == /abc/n).should == false + (/abc/u == /abc/u).should == true + (/abc/n == /abc/n).should == true + end + end + + it "is true if other has the same #casefold? values" do + (/abc/ == /abc/i).should == false + (/abc/i == /abc/i).should == true + end + + not_supported_on :opal do + it "is true if self does not specify /n option and other does" do + (// == //n).should == true + end + + it "is true if self specifies /n option and other does not" do + (//n == //).should == true + end + end end diff --git a/spec/ruby/core/regexp/escape_spec.rb b/spec/ruby/core/regexp/escape_spec.rb index 6b06ab1cbc..99e5eb71d6 100644 --- a/spec/ruby/core/regexp/escape_spec.rb +++ b/spec/ruby/core/regexp/escape_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/quote' describe "Regexp.escape" do - it_behaves_like :regexp_quote, :escape + it "is an alias of Regexp.quote" do + Regexp.method(:escape).should == Regexp.method(:quote) + end end diff --git a/spec/ruby/core/regexp/quote_spec.rb b/spec/ruby/core/regexp/quote_spec.rb index 370ab13e30..27fa0c0669 100644 --- a/spec/ruby/core/regexp/quote_spec.rb +++ b/spec/ruby/core/regexp/quote_spec.rb @@ -1,6 +1,43 @@ +# encoding: binary + require_relative '../../spec_helper' -require_relative 'shared/quote' describe "Regexp.quote" do - it_behaves_like :regexp_quote, :quote + it "escapes any characters with special meaning in a regular expression" do + Regexp.quote('\*?{}.+^$[]()- ').should == '\\\\\*\?\{\}\.\+\^\$\[\]\(\)\-\\ ' + Regexp.quote("\*?{}.+^$[]()- ").should == '\\*\\?\\{\\}\\.\\+\\^\\$\\[\\]\\(\\)\\-\\ ' + Regexp.quote('\n\r\f\t').should == '\\\\n\\\\r\\\\f\\\\t' + Regexp.quote("\n\r\f\t").should == '\\n\\r\\f\\t' + end + + it "works with symbols" do + Regexp.quote(:symbol).should == 'symbol' + end + + it "works with substrings" do + str = ".+[]()"[1...-1] + Regexp.quote(str).should == '\+\[\]\(' + end + + it "works for broken strings" do + Regexp.quote("a.\x85b.".dup.force_encoding("US-ASCII")).should =="a\\.\x85b\\.".dup.force_encoding("US-ASCII") + Regexp.quote("a.\x80".dup.force_encoding("UTF-8")).should == "a\\.\x80".dup.force_encoding("UTF-8") + end + + it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do + str = "abc".dup.force_encoding("euc-jp") + Regexp.quote(str).encoding.should == Encoding::US_ASCII + end + + it "sets the encoding of the result to the encoding of the String if any non-US-ASCII characters are present in an input String with valid encoding" do + str = "ありがとう".dup.force_encoding("utf-8") + str.valid_encoding?.should == true + Regexp.quote(str).encoding.should == Encoding::UTF_8 + end + + it "sets the encoding of the result to BINARY if any non-US-ASCII characters are present in an input String with invalid encoding" do + str = "\xff".dup.force_encoding "us-ascii" + str.valid_encoding?.should == false + Regexp.quote("\xff").encoding.should == Encoding::BINARY + end end diff --git a/spec/ruby/core/regexp/shared/equal_value.rb b/spec/ruby/core/regexp/shared/equal_value.rb deleted file mode 100644 index 803988de9e..0000000000 --- a/spec/ruby/core/regexp/shared/equal_value.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe :regexp_eql, shared: true do - it "is true if self and other have the same pattern" do - /abc/.send(@method, /abc/).should == true - /abc/.send(@method, /abd/).should == false - end - - not_supported_on :opal do - it "is true if self and other have the same character set code" do - /abc/.send(@method, /abc/x).should == false - /abc/x.send(@method, /abc/x).should == true - /abc/u.send(@method, /abc/n).should == false - /abc/u.send(@method, /abc/u).should == true - /abc/n.send(@method, /abc/n).should == true - end - end - - it "is true if other has the same #casefold? values" do - /abc/.send(@method, /abc/i).should == false - /abc/i.send(@method, /abc/i).should == true - end - - not_supported_on :opal do - it "is true if self does not specify /n option and other does" do - //.send(@method, //n).should == true - end - - it "is true if self specifies /n option and other does not" do - //n.send(@method, //).should == true - end - end -end diff --git a/spec/ruby/core/regexp/shared/quote.rb b/spec/ruby/core/regexp/shared/quote.rb deleted file mode 100644 index 083f12d78c..0000000000 --- a/spec/ruby/core/regexp/shared/quote.rb +++ /dev/null @@ -1,41 +0,0 @@ -# encoding: binary - -describe :regexp_quote, shared: true do - it "escapes any characters with special meaning in a regular expression" do - Regexp.send(@method, '\*?{}.+^$[]()- ').should == '\\\\\*\?\{\}\.\+\^\$\[\]\(\)\-\\ ' - Regexp.send(@method, "\*?{}.+^$[]()- ").should == '\\*\\?\\{\\}\\.\\+\\^\\$\\[\\]\\(\\)\\-\\ ' - Regexp.send(@method, '\n\r\f\t').should == '\\\\n\\\\r\\\\f\\\\t' - Regexp.send(@method, "\n\r\f\t").should == '\\n\\r\\f\\t' - end - - it "works with symbols" do - Regexp.send(@method, :symbol).should == 'symbol' - end - - it "works with substrings" do - str = ".+[]()"[1...-1] - Regexp.send(@method, str).should == '\+\[\]\(' - end - - it "works for broken strings" do - Regexp.send(@method, "a.\x85b.".dup.force_encoding("US-ASCII")).should =="a\\.\x85b\\.".dup.force_encoding("US-ASCII") - Regexp.send(@method, "a.\x80".dup.force_encoding("UTF-8")).should == "a\\.\x80".dup.force_encoding("UTF-8") - end - - it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do - str = "abc".dup.force_encoding("euc-jp") - Regexp.send(@method, str).encoding.should == Encoding::US_ASCII - end - - it "sets the encoding of the result to the encoding of the String if any non-US-ASCII characters are present in an input String with valid encoding" do - str = "ありがとう".dup.force_encoding("utf-8") - str.valid_encoding?.should == true - Regexp.send(@method, str).encoding.should == Encoding::UTF_8 - end - - it "sets the encoding of the result to BINARY if any non-US-ASCII characters are present in an input String with invalid encoding" do - str = "\xff".dup.force_encoding "us-ascii" - str.valid_encoding?.should == false - Regexp.send(@method, "\xff").encoding.should == Encoding::BINARY - end -end diff --git a/spec/ruby/core/set/add_spec.rb b/spec/ruby/core/set/add_spec.rb index 1ce03b1eab..1a018b186a 100644 --- a/spec/ruby/core/set/add_spec.rb +++ b/spec/ruby/core/set/add_spec.rb @@ -1,8 +1,18 @@ require_relative '../../spec_helper' -require_relative 'shared/add' describe "Set#add" do - it_behaves_like :set_add, :add + before :each do + @set = Set.new + end + + it "adds the passed Object to self" do + @set.add("dog") + @set.should.include?("dog") + end + + it "returns self" do + @set.add("dog").should.equal?(@set) + end end describe "Set#add?" do diff --git a/spec/ruby/core/set/append_spec.rb b/spec/ruby/core/set/append_spec.rb index 82d34d9130..4f4e2351e2 100644 --- a/spec/ruby/core/set/append_spec.rb +++ b/spec/ruby/core/set/append_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/add' describe "Set#<<" do - it_behaves_like :set_add, :<< + it "is an alias of Set#add" do + Set.instance_method(:<<).should == Set.instance_method(:add) + end end diff --git a/spec/ruby/core/set/case_compare_spec.rb b/spec/ruby/core/set/case_compare_spec.rb index 3781b1b963..6fe749c79b 100644 --- a/spec/ruby/core/set/case_compare_spec.rb +++ b/spec/ruby/core/set/case_compare_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "Set#===" do - it_behaves_like :set_include, :=== - - it "is an alias for include?" do - set = Set.new - set.method(:===).should == set.method(:include?) + it "is an alias of Set#include?" do + Set.instance_method(:===).should == Set.instance_method(:include?) end end diff --git a/spec/ruby/core/set/case_equality_spec.rb b/spec/ruby/core/set/case_equality_spec.rb deleted file mode 100644 index 19c1fb6b9c..0000000000 --- a/spec/ruby/core/set/case_equality_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require_relative '../../spec_helper' -require_relative 'shared/include' - -describe "Set#===" do - it_behaves_like :set_include, :=== -end diff --git a/spec/ruby/core/set/collect_spec.rb b/spec/ruby/core/set/collect_spec.rb index d186f1a0d9..b78ee493d4 100644 --- a/spec/ruby/core/set/collect_spec.rb +++ b/spec/ruby/core/set/collect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/collect' describe "Set#collect!" do - it_behaves_like :set_collect_bang, :collect! + it "is an alias of Set#map!" do + Set.instance_method(:collect!).should == Set.instance_method(:map!) + end end diff --git a/spec/ruby/core/set/difference_spec.rb b/spec/ruby/core/set/difference_spec.rb index 149f946592..22d89973a8 100644 --- a/spec/ruby/core/set/difference_spec.rb +++ b/spec/ruby/core/set/difference_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/difference' describe "Set#difference" do - it_behaves_like :set_difference, :difference + it "is an alias of Set#-" do + Set.instance_method(:difference).should == Set.instance_method(:-) + end end diff --git a/spec/ruby/core/set/eql_spec.rb b/spec/ruby/core/set/eql_spec.rb index 6862ed4eda..e7eacf2999 100644 --- a/spec/ruby/core/set/eql_spec.rb +++ b/spec/ruby/core/set/eql_spec.rb @@ -1,14 +1,22 @@ require_relative '../../spec_helper' describe "Set#eql?" do - it "returns true when the passed argument is a Set and contains the same elements" do - Set[].should.eql?(Set[]) - Set[1, 2, 3].should.eql?(Set[1, 2, 3]) - Set[1, 2, 3].should.eql?(Set[3, 2, 1]) - Set["a", :b, ?c].should.eql?(Set[?c, :b, "a"]) + ruby_version_is ""..."4.0" do + it "returns true when the passed argument is a Set and contains the same elements" do + Set[].should.eql?(Set[]) + Set[1, 2, 3].should.eql?(Set[1, 2, 3]) + Set[1, 2, 3].should.eql?(Set[3, 2, 1]) + Set["a", :b, ?c].should.eql?(Set[?c, :b, "a"]) - Set[1, 2, 3].should_not.eql?(Set[1.0, 2, 3]) - Set[1, 2, 3].should_not.eql?(Set[2, 3]) - Set[1, 2, 3].should_not.eql?(Set[]) + Set[1, 2, 3].should_not.eql?(Set[1.0, 2, 3]) + Set[1, 2, 3].should_not.eql?(Set[2, 3]) + Set[1, 2, 3].should_not.eql?(Set[]) + end + end + + ruby_version_is "4.0" do + it "is an alias of Set#==" do + Set.instance_method(:eql?).should == Set.instance_method(:==) + end end end diff --git a/spec/ruby/core/set/filter_spec.rb b/spec/ruby/core/set/filter_spec.rb index 779254ad68..d0c294c27f 100644 --- a/spec/ruby/core/set/filter_spec.rb +++ b/spec/ruby/core/set/filter_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/select' describe "Set#filter!" do - it_behaves_like :set_select_bang, :filter! + it "is an alias of Set#select!" do + Set.instance_method(:filter!).should == Set.instance_method(:select!) + end end diff --git a/spec/ruby/core/set/gt_spec.rb b/spec/ruby/core/set/gt_spec.rb new file mode 100644 index 0000000000..8a7e421e40 --- /dev/null +++ b/spec/ruby/core/set/gt_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Set#>" do + it "is an alias of Set#proper_superset?" do + Set.instance_method(:>).should == Set.instance_method(:proper_superset?) + end +end diff --git a/spec/ruby/core/set/gte_spec.rb b/spec/ruby/core/set/gte_spec.rb new file mode 100644 index 0000000000..e98c3cb1e2 --- /dev/null +++ b/spec/ruby/core/set/gte_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Set#>=" do + it "is an alias of Set#superset?" do + Set.instance_method(:>=).should == Set.instance_method(:superset?) + end +end diff --git a/spec/ruby/core/set/include_spec.rb b/spec/ruby/core/set/include_spec.rb index dd33bbc3bd..92a6ca04e6 100644 --- a/spec/ruby/core/set/include_spec.rb +++ b/spec/ruby/core/set/include_spec.rb @@ -1,6 +1,31 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "Set#include?" do - it_behaves_like :set_include, :include? + it "returns true when self contains the passed Object" do + set = Set[:a, :b, :c] + set.include?(:a).should == true + set.include?(:e).should == false + end + + describe "member equality" do + it "is checked using both #hash and #eql?" do + obj = Object.new + obj_another = Object.new + + def obj.hash; 42 end + def obj_another.hash; 42 end + def obj_another.eql?(o) hash == o.hash end + + set = Set["a", "b", "c", obj] + set.include?(obj_another).should == true + end + + it "is not checked using #==" do + obj = Object.new + set = Set["a", "b", "c"] + + obj.should_not_receive(:==) + set.include?(obj) + end + end end diff --git a/spec/ruby/core/set/inspect_spec.rb b/spec/ruby/core/set/inspect_spec.rb index 0dcce83eb6..45aeed280e 100644 --- a/spec/ruby/core/set/inspect_spec.rb +++ b/spec/ruby/core/set/inspect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/inspect' describe "Set#inspect" do - it_behaves_like :set_inspect, :inspect + it "is an alias of Set#to_s" do + Set.instance_method(:inspect).should == Set.instance_method(:to_s) + end end diff --git a/spec/ruby/core/set/intersection_spec.rb b/spec/ruby/core/set/intersection_spec.rb index 136b886775..c14e1f62ad 100644 --- a/spec/ruby/core/set/intersection_spec.rb +++ b/spec/ruby/core/set/intersection_spec.rb @@ -1,10 +1,23 @@ require_relative '../../spec_helper' -require_relative 'shared/intersection' describe "Set#intersection" do - it_behaves_like :set_intersection, :intersection + it "is an alias of Set#&" do + Set.instance_method(:intersection).should == Set.instance_method(:&) + end end describe "Set#&" do - it_behaves_like :set_intersection, :& + before :each do + @set = Set[:a, :b, :c] + end + + it "returns a new Set containing only elements shared by self and the passed Enumerable" do + (@set & Set[:b, :c, :d, :e]).should == Set[:b, :c] + (@set & [:b, :c, :d]).should == Set[:b, :c] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + -> { @set & 1 }.should.raise(ArgumentError) + -> { @set & Object.new }.should.raise(ArgumentError) + end end diff --git a/spec/ruby/core/set/length_spec.rb b/spec/ruby/core/set/length_spec.rb index 6bb697b4ca..9b0d3622b8 100644 --- a/spec/ruby/core/set/length_spec.rb +++ b/spec/ruby/core/set/length_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "Set#length" do - it_behaves_like :set_length, :length + it "is an alias of Set#size" do + Set.instance_method(:length).should == Set.instance_method(:size) + end end diff --git a/spec/ruby/core/set/lt_spec.rb b/spec/ruby/core/set/lt_spec.rb new file mode 100644 index 0000000000..0f5bc9c642 --- /dev/null +++ b/spec/ruby/core/set/lt_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Set#<" do + it "is an alias of Set#proper_subset?" do + Set.instance_method(:<).should == Set.instance_method(:proper_subset?) + end +end diff --git a/spec/ruby/core/set/lte_spec.rb b/spec/ruby/core/set/lte_spec.rb new file mode 100644 index 0000000000..291d582240 --- /dev/null +++ b/spec/ruby/core/set/lte_spec.rb @@ -0,0 +1,7 @@ +require_relative '../../spec_helper' + +describe "Set#<=" do + it "is an alias of Set#subset?" do + Set.instance_method(:<=).should == Set.instance_method(:subset?) + end +end diff --git a/spec/ruby/core/set/map_spec.rb b/spec/ruby/core/set/map_spec.rb index 996191b0a8..fd04a8bde1 100644 --- a/spec/ruby/core/set/map_spec.rb +++ b/spec/ruby/core/set/map_spec.rb @@ -1,6 +1,22 @@ require_relative '../../spec_helper' -require_relative 'shared/collect' describe "Set#map!" do - it_behaves_like :set_collect_bang, :map! + before :each do + @set = Set[1, 2, 3, 4, 5] + end + + it "yields each Object in self" do + res = [] + @set.map! { |x| res << x } + res.sort.should == [1, 2, 3, 4, 5].sort + end + + it "returns self" do + @set.map! { |x| x }.should.equal?(@set) + end + + it "replaces self with the return values of the block" do + @set.map! { |x| x * 2 } + @set.should == Set[2, 4, 6, 8, 10] + end end diff --git a/spec/ruby/core/set/member_spec.rb b/spec/ruby/core/set/member_spec.rb index 5c82e8f826..a36308eec7 100644 --- a/spec/ruby/core/set/member_spec.rb +++ b/spec/ruby/core/set/member_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/include' describe "Set#member?" do - it_behaves_like :set_include, :member? + it "is an alias of Set#include?" do + Set.instance_method(:member?).should == Set.instance_method(:include?) + end end diff --git a/spec/ruby/core/set/minus_spec.rb b/spec/ruby/core/set/minus_spec.rb index 72f98f985e..8574708559 100644 --- a/spec/ruby/core/set/minus_spec.rb +++ b/spec/ruby/core/set/minus_spec.rb @@ -1,6 +1,17 @@ require_relative '../../spec_helper' -require_relative 'shared/difference' describe "Set#-" do - it_behaves_like :set_difference, :- + before :each do + @set = Set[:a, :b, :c] + end + + it "returns a new Set containing self's elements excluding the elements in the passed Enumerable" do + (@set - Set[:a, :b]).should == Set[:c] + (@set - [:b, :c]).should == Set[:a] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + -> { @set - 1 }.should.raise(ArgumentError) + -> { @set - Object.new }.should.raise(ArgumentError) + end end diff --git a/spec/ruby/core/set/plus_spec.rb b/spec/ruby/core/set/plus_spec.rb index 7e44ff0b7e..839f77fc39 100644 --- a/spec/ruby/core/set/plus_spec.rb +++ b/spec/ruby/core/set/plus_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/union' describe "Set#+" do - it_behaves_like :set_union, :+ + it "is an alias of Set#|" do + Set.instance_method(:+).should == Set.instance_method(:|) + end end diff --git a/spec/ruby/core/set/select_spec.rb b/spec/ruby/core/set/select_spec.rb index b458ffacaa..619194605b 100644 --- a/spec/ruby/core/set/select_spec.rb +++ b/spec/ruby/core/set/select_spec.rb @@ -1,6 +1,41 @@ require_relative '../../spec_helper' -require_relative 'shared/select' describe "Set#select!" do - it_behaves_like :set_select_bang, :select! + before :each do + @set = Set["one", "two", "three"] + end + + it "yields every element of self" do + ret = [] + @set.select! { |x| ret << x } + ret.sort.should == ["one", "two", "three"].sort + end + + it "keeps every element from self for which the passed block returns true" do + @set.select! { |x| x.size != 3 } + @set.size.should.eql?(1) + + @set.should_not.include?("one") + @set.should_not.include?("two") + @set.should.include?("three") + end + + it "returns self when self was modified" do + @set.select! { false }.should.equal?(@set) + end + + it "returns nil when self was not modified" do + @set.select! { true }.should == nil + end + + it "returns an Enumerator when passed no block" do + enum = @set.select! + enum.should.instance_of?(Enumerator) + + enum.each { |x| x.size != 3 } + + @set.should_not.include?("one") + @set.should_not.include?("two") + @set.should.include?("three") + end end diff --git a/spec/ruby/core/set/shared/add.rb b/spec/ruby/core/set/shared/add.rb deleted file mode 100644 index 8d6d83434f..0000000000 --- a/spec/ruby/core/set/shared/add.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe :set_add, shared: true do - before :each do - @set = Set.new - end - - it "adds the passed Object to self" do - @set.send(@method, "dog") - @set.should.include?("dog") - end - - it "returns self" do - @set.send(@method, "dog").should.equal?(@set) - end -end diff --git a/spec/ruby/core/set/shared/collect.rb b/spec/ruby/core/set/shared/collect.rb deleted file mode 100644 index ad5c5afa59..0000000000 --- a/spec/ruby/core/set/shared/collect.rb +++ /dev/null @@ -1,20 +0,0 @@ -describe :set_collect_bang, shared: true do - before :each do - @set = Set[1, 2, 3, 4, 5] - end - - it "yields each Object in self" do - res = [] - @set.send(@method) { |x| res << x } - res.sort.should == [1, 2, 3, 4, 5].sort - end - - it "returns self" do - @set.send(@method) { |x| x }.should.equal?(@set) - end - - it "replaces self with the return values of the block" do - @set.send(@method) { |x| x * 2 } - @set.should == Set[2, 4, 6, 8, 10] - end -end diff --git a/spec/ruby/core/set/shared/difference.rb b/spec/ruby/core/set/shared/difference.rb deleted file mode 100644 index 8a17056a82..0000000000 --- a/spec/ruby/core/set/shared/difference.rb +++ /dev/null @@ -1,15 +0,0 @@ -describe :set_difference, shared: true do - before :each do - @set = Set[:a, :b, :c] - end - - it "returns a new Set containing self's elements excluding the elements in the passed Enumerable" do - @set.send(@method, Set[:a, :b]).should == Set[:c] - @set.send(@method, [:b, :c]).should == Set[:a] - end - - it "raises an ArgumentError when passed a non-Enumerable" do - -> { @set.send(@method, 1) }.should.raise(ArgumentError) - -> { @set.send(@method, Object.new) }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/core/set/shared/include.rb b/spec/ruby/core/set/shared/include.rb deleted file mode 100644 index 82755ccf59..0000000000 --- a/spec/ruby/core/set/shared/include.rb +++ /dev/null @@ -1,29 +0,0 @@ -describe :set_include, shared: true do - it "returns true when self contains the passed Object" do - set = Set[:a, :b, :c] - set.send(@method, :a).should == true - set.send(@method, :e).should == false - end - - describe "member equality" do - it "is checked using both #hash and #eql?" do - obj = Object.new - obj_another = Object.new - - def obj.hash; 42 end - def obj_another.hash; 42 end - def obj_another.eql?(o) hash == o.hash end - - set = Set["a", "b", "c", obj] - set.send(@method, obj_another).should == true - end - - it "is not checked using #==" do - obj = Object.new - set = Set["a", "b", "c"] - - obj.should_not_receive(:==) - set.send(@method, obj) - end - end -end diff --git a/spec/ruby/core/set/shared/inspect.rb b/spec/ruby/core/set/shared/inspect.rb deleted file mode 100644 index 31bd8accfd..0000000000 --- a/spec/ruby/core/set/shared/inspect.rb +++ /dev/null @@ -1,45 +0,0 @@ -describe :set_inspect, shared: true do - it "returns a String representation of self" do - Set[].send(@method).should.is_a?(String) - Set[nil, false, true].send(@method).should.is_a?(String) - Set[1, 2, 3].send(@method).should.is_a?(String) - Set["1", "2", "3"].send(@method).should.is_a?(String) - Set[:a, "b", Set[?c]].send(@method).should.is_a?(String) - end - - ruby_version_is "4.0" do - it "does include the elements of the set" do - Set["1"].send(@method).should == 'Set["1"]' - end - end - - ruby_version_is ""..."4.0" do - it "does include the elements of the set" do - Set["1"].send(@method).should == '#<Set: {"1"}>' - end - end - - it "puts spaces between the elements" do - Set["1", "2"].send(@method).should.include?('", "') - end - - ruby_version_is "4.0" do - it "correctly handles cyclic-references" do - set1 = Set[] - set2 = Set[set1] - set1 << set2 - set1.send(@method).should.is_a?(String) - set1.send(@method).should.include?("Set[...]") - end - end - - ruby_version_is ""..."4.0" do - it "correctly handles cyclic-references" do - set1 = Set[] - set2 = Set[set1] - set1 << set2 - set1.send(@method).should.is_a?(String) - set1.send(@method).should.include?("#<Set: {...}>") - end - end -end diff --git a/spec/ruby/core/set/shared/intersection.rb b/spec/ruby/core/set/shared/intersection.rb deleted file mode 100644 index 978a4924ef..0000000000 --- a/spec/ruby/core/set/shared/intersection.rb +++ /dev/null @@ -1,15 +0,0 @@ -describe :set_intersection, shared: true do - before :each do - @set = Set[:a, :b, :c] - end - - it "returns a new Set containing only elements shared by self and the passed Enumerable" do - @set.send(@method, Set[:b, :c, :d, :e]).should == Set[:b, :c] - @set.send(@method, [:b, :c, :d]).should == Set[:b, :c] - end - - it "raises an ArgumentError when passed a non-Enumerable" do - -> { @set.send(@method, 1) }.should.raise(ArgumentError) - -> { @set.send(@method, Object.new) }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/core/set/shared/length.rb b/spec/ruby/core/set/shared/length.rb deleted file mode 100644 index a8fcee9f39..0000000000 --- a/spec/ruby/core/set/shared/length.rb +++ /dev/null @@ -1,6 +0,0 @@ -describe :set_length, shared: true do - it "returns the number of elements in the set" do - set = Set[:a, :b, :c] - set.send(@method).should == 3 - end -end diff --git a/spec/ruby/core/set/shared/select.rb b/spec/ruby/core/set/shared/select.rb deleted file mode 100644 index 0d4a53fffd..0000000000 --- a/spec/ruby/core/set/shared/select.rb +++ /dev/null @@ -1,41 +0,0 @@ -require_relative '../../../spec_helper' - -describe :set_select_bang, shared: true do - before :each do - @set = Set["one", "two", "three"] - end - - it "yields every element of self" do - ret = [] - @set.send(@method) { |x| ret << x } - ret.sort.should == ["one", "two", "three"].sort - end - - it "keeps every element from self for which the passed block returns true" do - @set.send(@method) { |x| x.size != 3 } - @set.size.should.eql?(1) - - @set.should_not.include?("one") - @set.should_not.include?("two") - @set.should.include?("three") - end - - it "returns self when self was modified" do - @set.send(@method) { false }.should.equal?(@set) - end - - it "returns nil when self was not modified" do - @set.send(@method) { true }.should == nil - end - - it "returns an Enumerator when passed no block" do - enum = @set.send(@method) - enum.should.instance_of?(Enumerator) - - enum.each { |x| x.size != 3 } - - @set.should_not.include?("one") - @set.should_not.include?("two") - @set.should.include?("three") - end -end diff --git a/spec/ruby/core/set/shared/union.rb b/spec/ruby/core/set/shared/union.rb deleted file mode 100644 index dddf1716e5..0000000000 --- a/spec/ruby/core/set/shared/union.rb +++ /dev/null @@ -1,15 +0,0 @@ -describe :set_union, shared: true do - before :each do - @set = Set[:a, :b, :c] - end - - it "returns a new Set containing all elements of self and the passed Enumerable" do - @set.send(@method, Set[:b, :d, :e]).should == Set[:a, :b, :c, :d, :e] - @set.send(@method, [:b, :e]).should == Set[:a, :b, :c, :e] - end - - it "raises an ArgumentError when passed a non-Enumerable" do - -> { @set.send(@method, 1) }.should.raise(ArgumentError) - -> { @set.send(@method, Object.new) }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/core/set/size_spec.rb b/spec/ruby/core/set/size_spec.rb index 4ae22c5f0a..c57272a235 100644 --- a/spec/ruby/core/set/size_spec.rb +++ b/spec/ruby/core/set/size_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "Set#size" do - it_behaves_like :set_length, :size + it "returns the number of elements in the set" do + set = Set[:a, :b, :c] + set.size.should == 3 + end end diff --git a/spec/ruby/core/set/to_s_spec.rb b/spec/ruby/core/set/to_s_spec.rb index 55b8bfd9b2..7f768bdcbf 100644 --- a/spec/ruby/core/set/to_s_spec.rb +++ b/spec/ruby/core/set/to_s_spec.rb @@ -1,11 +1,47 @@ require_relative "../../spec_helper" -require_relative 'shared/inspect' describe "Set#to_s" do - it_behaves_like :set_inspect, :to_s + it "returns a String representation of self" do + Set[].to_s.should.is_a?(String) + Set[nil, false, true].to_s.should.is_a?(String) + Set[1, 2, 3].to_s.should.is_a?(String) + Set["1", "2", "3"].to_s.should.is_a?(String) + Set[:a, "b", Set[?c]].to_s.should.is_a?(String) + end + + ruby_version_is "4.0" do + it "does include the elements of the set" do + Set["1"].to_s.should == 'Set["1"]' + end + end + + ruby_version_is ""..."4.0" do + it "does include the elements of the set" do + Set["1"].to_s.should == '#<Set: {"1"}>' + end + end + + it "puts spaces between the elements" do + Set["1", "2"].to_s.should.include?('", "') + end + + ruby_version_is "4.0" do + it "correctly handles cyclic-references" do + set1 = Set[] + set2 = Set[set1] + set1 << set2 + set1.to_s.should.is_a?(String) + set1.to_s.should.include?("Set[...]") + end + end - it "is an alias of inspect" do - set = Set.new - set.method(:to_s).should == set.method(:inspect) + ruby_version_is ""..."4.0" do + it "correctly handles cyclic-references" do + set1 = Set[] + set2 = Set[set1] + set1 << set2 + set1.to_s.should.is_a?(String) + set1.to_s.should.include?("#<Set: {...}>") + end end end diff --git a/spec/ruby/core/set/union_spec.rb b/spec/ruby/core/set/union_spec.rb index 3e77022d4b..206535aae2 100644 --- a/spec/ruby/core/set/union_spec.rb +++ b/spec/ruby/core/set/union_spec.rb @@ -1,10 +1,23 @@ require_relative '../../spec_helper' -require_relative 'shared/union' describe "Set#union" do - it_behaves_like :set_union, :union + it "is an alias of Set#|" do + Set.instance_method(:union).should == Set.instance_method(:|) + end end describe "Set#|" do - it_behaves_like :set_union, :| + before :each do + @set = Set[:a, :b, :c] + end + + it "returns a new Set containing all elements of self and the passed Enumerable" do + (@set | Set[:b, :d, :e]).should == Set[:a, :b, :c, :d, :e] + (@set | [:b, :e]).should == Set[:a, :b, :c, :e] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + -> { @set | 1 }.should.raise(ArgumentError) + -> { @set | Object.new }.should.raise(ArgumentError) + end end diff --git a/spec/ruby/core/sizedqueue/deq_spec.rb b/spec/ruby/core/sizedqueue/deq_spec.rb index 2aeb52f8a6..51ff706557 100644 --- a/spec/ruby/core/sizedqueue/deq_spec.rb +++ b/spec/ruby/core/sizedqueue/deq_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/deque' -require_relative '../../shared/types/rb_num2dbl_fails' describe "SizedQueue#deq" do - it_behaves_like :queue_deq, :deq, -> { SizedQueue.new(10) } -end - -describe "SizedQueue operations with timeout" do - it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(10); q.push(1); q.deq(timeout: v) } + it "is an alias of SizedQueue#pop" do + SizedQueue.instance_method(:deq).should == SizedQueue.instance_method(:pop) + end end diff --git a/spec/ruby/core/sizedqueue/enq_spec.rb b/spec/ruby/core/sizedqueue/enq_spec.rb index b955909475..94697cd247 100644 --- a/spec/ruby/core/sizedqueue/enq_spec.rb +++ b/spec/ruby/core/sizedqueue/enq_spec.rb @@ -1,16 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/enque' -require_relative '../../shared/sizedqueue/enque' -require_relative '../../shared/types/rb_num2dbl_fails' describe "SizedQueue#enq" do - it_behaves_like :queue_enq, :enq, -> { SizedQueue.new(10) } -end - -describe "SizedQueue#enq" do - it_behaves_like :sizedqueue_enq, :enq, -> n { SizedQueue.new(n) } -end - -describe "SizedQueue operations with timeout" do - it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(1); q.enq(1, timeout: v) } + it "is an alias of SizedQueue#<<" do + SizedQueue.instance_method(:enq).should == SizedQueue.instance_method(:<<) + end end diff --git a/spec/ruby/core/sizedqueue/length_spec.rb b/spec/ruby/core/sizedqueue/length_spec.rb index b93e7f8997..b9d16d8932 100644 --- a/spec/ruby/core/sizedqueue/length_spec.rb +++ b/spec/ruby/core/sizedqueue/length_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/length' describe "SizedQueue#length" do - it_behaves_like :queue_length, :length, -> { SizedQueue.new(10) } + it "is an alias of SizedQueue#size" do + SizedQueue.instance_method(:length).should == SizedQueue.instance_method(:size) + end end diff --git a/spec/ruby/core/sizedqueue/push_spec.rb b/spec/ruby/core/sizedqueue/push_spec.rb index 9eaa6beca0..943d0fcfb4 100644 --- a/spec/ruby/core/sizedqueue/push_spec.rb +++ b/spec/ruby/core/sizedqueue/push_spec.rb @@ -1,16 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/enque' -require_relative '../../shared/sizedqueue/enque' -require_relative '../../shared/types/rb_num2dbl_fails' describe "SizedQueue#push" do - it_behaves_like :queue_enq, :push, -> { SizedQueue.new(10) } -end - -describe "SizedQueue#push" do - it_behaves_like :sizedqueue_enq, :push, -> n { SizedQueue.new(n) } -end - -describe "SizedQueue operations with timeout" do - it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(1); q.push(1, timeout: v) } + it "is an alias of SizedQueue#<<" do + SizedQueue.instance_method(:push).should == SizedQueue.instance_method(:<<) + end end diff --git a/spec/ruby/core/sizedqueue/shift_spec.rb b/spec/ruby/core/sizedqueue/shift_spec.rb index 52974c1d99..f410f3f80d 100644 --- a/spec/ruby/core/sizedqueue/shift_spec.rb +++ b/spec/ruby/core/sizedqueue/shift_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative '../../shared/queue/deque' -require_relative '../../shared/types/rb_num2dbl_fails' describe "SizedQueue#shift" do - it_behaves_like :queue_deq, :shift, -> { SizedQueue.new(10) } -end - -describe "SizedQueue operations with timeout" do - it_behaves_like :rb_num2dbl_fails, nil, -> v { q = SizedQueue.new(10); q.push(1); q.shift(timeout: v) } + it "is an alias of SizedQueue#pop" do + SizedQueue.instance_method(:shift).should == SizedQueue.instance_method(:pop) + end end diff --git a/spec/ruby/core/string/case_compare_spec.rb b/spec/ruby/core/string/case_compare_spec.rb index b83d1adb91..f98ec003be 100644 --- a/spec/ruby/core/string/case_compare_spec.rb +++ b/spec/ruby/core/string/case_compare_spec.rb @@ -1,8 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' -require_relative 'shared/equal_value' describe "String#===" do - it_behaves_like :string_eql_value, :=== - it_behaves_like :string_equal_value, :=== + it "is an alias of String#==" do + String.instance_method(:===).should == String.instance_method(:==) + end end diff --git a/spec/ruby/core/string/codepoints_spec.rb b/spec/ruby/core/string/codepoints_spec.rb index 51bd57d127..4434eb66a5 100644 --- a/spec/ruby/core/string/codepoints_spec.rb +++ b/spec/ruby/core/string/codepoints_spec.rb @@ -1,7 +1,6 @@ # encoding: binary require_relative '../../spec_helper' require_relative 'shared/codepoints' -require_relative 'shared/each_codepoint_without_block' describe "String#codepoints" do it_behaves_like :string_codepoints, :codepoints diff --git a/spec/ruby/core/string/dedup_spec.rb b/spec/ruby/core/string/dedup_spec.rb index 2b31d80708..940f07668e 100644 --- a/spec/ruby/core/string/dedup_spec.rb +++ b/spec/ruby/core/string/dedup_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/dedup' describe 'String#dedup' do - it_behaves_like :string_dedup, :dedup + it "is an alias of String#-@" do + String.instance_method(:dedup).should == String.instance_method(:-@) + end end diff --git a/spec/ruby/core/string/each_codepoint_spec.rb b/spec/ruby/core/string/each_codepoint_spec.rb index c11cb1beae..08d4292371 100644 --- a/spec/ruby/core/string/each_codepoint_spec.rb +++ b/spec/ruby/core/string/each_codepoint_spec.rb @@ -1,8 +1,38 @@ +# encoding: binary require_relative '../../spec_helper' require_relative 'shared/codepoints' -require_relative 'shared/each_codepoint_without_block' describe "String#each_codepoint" do it_behaves_like :string_codepoints, :each_codepoint - it_behaves_like :string_each_codepoint_without_block, :each_codepoint + + describe "when no block is given" do + it "returns an Enumerator" do + "".each_codepoint.should.instance_of?(Enumerator) + end + + it "returns an Enumerator even when self has an invalid encoding" do + s = "\xDF".dup.force_encoding(Encoding::UTF_8) + s.valid_encoding?.should == false + s.each_codepoint.should.instance_of?(Enumerator) + end + + describe "returned Enumerator" do + describe "size" do + it "should return the size of the string" do + str = "hello" + str.each_codepoint.size.should == str.size + str = "ola" + str.each_codepoint.size.should == str.size + str = "\303\207\342\210\202\303\251\306\222g" + str.each_codepoint.size.should == str.size + end + + it "should return the size of the string even when the string has an invalid encoding" do + s = "\xDF".dup.force_encoding(Encoding::UTF_8) + s.valid_encoding?.should == false + s.each_codepoint.size.should == 1 + end + end + end + end end diff --git a/spec/ruby/core/string/equal_value_spec.rb b/spec/ruby/core/string/equal_value_spec.rb index b9c9c372f8..e8b706c524 100644 --- a/spec/ruby/core/string/equal_value_spec.rb +++ b/spec/ruby/core/string/equal_value_spec.rb @@ -1,8 +1,30 @@ require_relative '../../spec_helper' require_relative 'shared/eql' -require_relative 'shared/equal_value' describe "String#==" do it_behaves_like :string_eql_value, :== - it_behaves_like :string_equal_value, :== + + it "returns false if obj does not respond to to_str" do + ('hello' == 5).should == false + not_supported_on :opal do + ('hello' == :hello).should == false + end + ('hello' == mock('x')).should == false + end + + it "returns obj == self if obj responds to to_str" do + obj = Object.new + + # String#== merely checks if #to_str is defined. It does + # not call it. + obj.stub!(:to_str) + + obj.should_receive(:==).and_return(true) + + ('hello' == obj).should == true + end + + it "is not fooled by NUL characters" do + ("abc\0def" == "abc\0xyz").should == false + end end diff --git a/spec/ruby/core/string/intern_spec.rb b/spec/ruby/core/string/intern_spec.rb index cd7dad4359..af85e56dba 100644 --- a/spec/ruby/core/string/intern_spec.rb +++ b/spec/ruby/core/string/intern_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/to_sym' describe "String#intern" do - it_behaves_like :string_to_sym, :intern + it "is an alias of String#to_sym" do + String.instance_method(:intern).should == String.instance_method(:to_sym) + end end diff --git a/spec/ruby/core/string/length_spec.rb b/spec/ruby/core/string/length_spec.rb index 98cee1f03d..a723babdbc 100644 --- a/spec/ruby/core/string/length_spec.rb +++ b/spec/ruby/core/string/length_spec.rb @@ -1,7 +1,56 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/length' describe "String#length" do - it_behaves_like :string_length, :length + it "returns the length of self" do + "".length.should == 0 + "\x00".length.should == 1 + "one".length.should == 3 + "two".length.should == 3 + "three".length.should == 5 + "four".length.should == 4 + end + + it "returns the length of a string in different encodings" do + utf8_str = 'こにちわ' * 100 + utf8_str.length.should == 400 + utf8_str.encode(Encoding::UTF_32BE).length.should == 400 + utf8_str.encode(Encoding::SHIFT_JIS).length.should == 400 + end + + it "returns the length of the new self after encoding is changed" do + str = +'こにちわ' + str.length + + str.force_encoding('BINARY').length.should == 12 + end + + it "returns the correct length after force_encoding(BINARY)" do + utf8 = "あ" + ascii = "a" + concat = utf8 + ascii + + concat.encoding.should == Encoding::UTF_8 + concat.bytesize.should == 4 + + concat.length.should == 2 + concat.force_encoding(Encoding::ASCII_8BIT) + concat.length.should == 4 + end + + it "adds 1 for every invalid byte in UTF-8" do + "\xF4\x90\x80\x80".length.should == 4 + "a\xF4\x90\x80\x80b".length.should == 6 + "é\xF4\x90\x80\x80è".length.should == 6 + end + + it "adds 1 (and not 2) for a incomplete surrogate in UTF-16" do + "\x00\xd8".dup.force_encoding("UTF-16LE").length.should == 1 + "\xd8\x00".dup.force_encoding("UTF-16BE").length.should == 1 + end + + it "adds 1 for a broken sequence in UTF-32" do + "\x04\x03\x02\x01".dup.force_encoding("UTF-32LE").length.should == 1 + "\x01\x02\x03\x04".dup.force_encoding("UTF-32BE").length.should == 1 + end end diff --git a/spec/ruby/core/string/next_spec.rb b/spec/ruby/core/string/next_spec.rb index fcd3e5ef90..2ab121a909 100644 --- a/spec/ruby/core/string/next_spec.rb +++ b/spec/ruby/core/string/next_spec.rb @@ -1,11 +1,13 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/succ' describe "String#next" do - it_behaves_like :string_succ, :next + it "is an alias of String#succ" do + String.instance_method(:next).should == String.instance_method(:succ) + end end describe "String#next!" do - it_behaves_like :string_succ_bang, :"next!" + it "is an alias of String#succ!" do + String.instance_method(:next!).should == String.instance_method(:succ!) + end end diff --git a/spec/ruby/core/string/shared/dedup.rb b/spec/ruby/core/string/shared/dedup.rb deleted file mode 100644 index 59506c2901..0000000000 --- a/spec/ruby/core/string/shared/dedup.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: false -describe :string_dedup, shared: true do - it 'returns self if the String is frozen' do - input = 'foo'.freeze - output = input.send(@method) - - output.should.equal?(input) - output.should.frozen? - end - - it 'returns a frozen copy if the String is not frozen' do - input = 'foo' - output = input.send(@method) - - output.should.frozen? - output.should_not.equal?(input) - output.should == 'foo' - end - - it "returns the same object for equal unfrozen strings" do - origin = "this is a string" - dynamic = %w(this is a string).join(' ') - - origin.should_not.equal?(dynamic) - origin.send(@method).should.equal?(dynamic.send(@method)) - end - - it "returns the same object when it's called on the same String literal" do - "unfrozen string".send(@method).should.equal?("unfrozen string".send(@method)) - "unfrozen string".send(@method).should_not.equal?("another unfrozen string".send(@method)) - end - - it "deduplicates frozen strings" do - dynamic = %w(this string is frozen).join(' ').freeze - - dynamic.should_not.equal?("this string is frozen".freeze) - - dynamic.send(@method).should.equal?("this string is frozen".freeze) - dynamic.send(@method).should.equal?("this string is frozen".send(@method).freeze) - end - - it "does not deduplicate a frozen string when it has instance variables" do - dynamic = %w(this string is frozen).join(' ') - dynamic.instance_variable_set(:@a, 1) - dynamic.freeze - - dynamic.send(@method).should_not.equal?("this string is frozen".freeze) - dynamic.send(@method).should_not.equal?("this string is frozen".send(@method).freeze) - dynamic.send(@method).should.equal?(dynamic) - end -end diff --git a/spec/ruby/core/string/shared/each_codepoint_without_block.rb b/spec/ruby/core/string/shared/each_codepoint_without_block.rb deleted file mode 100644 index 60d603954c..0000000000 --- a/spec/ruby/core/string/shared/each_codepoint_without_block.rb +++ /dev/null @@ -1,33 +0,0 @@ -# encoding: binary -describe :string_each_codepoint_without_block, shared: true do - describe "when no block is given" do - it "returns an Enumerator" do - "".send(@method).should.instance_of?(Enumerator) - end - - it "returns an Enumerator even when self has an invalid encoding" do - s = "\xDF".dup.force_encoding(Encoding::UTF_8) - s.valid_encoding?.should == false - s.send(@method).should.instance_of?(Enumerator) - end - - describe "returned Enumerator" do - describe "size" do - it "should return the size of the string" do - str = "hello" - str.send(@method).size.should == str.size - str = "ola" - str.send(@method).size.should == str.size - str = "\303\207\342\210\202\303\251\306\222g" - str.send(@method).size.should == str.size - end - - it "should return the size of the string even when the string has an invalid encoding" do - s = "\xDF".dup.force_encoding(Encoding::UTF_8) - s.valid_encoding?.should == false - s.send(@method).size.should == 1 - end - end - end - end -end diff --git a/spec/ruby/core/string/shared/equal_value.rb b/spec/ruby/core/string/shared/equal_value.rb deleted file mode 100644 index dfc5c7cd29..0000000000 --- a/spec/ruby/core/string/shared/equal_value.rb +++ /dev/null @@ -1,29 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :string_equal_value, shared: true do - it "returns false if obj does not respond to to_str" do - 'hello'.send(@method, 5).should == false - not_supported_on :opal do - 'hello'.send(@method, :hello).should == false - end - 'hello'.send(@method, mock('x')).should == false - end - - it "returns obj == self if obj responds to to_str" do - obj = Object.new - - # String#== merely checks if #to_str is defined. It does - # not call it. - obj.stub!(:to_str) - - # Don't use @method for :== in `obj.should_receive(:==)` - obj.should_receive(:==).and_return(true) - - 'hello'.send(@method, obj).should == true - end - - it "is not fooled by NUL characters" do - "abc\0def".send(@method, "abc\0xyz").should == false - end -end diff --git a/spec/ruby/core/string/shared/length.rb b/spec/ruby/core/string/shared/length.rb deleted file mode 100644 index ae572ba755..0000000000 --- a/spec/ruby/core/string/shared/length.rb +++ /dev/null @@ -1,55 +0,0 @@ -# encoding: utf-8 - -describe :string_length, shared: true do - it "returns the length of self" do - "".send(@method).should == 0 - "\x00".send(@method).should == 1 - "one".send(@method).should == 3 - "two".send(@method).should == 3 - "three".send(@method).should == 5 - "four".send(@method).should == 4 - end - - it "returns the length of a string in different encodings" do - utf8_str = 'こにちわ' * 100 - utf8_str.send(@method).should == 400 - utf8_str.encode(Encoding::UTF_32BE).send(@method).should == 400 - utf8_str.encode(Encoding::SHIFT_JIS).send(@method).should == 400 - end - - it "returns the length of the new self after encoding is changed" do - str = +'こにちわ' - str.send(@method) - - str.force_encoding('BINARY').send(@method).should == 12 - end - - it "returns the correct length after force_encoding(BINARY)" do - utf8 = "あ" - ascii = "a" - concat = utf8 + ascii - - concat.encoding.should == Encoding::UTF_8 - concat.bytesize.should == 4 - - concat.send(@method).should == 2 - concat.force_encoding(Encoding::ASCII_8BIT) - concat.send(@method).should == 4 - end - - it "adds 1 for every invalid byte in UTF-8" do - "\xF4\x90\x80\x80".send(@method).should == 4 - "a\xF4\x90\x80\x80b".send(@method).should == 6 - "é\xF4\x90\x80\x80è".send(@method).should == 6 - end - - it "adds 1 (and not 2) for a incomplete surrogate in UTF-16" do - "\x00\xd8".dup.force_encoding("UTF-16LE").send(@method).should == 1 - "\xd8\x00".dup.force_encoding("UTF-16BE").send(@method).should == 1 - end - - it "adds 1 for a broken sequence in UTF-32" do - "\x04\x03\x02\x01".dup.force_encoding("UTF-32LE").send(@method).should == 1 - "\x01\x02\x03\x04".dup.force_encoding("UTF-32BE").send(@method).should == 1 - end -end diff --git a/spec/ruby/core/string/shared/succ.rb b/spec/ruby/core/string/shared/succ.rb deleted file mode 100644 index 8f1d327741..0000000000 --- a/spec/ruby/core/string/shared/succ.rb +++ /dev/null @@ -1,87 +0,0 @@ -# encoding: binary -describe :string_succ, shared: true do - it "returns an empty string for empty strings" do - "".send(@method).should == "" - end - - it "returns the successor by increasing the rightmost alphanumeric (digit => digit, letter => letter with same case)" do - "abcd".send(@method).should == "abce" - "THX1138".send(@method).should == "THX1139" - - "<<koala>>".send(@method).should == "<<koalb>>" - "==A??".send(@method).should == "==B??" - end - - it "increases non-alphanumerics (via ascii rules) if there are no alphanumerics" do - "***".send(@method).should == "**+" - "**`".send(@method).should == "**a" - end - - it "increases the next best alphanumeric (jumping over non-alphanumerics) if there is a carry" do - "dz".send(@method).should == "ea" - "HZ".send(@method).should == "IA" - "49".send(@method).should == "50" - - "izz".send(@method).should == "jaa" - "IZZ".send(@method).should == "JAA" - "699".send(@method).should == "700" - - "6Z99z99Z".send(@method).should == "7A00a00A" - - "1999zzz".send(@method).should == "2000aaa" - "NZ/[]ZZZ9999".send(@method).should == "OA/[]AAA0000" - end - - it "increases the next best character if there is a carry for non-alphanumerics" do - "(\xFF".send(@method).should == ")\x00" - "`\xFF".send(@method).should == "a\x00" - "<\xFF\xFF".send(@method).should == "=\x00\x00" - end - - it "adds an additional character (just left to the last increased one) if there is a carry and no character left to increase" do - "z".send(@method).should == "aa" - "Z".send(@method).should == "AA" - "9".send(@method).should == "10" - - "zz".send(@method).should == "aaa" - "ZZ".send(@method).should == "AAA" - "99".send(@method).should == "100" - - "9Z99z99Z".send(@method).should == "10A00a00A" - - "ZZZ9999".send(@method).should == "AAAA0000" - "/[]9999".send(@method).should == "/[]10000" - "/[]ZZZ9999".send(@method).should == "/[]AAAA0000" - "Z/[]ZZZ9999".send(@method).should == "AA/[]AAA0000" - - # non-alphanumeric cases - "\xFF".send(@method).should == "\x01\x00" - "\xFF\xFF".send(@method).should == "\x01\x00\x00" - end - - it "returns String instances when called on a subclass" do - StringSpecs::MyString.new("").send(@method).should.instance_of?(String) - StringSpecs::MyString.new("a").send(@method).should.instance_of?(String) - StringSpecs::MyString.new("z").send(@method).should.instance_of?(String) - end - - it "returns a String in the same encoding as self" do - "z".encode("US-ASCII").send(@method).encoding.should == Encoding::US_ASCII - end -end - -describe :string_succ_bang, shared: true do - it "is equivalent to succ, but modifies self in place (still returns self)" do - ["", "abcd", "THX1138"].each do |s| - s = +s - r = s.dup.send(@method) - s.send(@method).should.equal?(s) - s.should == r - end - end - - it "raises a FrozenError if self is frozen" do - -> { "".freeze.send(@method) }.should.raise(FrozenError) - -> { "abcd".freeze.send(@method) }.should.raise(FrozenError) - end -end diff --git a/spec/ruby/core/string/shared/to_s.rb b/spec/ruby/core/string/shared/to_s.rb deleted file mode 100644 index 96c59470d6..0000000000 --- a/spec/ruby/core/string/shared/to_s.rb +++ /dev/null @@ -1,13 +0,0 @@ -describe :string_to_s, shared: true do - it "returns self when self.class == String" do - a = "a string" - a.should.equal?(a.send(@method)) - end - - it "returns a new instance of String when called on a subclass" do - a = StringSpecs::MyString.new("a string") - s = a.send(@method) - s.should == "a string" - s.should.instance_of?(String) - end -end diff --git a/spec/ruby/core/string/shared/to_sym.rb b/spec/ruby/core/string/shared/to_sym.rb deleted file mode 100644 index 2a8a2e3182..0000000000 --- a/spec/ruby/core/string/shared/to_sym.rb +++ /dev/null @@ -1,72 +0,0 @@ -describe :string_to_sym, shared: true do - it "returns the symbol corresponding to self" do - "Koala".send(@method).should.equal? :Koala - 'cat'.send(@method).should.equal? :cat - '@cat'.send(@method).should.equal? :@cat - 'cat and dog'.send(@method).should.equal? :"cat and dog" - "abc=".send(@method).should.equal? :abc= - end - - it "does not special case +(binary) and -(binary)" do - "+(binary)".send(@method).should.equal? :"+(binary)" - "-(binary)".send(@method).should.equal? :"-(binary)" - end - - it "does not special case certain operators" do - "!@".send(@method).should.equal? :"!@" - "~@".send(@method).should.equal? :"~@" - "!(unary)".send(@method).should.equal? :"!(unary)" - "~(unary)".send(@method).should.equal? :"~(unary)" - "+(unary)".send(@method).should.equal? :"+(unary)" - "-(unary)".send(@method).should.equal? :"-(unary)" - end - - it "returns a US-ASCII Symbol for a UTF-8 String containing only US-ASCII characters" do - sym = "foobar".send(@method) - sym.encoding.should == Encoding::US_ASCII - sym.should.equal? :"foobar" - end - - it "returns a US-ASCII Symbol for a binary String containing only US-ASCII characters" do - sym = "foobar".b.send(@method) - sym.encoding.should == Encoding::US_ASCII - sym.should.equal? :"foobar" - end - - it "returns a UTF-8 Symbol for a UTF-8 String containing non US-ASCII characters" do - sym = "il était une fois".send(@method) - sym.encoding.should == Encoding::UTF_8 - sym.should.equal? :"il était une #{'fois'}" - end - - it "returns a UTF-16LE Symbol for a UTF-16LE String containing non US-ASCII characters" do - utf16_str = "UtéF16".encode(Encoding::UTF_16LE) - sym = utf16_str.send(@method) - sym.encoding.should == Encoding::UTF_16LE - sym.to_s.should == utf16_str - end - - it "returns a binary Symbol for a binary String containing non US-ASCII characters" do - binary_string = "binarí".b - sym = binary_string.send(@method) - sym.encoding.should == Encoding::BINARY - sym.to_s.should == binary_string - end - - it "ignores existing symbols with different encoding" do - source = "fée" - - iso_symbol = source.dup.force_encoding(Encoding::ISO_8859_1).send(@method) - iso_symbol.encoding.should == Encoding::ISO_8859_1 - binary_symbol = source.dup.force_encoding(Encoding::BINARY).send(@method) - binary_symbol.encoding.should == Encoding::BINARY - end - - it "raises an EncodingError for UTF-8 String containing invalid bytes" do - invalid_utf8 = "\xC3" - invalid_utf8.should_not.valid_encoding? - -> { - invalid_utf8.send(@method) - }.should.raise(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"') - end -end diff --git a/spec/ruby/core/string/size_spec.rb b/spec/ruby/core/string/size_spec.rb index 9e1f40c5ae..6fc81480c4 100644 --- a/spec/ruby/core/string/size_spec.rb +++ b/spec/ruby/core/string/size_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/length' describe "String#size" do - it_behaves_like :string_length, :size + it "is an alias of String#length" do + String.instance_method(:size).should == String.instance_method(:length) + end end diff --git a/spec/ruby/core/string/slice_spec.rb b/spec/ruby/core/string/slice_spec.rb index 14e2251b3f..16d7665bbf 100644 --- a/spec/ruby/core/string/slice_spec.rb +++ b/spec/ruby/core/string/slice_spec.rb @@ -1,39 +1,12 @@ -# -*- encoding: utf-8 -*- # frozen_string_literal: false require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/slice' describe "String#slice" do - it_behaves_like :string_slice, :slice -end - -describe "String#slice with index, length" do - it_behaves_like :string_slice_index_length, :slice -end - -describe "String#slice with Range" do - it_behaves_like :string_slice_range, :slice -end - -describe "String#slice with Regexp" do - it_behaves_like :string_slice_regexp, :slice -end - -describe "String#slice with Regexp, index" do - it_behaves_like :string_slice_regexp_index, :slice -end - -describe "String#slice with Regexp, group" do - it_behaves_like :string_slice_regexp_group, :slice -end - -describe "String#slice with String" do - it_behaves_like :string_slice_string, :slice -end - -describe "String#slice with Symbol" do - it_behaves_like :string_slice_symbol, :slice + it "is an alias of String#[]" do + String.instance_method(:slice).should == String.instance_method(:[]) + end end describe "String#slice! with index" do diff --git a/spec/ruby/core/string/succ_spec.rb b/spec/ruby/core/string/succ_spec.rb index 65047e0aa2..87beca8b09 100644 --- a/spec/ruby/core/string/succ_spec.rb +++ b/spec/ruby/core/string/succ_spec.rb @@ -1,11 +1,90 @@ +# encoding: binary require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/succ' describe "String#succ" do - it_behaves_like :string_succ, :succ + it "returns an empty string for empty strings" do + "".succ.should == "" + end + + it "returns the successor by increasing the rightmost alphanumeric (digit => digit, letter => letter with same case)" do + "abcd".succ.should == "abce" + "THX1138".succ.should == "THX1139" + + "<<koala>>".succ.should == "<<koalb>>" + "==A??".succ.should == "==B??" + end + + it "increases non-alphanumerics (via ascii rules) if there are no alphanumerics" do + "***".succ.should == "**+" + "**`".succ.should == "**a" + end + + it "increases the next best alphanumeric (jumping over non-alphanumerics) if there is a carry" do + "dz".succ.should == "ea" + "HZ".succ.should == "IA" + "49".succ.should == "50" + + "izz".succ.should == "jaa" + "IZZ".succ.should == "JAA" + "699".succ.should == "700" + + "6Z99z99Z".succ.should == "7A00a00A" + + "1999zzz".succ.should == "2000aaa" + "NZ/[]ZZZ9999".succ.should == "OA/[]AAA0000" + end + + it "increases the next best character if there is a carry for non-alphanumerics" do + "(\xFF".succ.should == ")\x00" + "`\xFF".succ.should == "a\x00" + "<\xFF\xFF".succ.should == "=\x00\x00" + end + + it "adds an additional character (just left to the last increased one) if there is a carry and no character left to increase" do + "z".succ.should == "aa" + "Z".succ.should == "AA" + "9".succ.should == "10" + + "zz".succ.should == "aaa" + "ZZ".succ.should == "AAA" + "99".succ.should == "100" + + "9Z99z99Z".succ.should == "10A00a00A" + + "ZZZ9999".succ.should == "AAAA0000" + "/[]9999".succ.should == "/[]10000" + "/[]ZZZ9999".succ.should == "/[]AAAA0000" + "Z/[]ZZZ9999".succ.should == "AA/[]AAA0000" + + # non-alphanumeric cases + "\xFF".succ.should == "\x01\x00" + "\xFF\xFF".succ.should == "\x01\x00\x00" + end + + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("").succ.should.instance_of?(String) + StringSpecs::MyString.new("a").succ.should.instance_of?(String) + StringSpecs::MyString.new("z").succ.should.instance_of?(String) + end + + it "returns a String in the same encoding as self" do + "z".encode("US-ASCII").succ.encoding.should == Encoding::US_ASCII + end end describe "String#succ!" do - it_behaves_like :string_succ_bang, :"succ!" + it "is equivalent to succ, but modifies self in place (still returns self)" do + ["", "abcd", "THX1138"].each do |s| + s = +s + r = s.dup.succ! + s.succ!.should.equal?(s) + s.should == r + end + end + + it "raises a FrozenError if self is frozen" do + -> { "".freeze.succ! }.should.raise(FrozenError) + -> { "abcd".freeze.succ! }.should.raise(FrozenError) + end end diff --git a/spec/ruby/core/string/to_s_spec.rb b/spec/ruby/core/string/to_s_spec.rb index e5872745a8..c48c7f89ac 100644 --- a/spec/ruby/core/string/to_s_spec.rb +++ b/spec/ruby/core/string/to_s_spec.rb @@ -1,7 +1,16 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/to_s' describe "String#to_s" do - it_behaves_like :string_to_s, :to_s + it "returns self when self.class == String" do + a = "a string" + a.should.equal?(a.to_s) + end + + it "returns a new instance of String when called on a subclass" do + a = StringSpecs::MyString.new("a string") + s = a.to_s + s.should == "a string" + s.should.instance_of?(String) + end end diff --git a/spec/ruby/core/string/to_str_spec.rb b/spec/ruby/core/string/to_str_spec.rb index e24262a7ae..8253b3d8a3 100644 --- a/spec/ruby/core/string/to_str_spec.rb +++ b/spec/ruby/core/string/to_str_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/to_s' describe "String#to_str" do - it_behaves_like :string_to_s, :to_str + it "is an alias of String#to_s" do + String.instance_method(:to_str).should == String.instance_method(:to_s) + end end diff --git a/spec/ruby/core/string/to_sym_spec.rb b/spec/ruby/core/string/to_sym_spec.rb index f9135211ce..f0ffe58674 100644 --- a/spec/ruby/core/string/to_sym_spec.rb +++ b/spec/ruby/core/string/to_sym_spec.rb @@ -1,7 +1,74 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/to_sym' describe "String#to_sym" do - it_behaves_like :string_to_sym, :to_sym + it "returns the symbol corresponding to self" do + "Koala".to_sym.should.equal? :Koala + 'cat'.to_sym.should.equal? :cat + '@cat'.to_sym.should.equal? :@cat + 'cat and dog'.to_sym.should.equal? :"cat and dog" + "abc=".to_sym.should.equal? :abc= + end + + it "does not special case +(binary) and -(binary)" do + "+(binary)".to_sym.should.equal? :"+(binary)" + "-(binary)".to_sym.should.equal? :"-(binary)" + end + + it "does not special case certain operators" do + "!@".to_sym.should.equal? :"!@" + "~@".to_sym.should.equal? :"~@" + "!(unary)".to_sym.should.equal? :"!(unary)" + "~(unary)".to_sym.should.equal? :"~(unary)" + "+(unary)".to_sym.should.equal? :"+(unary)" + "-(unary)".to_sym.should.equal? :"-(unary)" + end + + it "returns a US-ASCII Symbol for a UTF-8 String containing only US-ASCII characters" do + sym = "foobar".to_sym + sym.encoding.should == Encoding::US_ASCII + sym.should.equal? :"foobar" + end + + it "returns a US-ASCII Symbol for a binary String containing only US-ASCII characters" do + sym = "foobar".b.to_sym + sym.encoding.should == Encoding::US_ASCII + sym.should.equal? :"foobar" + end + + it "returns a UTF-8 Symbol for a UTF-8 String containing non US-ASCII characters" do + sym = "il était une fois".to_sym + sym.encoding.should == Encoding::UTF_8 + sym.should.equal? :"il était une fois" + end + + it "returns a UTF-16LE Symbol for a UTF-16LE String containing non US-ASCII characters" do + utf16_str = "UtéF16".encode(Encoding::UTF_16LE) + sym = utf16_str.to_sym + sym.encoding.should == Encoding::UTF_16LE + sym.to_s.should == utf16_str + end + + it "returns a binary Symbol for a binary String containing non US-ASCII characters" do + binary_string = "binarí".b + sym = binary_string.to_sym + sym.encoding.should == Encoding::BINARY + sym.to_s.should == binary_string + end + + it "ignores existing symbols with different encoding" do + source = "fée" + + iso_symbol = source.dup.force_encoding(Encoding::ISO_8859_1).to_sym + iso_symbol.encoding.should == Encoding::ISO_8859_1 + binary_symbol = source.dup.force_encoding(Encoding::BINARY).to_sym + binary_symbol.encoding.should == Encoding::BINARY + end + + it "raises an EncodingError for UTF-8 String containing invalid bytes" do + invalid_utf8 = "\xC3" + invalid_utf8.should_not.valid_encoding? + -> { + invalid_utf8.to_sym + }.should.raise(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"') + end end diff --git a/spec/ruby/core/string/uminus_spec.rb b/spec/ruby/core/string/uminus_spec.rb index 46d88f6704..43abf71d50 100644 --- a/spec/ruby/core/string/uminus_spec.rb +++ b/spec/ruby/core/string/uminus_spec.rb @@ -1,6 +1,53 @@ +# frozen_string_literal: false require_relative '../../spec_helper' -require_relative 'shared/dedup' describe 'String#-@' do - it_behaves_like :string_dedup, :-@ + it 'returns self if the String is frozen' do + input = 'foo'.freeze + output = -input + + output.should.equal?(input) + output.should.frozen? + end + + it 'returns a frozen copy if the String is not frozen' do + input = 'foo' + output = -input + + output.should.frozen? + output.should_not.equal?(input) + output.should == 'foo' + end + + it "returns the same object for equal unfrozen strings" do + origin = "this is a string" + dynamic = %w(this is a string).join(' ') + + origin.should_not.equal?(dynamic) + (-origin).should.equal?(-dynamic) + end + + it "returns the same object when it's called on the same String literal" do + (-"unfrozen string").should.equal?(-"unfrozen string") + (-"unfrozen string").should_not.equal?(-"another unfrozen string") + end + + it "deduplicates frozen strings" do + dynamic = %w(this string is frozen).join(' ').freeze + + dynamic.should_not.equal?("this string is frozen".freeze) + + (-dynamic).should.equal?("this string is frozen".freeze) + (-dynamic).should.equal?((-"this string is frozen").freeze) + end + + it "does not deduplicate a frozen string when it has instance variables" do + dynamic = %w(this string is frozen).join(' ') + dynamic.instance_variable_set(:@a, 1) + dynamic.freeze + + (-dynamic).should_not.equal?("this string is frozen".freeze) + (-dynamic).should_not.equal?((-"this string is frozen").freeze) + (-dynamic).should.equal?(-dynamic) + end end diff --git a/spec/ruby/core/struct/deconstruct_spec.rb b/spec/ruby/core/struct/deconstruct_spec.rb index 32d4f6bac4..aad82ac2ba 100644 --- a/spec/ruby/core/struct/deconstruct_spec.rb +++ b/spec/ruby/core/struct/deconstruct_spec.rb @@ -1,10 +1,7 @@ require_relative '../../spec_helper' describe "Struct#deconstruct" do - it "returns an array of attribute values" do - struct = Struct.new(:x, :y) - s = struct.new(1, 2) - - s.deconstruct.should == [1, 2] + it "is an alias of Struct#to_a" do + Struct.instance_method(:deconstruct).should == Struct.instance_method(:to_a) end end diff --git a/spec/ruby/core/struct/filter_spec.rb b/spec/ruby/core/struct/filter_spec.rb index 0ccd8ad6b2..5d11f47e6b 100644 --- a/spec/ruby/core/struct/filter_spec.rb +++ b/spec/ruby/core/struct/filter_spec.rb @@ -1,10 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/select' -require_relative 'shared/accessor' -require_relative '../enumerable/shared/enumeratorized' +require_relative 'fixtures/classes' describe "Struct#filter" do - it_behaves_like :struct_select, :filter - it_behaves_like :struct_accessor, :filter - it_behaves_like :enumeratorized_with_origin_size, :filter, Struct.new(:foo).new + it "is an alias of Struct#select" do + StructClasses::Car.instance_method(:filter).should == StructClasses::Car.instance_method(:select) + end end diff --git a/spec/ruby/core/struct/inspect_spec.rb b/spec/ruby/core/struct/inspect_spec.rb index 657b06abc1..13fd0bef41 100644 --- a/spec/ruby/core/struct/inspect_spec.rb +++ b/spec/ruby/core/struct/inspect_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/inspect' describe "Struct#inspect" do - it_behaves_like :struct_inspect, :inspect + it "is an alias of Struct#to_s" do + StructClasses::Car.instance_method(:inspect).should == StructClasses::Car.instance_method(:to_s) + end end diff --git a/spec/ruby/core/struct/length_spec.rb b/spec/ruby/core/struct/length_spec.rb index 1143676122..8d48f4118d 100644 --- a/spec/ruby/core/struct/length_spec.rb +++ b/spec/ruby/core/struct/length_spec.rb @@ -1,12 +1,8 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/accessor' describe "Struct#length" do - it "returns the number of attributes" do - StructClasses::Car.new('Cadillac', 'DeVille').length.should == 3 - StructClasses::Car.new.length.should == 3 + it "is an alias of Struct#size" do + StructClasses::Car.instance_method(:length).should == StructClasses::Car.instance_method(:size) end - - it_behaves_like :struct_accessor, :length end diff --git a/spec/ruby/core/struct/select_spec.rb b/spec/ruby/core/struct/select_spec.rb index ee846ec45f..5f26a177eb 100644 --- a/spec/ruby/core/struct/select_spec.rb +++ b/spec/ruby/core/struct/select_spec.rb @@ -1,10 +1,31 @@ require_relative '../../spec_helper' -require_relative 'shared/select' +require_relative 'fixtures/classes' require_relative 'shared/accessor' require_relative '../enumerable/shared/enumeratorized' describe "Struct#select" do - it_behaves_like :struct_select, :select it_behaves_like :struct_accessor, :select it_behaves_like :enumeratorized_with_origin_size, :select, Struct.new(:foo).new + + it "raises an ArgumentError if given any non-block arguments" do + struct = StructClasses::Car.new + -> { struct.select(1) { } }.should.raise(ArgumentError) + end + + it "returns a new array of elements for which block is true" do + struct = StructClasses::Car.new("Toyota", "Tercel", "2000") + struct.select { |i| i == "2000" }.should == [ "2000" ] + end + + it "returns an instance of Array" do + struct = StructClasses::Car.new("Ford", "Escort", "1995") + struct.select { true }.should.instance_of?(Array) + end + + describe "without block" do + it "returns an instance of Enumerator" do + struct = Struct.new(:foo).new + struct.select.should.instance_of?(Enumerator) + end + end end diff --git a/spec/ruby/core/struct/shared/inspect.rb b/spec/ruby/core/struct/shared/inspect.rb deleted file mode 100644 index 1a0fb6a6b2..0000000000 --- a/spec/ruby/core/struct/shared/inspect.rb +++ /dev/null @@ -1,40 +0,0 @@ -describe :struct_inspect, shared: true do - it "returns a string representation showing members and values" do - car = StructClasses::Car.new('Ford', 'Ranger') - car.send(@method).should == '#<struct StructClasses::Car make="Ford", model="Ranger", year=nil>' - end - - it "returns a string representation without the class name for anonymous structs" do - Struct.new(:a).new("").send(@method).should == '#<struct a="">' - end - - it "returns a string representation without the class name for structs nested in anonymous classes" do - c = Class.new - c.class_eval <<~DOC - class Foo < Struct.new(:a); end - DOC - - c::Foo.new("").send(@method).should == '#<struct a="">' - end - - it "returns a string representation without the class name for structs nested in anonymous modules" do - m = Module.new - m.module_eval <<~DOC - class Foo < Struct.new(:a); end - DOC - - m::Foo.new("").send(@method).should == '#<struct a="">' - end - - it "does not call #name method" do - struct = StructClasses::StructWithOverriddenName.new("") - struct.send(@method).should == '#<struct StructClasses::StructWithOverriddenName a="">' - end - - it "does not call #name method when struct is anonymous" do - struct = Struct.new(:a) - def struct.name; "A"; end - - struct.new("").send(@method).should == '#<struct a="">' - end -end diff --git a/spec/ruby/core/struct/shared/select.rb b/spec/ruby/core/struct/shared/select.rb deleted file mode 100644 index dfa1a809fe..0000000000 --- a/spec/ruby/core/struct/shared/select.rb +++ /dev/null @@ -1,26 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :struct_select, shared: true do - it "raises an ArgumentError if given any non-block arguments" do - struct = StructClasses::Car.new - -> { struct.send(@method, 1) { } }.should.raise(ArgumentError) - end - - it "returns a new array of elements for which block is true" do - struct = StructClasses::Car.new("Toyota", "Tercel", "2000") - struct.send(@method) { |i| i == "2000" }.should == [ "2000" ] - end - - it "returns an instance of Array" do - struct = StructClasses::Car.new("Ford", "Escort", "1995") - struct.send(@method) { true }.should.instance_of?(Array) - end - - describe "without block" do - it "returns an instance of Enumerator" do - struct = Struct.new(:foo).new - struct.send(@method).should.instance_of?(Enumerator) - end - end -end diff --git a/spec/ruby/core/struct/size_spec.rb b/spec/ruby/core/struct/size_spec.rb index 09f260cf20..5f07320bb9 100644 --- a/spec/ruby/core/struct/size_spec.rb +++ b/spec/ruby/core/struct/size_spec.rb @@ -3,8 +3,9 @@ require_relative 'fixtures/classes' require_relative 'shared/accessor' describe "Struct#size" do - it "is a synonym for length" do - StructClasses::Car.new.size.should == StructClasses::Car.new.length + it "returns the number of attributes" do + StructClasses::Car.new('Cadillac', 'DeVille').length.should == 3 + StructClasses::Car.new.length.should == 3 end it_behaves_like :struct_accessor, :size diff --git a/spec/ruby/core/struct/to_s_spec.rb b/spec/ruby/core/struct/to_s_spec.rb index 94c672d3d5..9648b8af9b 100644 --- a/spec/ruby/core/struct/to_s_spec.rb +++ b/spec/ruby/core/struct/to_s_spec.rb @@ -1,12 +1,43 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/inspect' describe "Struct#to_s" do - it "is a synonym for inspect" do + it "returns a string representation showing members and values" do car = StructClasses::Car.new('Ford', 'Ranger') - car.inspect.should == car.to_s + car.to_s.should == '#<struct StructClasses::Car make="Ford", model="Ranger", year=nil>' end - it_behaves_like :struct_inspect, :to_s + it "returns a string representation without the class name for anonymous structs" do + Struct.new(:a).new("").to_s.should == '#<struct a="">' + end + + it "returns a string representation without the class name for structs nested in anonymous classes" do + c = Class.new + c.class_eval <<~DOC + class Foo < Struct.new(:a); end + DOC + + c::Foo.new("").to_s.should == '#<struct a="">' + end + + it "returns a string representation without the class name for structs nested in anonymous modules" do + m = Module.new + m.module_eval <<~DOC + class Foo < Struct.new(:a); end + DOC + + m::Foo.new("").to_s.should == '#<struct a="">' + end + + it "does not call #name method" do + struct = StructClasses::StructWithOverriddenName.new("") + struct.to_s.should == '#<struct StructClasses::StructWithOverriddenName a="">' + end + + it "does not call #name method when struct is anonymous" do + struct = Struct.new(:a) + def struct.name; "A"; end + + struct.new("").to_s.should == '#<struct a="">' + end end diff --git a/spec/ruby/core/struct/values_spec.rb b/spec/ruby/core/struct/values_spec.rb index b2d11725b9..16583253d7 100644 --- a/spec/ruby/core/struct/values_spec.rb +++ b/spec/ruby/core/struct/values_spec.rb @@ -2,10 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Struct#values" do - it "is a synonym for to_a" do - car = StructClasses::Car.new('Nissan', 'Maxima') - car.values.should == car.to_a - - StructClasses::Car.new.values.should == StructClasses::Car.new.to_a + it "is an alias of Struct#to_a" do + StructClasses::Car.instance_method(:values).should == StructClasses::Car.instance_method(:to_a) end end diff --git a/spec/ruby/core/symbol/case_compare_spec.rb b/spec/ruby/core/symbol/case_compare_spec.rb index 0c6bc1eda5..a296132c04 100644 --- a/spec/ruby/core/symbol/case_compare_spec.rb +++ b/spec/ruby/core/symbol/case_compare_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' describe "Symbol#===" do - it "returns true when the argument is a Symbol" do - (Symbol === :ruby).should == true - end - - it "returns false when the argument is a String" do - (Symbol === 'ruby').should == false + it "is an alias of Symbol#==" do + Symbol.instance_method(:===).should == Symbol.instance_method(:==) end end diff --git a/spec/ruby/core/symbol/element_reference_spec.rb b/spec/ruby/core/symbol/element_reference_spec.rb index df6bc15ddb..360a661891 100644 --- a/spec/ruby/core/symbol/element_reference_spec.rb +++ b/spec/ruby/core/symbol/element_reference_spec.rb @@ -1,6 +1,263 @@ require_relative '../../spec_helper' -require_relative 'shared/slice' +require_relative 'fixtures/classes' describe "Symbol#[]" do - it_behaves_like :symbol_slice, :[] + describe "with an Integer index" do + it "returns the character code of the element at the index" do + :symbol[1].should == ?y + end + + it "returns nil if the index starts from the end and is greater than the length" do + :symbol[-10].should == nil + end + + it "returns nil if the index is greater than the length" do + :symbol[42].should == nil + end + end + + describe "with an Integer index and length" do + describe "and a positive index and length" do + it "returns a slice" do + :symbol[1, 3].should == "ymb" + end + + it "returns a blank slice if the length is 0" do + :symbol[0, 0].should == "" + :symbol[1, 0].should == "" + end + + it "returns a slice of all remaining characters if the given length is greater than the actual length" do + :symbol[1, 100].should == "ymbol" + end + + it "returns nil if the index is greater than the length" do + :symbol[10, 1].should == nil + end + end + + describe "and a positive index and negative length" do + it "returns nil" do + :symbol[0, -1].should == nil + :symbol[1, -1].should == nil + end + end + + describe "and a negative index and positive length" do + it "returns a slice starting from the end upto the length" do + :symbol[-3, 2].should == "bo" + end + + it "returns a blank slice if the length is 0" do + :symbol[-1, 0].should == "" + end + + it "returns a slice of all remaining characters if the given length is larger than the actual length" do + :symbol[-4, 100].should == "mbol" + end + + it "returns nil if the index is past the start" do + :symbol[-10, 1].should == nil + end + end + + describe "and a negative index and negative length" do + it "returns nil" do + :symbol[-1, -1].should == nil + end + end + + describe "and a Float length" do + it "converts the length to an Integer" do + :symbol[2, 2.5].should == "mb" + end + end + + describe "and a nil length" do + it "raises a TypeError" do + -> { :symbol[1, nil] }.should.raise(TypeError) + end + end + + describe "and a length that cannot be converted into an Integer" do + it "raises a TypeError when given an Array" do + -> { :symbol[1, Array.new] }.should.raise(TypeError) + end + + it "raises a TypeError when given an Hash" do + -> { :symbol[1, Hash.new] }.should.raise(TypeError) + end + + it "raises a TypeError when given an Object" do + -> { :symbol[1, Object.new] }.should.raise(TypeError) + end + end + end + + describe "with a Float index" do + it "converts the index to an Integer" do + :symbol[1.5].should == ?y + end + end + + describe "with a nil index" do + it "raises a TypeError" do + -> { :symbol[nil] }.should.raise(TypeError) + end + end + + describe "with an index that cannot be converted into an Integer" do + it "raises a TypeError when given an Array" do + -> { :symbol[Array.new] }.should.raise(TypeError) + end + + it "raises a TypeError when given an Hash" do + -> { :symbol[Hash.new] }.should.raise(TypeError) + end + + it "raises a TypeError when given an Object" do + -> { :symbol[Object.new] }.should.raise(TypeError) + end + end + + describe "with a Range slice" do + describe "that is within bounds" do + it "returns a slice if both range values begin at the start and are within bounds" do + :symbol[1..4].should == "ymbo" + end + + it "returns a slice if the first range value begins at the start and the last begins at the end" do + :symbol[1..-1].should == "ymbol" + end + + it "returns a slice if the first range value begins at the end and the last begins at the end" do + :symbol[-4..-1].should == "mbol" + end + end + + describe "that is out of bounds" do + it "returns nil if the first range value begins past the end" do + :symbol[10..12].should == nil + end + + it "returns a blank string if the first range value is within bounds and the last range value is not" do + :symbol[-2..-10].should == "" + :symbol[2..-10].should == "" + end + + it "returns nil if the first range value starts from the end and is within bounds and the last value starts from the end and is greater than the length" do + :symbol[-10..-12].should == nil + end + + it "returns nil if the first range value starts from the end and is out of bounds and the last value starts from the end and is less than the length" do + :symbol[-10..-2].should == nil + end + end + + describe "with Float values" do + it "converts the first value to an Integer" do + :symbol[0.5..2].should == "sym" + end + + it "converts the last value to an Integer" do + :symbol[0..2.5].should == "sym" + end + end + end + + describe "with a Range subclass slice" do + it "returns a slice" do + range = SymbolSpecs::MyRange.new(1, 4) + :symbol[range].should == "ymbo" + end + end + + describe "with a Regex slice" do + describe "without a capture index" do + it "returns a string of the match" do + :symbol[/[^bol]+/].should == "sym" + end + + it "returns nil if the expression does not match" do + :symbol[/0-9/].should == nil + end + + it "sets $~ to the MatchData if there is a match" do + :symbol[/[^bol]+/] + $~[0].should == "sym" + end + + it "does not set $~ if there if there is not a match" do + :symbol[/[0-9]+/] + $~.should == nil + end + end + + describe "with a capture index" do + it "returns a string of the complete match if the capture index is 0" do + :symbol[/(sy)(mb)(ol)/, 0].should == "symbol" + end + + it "returns a string for the matched capture at the given index" do + :symbol[/(sy)(mb)(ol)/, 1].should == "sy" + :symbol[/(sy)(mb)(ol)/, -1].should == "ol" + end + + it "returns nil if there is no capture for the index" do + :symbol[/(sy)(mb)(ol)/, 4].should == nil + :symbol[/(sy)(mb)(ol)/, -4].should == nil + end + + it "converts the index to an Integer" do + :symbol[/(sy)(mb)(ol)/, 1.5].should == "sy" + end + + describe "and an index that cannot be converted to an Integer" do + it "raises a TypeError when given an Hash" do + -> { :symbol[/(sy)(mb)(ol)/, Hash.new] }.should.raise(TypeError) + end + + it "raises a TypeError when given an Array" do + -> { :symbol[/(sy)(mb)(ol)/, Array.new] }.should.raise(TypeError) + end + + it "raises a TypeError when given an Object" do + -> { :symbol[/(sy)(mb)(ol)/, Object.new] }.should.raise(TypeError) + end + end + + it "raises a TypeError if the index is nil" do + -> { :symbol[/(sy)(mb)(ol)/, nil] }.should.raise(TypeError) + end + + it "sets $~ to the MatchData if there is a match" do + :symbol[/(sy)(mb)(ol)/, 0] + $~[0].should == "symbol" + $~[1].should == "sy" + $~[2].should == "mb" + $~[3].should == "ol" + end + + it "does not set $~ to the MatchData if there is not a match" do + :symbol[/0-9/, 0] + $~.should == nil + end + end + end + + describe "with a String slice" do + it "does not set $~" do + $~ = nil + :symbol["sym"] + $~.should == nil + end + + it "returns a string if there is match" do + :symbol["ymb"].should == "ymb" + end + + it "returns nil if there is not a match" do + :symbol["foo"].should == nil + end + end end diff --git a/spec/ruby/core/symbol/id2name_spec.rb b/spec/ruby/core/symbol/id2name_spec.rb index 2caa89fc37..abcbff65f4 100644 --- a/spec/ruby/core/symbol/id2name_spec.rb +++ b/spec/ruby/core/symbol/id2name_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/id2name' describe "Symbol#id2name" do - it_behaves_like :symbol_id2name, :id2name + it "is an alias of Symbol#to_s" do + Symbol.instance_method(:id2name).should == Symbol.instance_method(:to_s) + end end diff --git a/spec/ruby/core/symbol/intern_spec.rb b/spec/ruby/core/symbol/intern_spec.rb index 9d0914c7fb..746d313d40 100644 --- a/spec/ruby/core/symbol/intern_spec.rb +++ b/spec/ruby/core/symbol/intern_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' describe "Symbol#intern" do - it "returns self" do - :foo.intern.should == :foo - end - - it "returns a Symbol" do - :foo.intern.should.is_a?(Symbol) + it "is an alias of Symbol#to_sym" do + Symbol.instance_method(:intern).should == Symbol.instance_method(:to_sym) end end diff --git a/spec/ruby/core/symbol/length_spec.rb b/spec/ruby/core/symbol/length_spec.rb index 27bee575ef..29dd4ad46b 100644 --- a/spec/ruby/core/symbol/length_spec.rb +++ b/spec/ruby/core/symbol/length_spec.rb @@ -1,6 +1,23 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "Symbol#length" do - it_behaves_like :symbol_length, :length + it "returns 0 for empty name" do + :''.length.should == 0 + end + + it "returns 1 for name formed by a NUL character" do + :"\x00".length.should == 1 + end + + it "returns 3 for name formed by 3 ASCII characters" do + :one.length.should == 3 + end + + it "returns 4 for name formed by 4 ASCII characters" do + :four.length.should == 4 + end + + it "returns 4 for name formed by 1 multibyte and 3 ASCII characters" do + :"\xC3\x9Cber".length.should == 4 + end end diff --git a/spec/ruby/core/symbol/next_spec.rb b/spec/ruby/core/symbol/next_spec.rb index 97fe913739..e80bbaa508 100644 --- a/spec/ruby/core/symbol/next_spec.rb +++ b/spec/ruby/core/symbol/next_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/succ' describe "Symbol#next" do - it_behaves_like :symbol_succ, :next + it "is an alias of Symbol#succ" do + Symbol.instance_method(:next).should == Symbol.instance_method(:succ) + end end diff --git a/spec/ruby/core/symbol/shared/id2name.rb b/spec/ruby/core/symbol/shared/id2name.rb deleted file mode 100644 index 00a9c7d7dc..0000000000 --- a/spec/ruby/core/symbol/shared/id2name.rb +++ /dev/null @@ -1,30 +0,0 @@ -describe :symbol_id2name, shared: true do - it "returns the string corresponding to self" do - :rubinius.send(@method).should == "rubinius" - :squash.send(@method).should == "squash" - :[].send(@method).should == "[]" - :@ruby.send(@method).should == "@ruby" - :@@ruby.send(@method).should == "@@ruby" - end - - it "returns a String in the same encoding as self" do - string = "ruby".encode("US-ASCII") - symbol = string.to_sym - - symbol.send(@method).encoding.should == Encoding::US_ASCII - end - - ruby_version_is "3.4" do - it "warns about mutating returned string" do - -> { :bad!.send(@method).upcase! }.should complain(/warning: string returned by :bad!.to_s will be frozen in the future/) - end - - it "does not warn about mutation when Warning[:deprecated] is false" do - deprecated = Warning[:deprecated] - Warning[:deprecated] = false - -> { :bad!.send(@method).upcase! }.should_not complain - ensure - Warning[:deprecated] = deprecated - end - end -end diff --git a/spec/ruby/core/symbol/shared/length.rb b/spec/ruby/core/symbol/shared/length.rb deleted file mode 100644 index 692e8c57e3..0000000000 --- a/spec/ruby/core/symbol/shared/length.rb +++ /dev/null @@ -1,23 +0,0 @@ -# -*- encoding: utf-8 -*- - -describe :symbol_length, shared: true do - it "returns 0 for empty name" do - :''.send(@method).should == 0 - end - - it "returns 1 for name formed by a NUL character" do - :"\x00".send(@method).should == 1 - end - - it "returns 3 for name formed by 3 ASCII characters" do - :one.send(@method).should == 3 - end - - it "returns 4 for name formed by 4 ASCII characters" do - :four.send(@method).should == 4 - end - - it "returns 4 for name formed by 1 multibyte and 3 ASCII characters" do - :"\xC3\x9Cber".send(@method).should == 4 - end -end diff --git a/spec/ruby/core/symbol/shared/slice.rb b/spec/ruby/core/symbol/shared/slice.rb deleted file mode 100644 index 4e3a35240c..0000000000 --- a/spec/ruby/core/symbol/shared/slice.rb +++ /dev/null @@ -1,262 +0,0 @@ -require_relative '../fixtures/classes' - -describe :symbol_slice, shared: true do - describe "with an Integer index" do - it "returns the character code of the element at the index" do - :symbol.send(@method, 1).should == ?y - end - - it "returns nil if the index starts from the end and is greater than the length" do - :symbol.send(@method, -10).should == nil - end - - it "returns nil if the index is greater than the length" do - :symbol.send(@method, 42).should == nil - end - end - - describe "with an Integer index and length" do - describe "and a positive index and length" do - it "returns a slice" do - :symbol.send(@method, 1,3).should == "ymb" - end - - it "returns a blank slice if the length is 0" do - :symbol.send(@method, 0,0).should == "" - :symbol.send(@method, 1,0).should == "" - end - - it "returns a slice of all remaining characters if the given length is greater than the actual length" do - :symbol.send(@method, 1,100).should == "ymbol" - end - - it "returns nil if the index is greater than the length" do - :symbol.send(@method, 10,1).should == nil - end - end - - describe "and a positive index and negative length" do - it "returns nil" do - :symbol.send(@method, 0,-1).should == nil - :symbol.send(@method, 1,-1).should == nil - end - end - - describe "and a negative index and positive length" do - it "returns a slice starting from the end upto the length" do - :symbol.send(@method, -3,2).should == "bo" - end - - it "returns a blank slice if the length is 0" do - :symbol.send(@method, -1,0).should == "" - end - - it "returns a slice of all remaining characters if the given length is larger than the actual length" do - :symbol.send(@method, -4,100).should == "mbol" - end - - it "returns nil if the index is past the start" do - :symbol.send(@method, -10,1).should == nil - end - end - - describe "and a negative index and negative length" do - it "returns nil" do - :symbol.send(@method, -1,-1).should == nil - end - end - - describe "and a Float length" do - it "converts the length to an Integer" do - :symbol.send(@method, 2,2.5).should == "mb" - end - end - - describe "and a nil length" do - it "raises a TypeError" do - -> { :symbol.send(@method, 1,nil) }.should.raise(TypeError) - end - end - - describe "and a length that cannot be converted into an Integer" do - it "raises a TypeError when given an Array" do - -> { :symbol.send(@method, 1,Array.new) }.should.raise(TypeError) - end - - it "raises a TypeError when given an Hash" do - -> { :symbol.send(@method, 1,Hash.new) }.should.raise(TypeError) - end - - it "raises a TypeError when given an Object" do - -> { :symbol.send(@method, 1,Object.new) }.should.raise(TypeError) - end - end - end - - describe "with a Float index" do - it "converts the index to an Integer" do - :symbol.send(@method, 1.5).should == ?y - end - end - - describe "with a nil index" do - it "raises a TypeError" do - -> { :symbol.send(@method, nil) }.should.raise(TypeError) - end - end - - describe "with an index that cannot be converted into an Integer" do - it "raises a TypeError when given an Array" do - -> { :symbol.send(@method, Array.new) }.should.raise(TypeError) - end - - it "raises a TypeError when given an Hash" do - -> { :symbol.send(@method, Hash.new) }.should.raise(TypeError) - end - - it "raises a TypeError when given an Object" do - -> { :symbol.send(@method, Object.new) }.should.raise(TypeError) - end - end - - describe "with a Range slice" do - describe "that is within bounds" do - it "returns a slice if both range values begin at the start and are within bounds" do - :symbol.send(@method, 1..4).should == "ymbo" - end - - it "returns a slice if the first range value begins at the start and the last begins at the end" do - :symbol.send(@method, 1..-1).should == "ymbol" - end - - it "returns a slice if the first range value begins at the end and the last begins at the end" do - :symbol.send(@method, -4..-1).should == "mbol" - end - end - - describe "that is out of bounds" do - it "returns nil if the first range value begins past the end" do - :symbol.send(@method, 10..12).should == nil - end - - it "returns a blank string if the first range value is within bounds and the last range value is not" do - :symbol.send(@method, -2..-10).should == "" - :symbol.send(@method, 2..-10).should == "" - end - - it "returns nil if the first range value starts from the end and is within bounds and the last value starts from the end and is greater than the length" do - :symbol.send(@method, -10..-12).should == nil - end - - it "returns nil if the first range value starts from the end and is out of bounds and the last value starts from the end and is less than the length" do - :symbol.send(@method, -10..-2).should == nil - end - end - - describe "with Float values" do - it "converts the first value to an Integer" do - :symbol.send(@method, 0.5..2).should == "sym" - end - - it "converts the last value to an Integer" do - :symbol.send(@method, 0..2.5).should == "sym" - end - end - end - - describe "with a Range subclass slice" do - it "returns a slice" do - range = SymbolSpecs::MyRange.new(1, 4) - :symbol.send(@method, range).should == "ymbo" - end - end - - describe "with a Regex slice" do - describe "without a capture index" do - it "returns a string of the match" do - :symbol.send(@method, /[^bol]+/).should == "sym" - end - - it "returns nil if the expression does not match" do - :symbol.send(@method, /0-9/).should == nil - end - - it "sets $~ to the MatchData if there is a match" do - :symbol.send(@method, /[^bol]+/) - $~[0].should == "sym" - end - - it "does not set $~ if there if there is not a match" do - :symbol.send(@method, /[0-9]+/) - $~.should == nil - end - end - - describe "with a capture index" do - it "returns a string of the complete match if the capture index is 0" do - :symbol.send(@method, /(sy)(mb)(ol)/, 0).should == "symbol" - end - - it "returns a string for the matched capture at the given index" do - :symbol.send(@method, /(sy)(mb)(ol)/, 1).should == "sy" - :symbol.send(@method, /(sy)(mb)(ol)/, -1).should == "ol" - end - - it "returns nil if there is no capture for the index" do - :symbol.send(@method, /(sy)(mb)(ol)/, 4).should == nil - :symbol.send(@method, /(sy)(mb)(ol)/, -4).should == nil - end - - it "converts the index to an Integer" do - :symbol.send(@method, /(sy)(mb)(ol)/, 1.5).should == "sy" - end - - describe "and an index that cannot be converted to an Integer" do - it "raises a TypeError when given an Hash" do - -> { :symbol.send(@method, /(sy)(mb)(ol)/, Hash.new) }.should.raise(TypeError) - end - - it "raises a TypeError when given an Array" do - -> { :symbol.send(@method, /(sy)(mb)(ol)/, Array.new) }.should.raise(TypeError) - end - - it "raises a TypeError when given an Object" do - -> { :symbol.send(@method, /(sy)(mb)(ol)/, Object.new) }.should.raise(TypeError) - end - end - - it "raises a TypeError if the index is nil" do - -> { :symbol.send(@method, /(sy)(mb)(ol)/, nil) }.should.raise(TypeError) - end - - it "sets $~ to the MatchData if there is a match" do - :symbol.send(@method, /(sy)(mb)(ol)/, 0) - $~[0].should == "symbol" - $~[1].should == "sy" - $~[2].should == "mb" - $~[3].should == "ol" - end - - it "does not set $~ to the MatchData if there is not a match" do - :symbol.send(@method, /0-9/, 0) - $~.should == nil - end - end - end - - describe "with a String slice" do - it "does not set $~" do - $~ = nil - :symbol.send(@method, "sym") - $~.should == nil - end - - it "returns a string if there is match" do - :symbol.send(@method, "ymb").should == "ymb" - end - - it "returns nil if there is not a match" do - :symbol.send(@method, "foo").should == nil - end - end -end diff --git a/spec/ruby/core/symbol/shared/succ.rb b/spec/ruby/core/symbol/shared/succ.rb deleted file mode 100644 index dde298207e..0000000000 --- a/spec/ruby/core/symbol/shared/succ.rb +++ /dev/null @@ -1,18 +0,0 @@ -require_relative '../../../spec_helper' - -describe :symbol_succ, shared: true do - it "returns a successor" do - :abcd.send(@method).should == :abce - :THX1138.send(@method).should == :THX1139 - end - - it "propagates a 'carry'" do - :"1999zzz".send(@method).should == :"2000aaa" - :ZZZ9999.send(@method).should == :AAAA0000 - end - - it "increments non-alphanumeric characters when no alphanumeric characters are present" do - :"<<koala>>".send(@method).should == :"<<koalb>>" - :"***".send(@method).should == :"**+" - end -end diff --git a/spec/ruby/core/symbol/size_spec.rb b/spec/ruby/core/symbol/size_spec.rb index 5e2aa8d4d2..b5d375b3aa 100644 --- a/spec/ruby/core/symbol/size_spec.rb +++ b/spec/ruby/core/symbol/size_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/length' describe "Symbol#size" do - it_behaves_like :symbol_length, :size + it "is an alias of Symbol#length" do + Symbol.instance_method(:size).should == Symbol.instance_method(:length) + end end diff --git a/spec/ruby/core/symbol/slice_spec.rb b/spec/ruby/core/symbol/slice_spec.rb index d2421c474c..050a2cf38c 100644 --- a/spec/ruby/core/symbol/slice_spec.rb +++ b/spec/ruby/core/symbol/slice_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/slice' describe "Symbol#slice" do - it_behaves_like :symbol_slice, :slice + it "is an alias of Symbol#[]" do + Symbol.instance_method(:slice).should == Symbol.instance_method(:[]) + end end diff --git a/spec/ruby/core/symbol/succ_spec.rb b/spec/ruby/core/symbol/succ_spec.rb index 694bfff862..893164a2a6 100644 --- a/spec/ruby/core/symbol/succ_spec.rb +++ b/spec/ruby/core/symbol/succ_spec.rb @@ -1,6 +1,18 @@ require_relative '../../spec_helper' -require_relative 'shared/succ' describe "Symbol#succ" do - it_behaves_like :symbol_succ, :succ + it "returns a successor" do + :abcd.succ.should == :abce + :THX1138.succ.should == :THX1139 + end + + it "propagates a 'carry'" do + :"1999zzz".succ.should == :"2000aaa" + :ZZZ9999.succ.should == :AAAA0000 + end + + it "increments non-alphanumeric characters when no alphanumeric characters are present" do + :"<<koala>>".succ.should == :"<<koalb>>" + :"***".succ.should == :"**+" + end end diff --git a/spec/ruby/core/symbol/to_s_spec.rb b/spec/ruby/core/symbol/to_s_spec.rb index cd963faa28..2cb57c4cbc 100644 --- a/spec/ruby/core/symbol/to_s_spec.rb +++ b/spec/ruby/core/symbol/to_s_spec.rb @@ -1,6 +1,32 @@ require_relative '../../spec_helper' -require_relative 'shared/id2name' describe "Symbol#to_s" do - it_behaves_like :symbol_id2name, :to_s + it "returns the string corresponding to self" do + :rubinius.to_s.should == "rubinius" + :squash.to_s.should == "squash" + :[].to_s.should == "[]" + :@ruby.to_s.should == "@ruby" + :@@ruby.to_s.should == "@@ruby" + end + + it "returns a String in the same encoding as self" do + string = "ruby".encode("US-ASCII") + symbol = string.to_sym + + symbol.to_s.encoding.should == Encoding::US_ASCII + end + + ruby_version_is "3.4" do + it "warns about mutating returned string" do + -> { :bad!.to_s.upcase! }.should complain(/warning: string returned by :bad!.to_s will be frozen in the future/) + end + + it "does not warn about mutation when Warning[:deprecated] is false" do + deprecated = Warning[:deprecated] + Warning[:deprecated] = false + -> { :bad!.to_s.upcase! }.should_not complain + ensure + Warning[:deprecated] = deprecated + end + end end diff --git a/spec/ruby/core/symbol/to_sym_spec.rb b/spec/ruby/core/symbol/to_sym_spec.rb index e75f3d48a8..062daa3fc7 100644 --- a/spec/ruby/core/symbol/to_sym_spec.rb +++ b/spec/ruby/core/symbol/to_sym_spec.rb @@ -3,7 +3,7 @@ require_relative '../../spec_helper' describe "Symbol#to_sym" do it "returns self" do [:rubinius, :squash, :[], :@ruby, :@@ruby].each do |sym| - sym.to_sym.should == sym + sym.to_sym.should.equal?(sym) end end end diff --git a/spec/ruby/core/thread/exit_spec.rb b/spec/ruby/core/thread/exit_spec.rb index b2e923c680..dbcd889898 100644 --- a/spec/ruby/core/thread/exit_spec.rb +++ b/spec/ruby/core/thread/exit_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/exit' + +describe "Thread#exit" do + it "is an alias of Thread#kill" do + Thread.instance_method(:exit).should == Thread.instance_method(:kill) + end +end describe "Thread#exit!" do it "needs to be reviewed for spec completeness" diff --git a/spec/ruby/core/thread/fork_spec.rb b/spec/ruby/core/thread/fork_spec.rb index a2f4181298..60d574a185 100644 --- a/spec/ruby/core/thread/fork_spec.rb +++ b/spec/ruby/core/thread/fork_spec.rb @@ -1,9 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/start' describe "Thread.fork" do - describe "Thread.start" do - it_behaves_like :thread_start, :fork + it "is an alias of Thread.start" do + Thread.method(:fork).should == Thread.method(:start) end end diff --git a/spec/ruby/core/thread/inspect_spec.rb b/spec/ruby/core/thread/inspect_spec.rb index bd6e0c31fc..fee401830a 100644 --- a/spec/ruby/core/thread/inspect_spec.rb +++ b/spec/ruby/core/thread/inspect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' describe "Thread#inspect" do - it_behaves_like :thread_to_s, :inspect + it "is an alias of Thread#to_s" do + Thread.instance_method(:inspect).should == Thread.instance_method(:to_s) + end end diff --git a/spec/ruby/core/thread/kill_spec.rb b/spec/ruby/core/thread/kill_spec.rb index f9f1f46744..907cc5c4d8 100644 --- a/spec/ruby/core/thread/kill_spec.rb +++ b/spec/ruby/core/thread/kill_spec.rb @@ -1,12 +1,221 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/exit' # This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19473223/job/f69derxnlo09xhuj # TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard. platform_is_not :mingw do describe "Thread#kill" do - it_behaves_like :thread_exit, :kill + before :each do + ScratchPad.clear + end + + it "kills sleeping thread" do + sleeping_thread = Thread.new do + sleep + ScratchPad.record :after_sleep + end + Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep" + sleeping_thread.kill + sleeping_thread.join + ScratchPad.recorded.should == nil + end + + it "kills current thread" do + thread = Thread.new do + Thread.current.kill + ScratchPad.record :after_sleep + end + thread.join + ScratchPad.recorded.should == nil + end + + it "runs ensure clause" do + thread = ThreadSpecs.dying_thread_ensures(:kill) { ScratchPad.record :in_ensure_clause } + thread.join + ScratchPad.recorded.should == :in_ensure_clause + end + + it "runs nested ensure clauses" do + ScratchPad.record [] + @outer = Thread.new do + begin + @inner = Thread.new do + begin + sleep + ensure + ScratchPad << :inner_ensure_clause + end + end + sleep + ensure + ScratchPad << :outer_ensure_clause + @inner.kill + @inner.join + end + end + Thread.pass while @outer.status and @outer.status != "sleep" + Thread.pass until @inner + Thread.pass while @inner.status and @inner.status != "sleep" + @outer.kill + @outer.join + ScratchPad.recorded.should.include?(:inner_ensure_clause) + ScratchPad.recorded.should.include?(:outer_ensure_clause) + end + + it "does not set $!" do + thread = ThreadSpecs.dying_thread_ensures(:kill) { ScratchPad.record $! } + thread.join + ScratchPad.recorded.should == nil + end + + it "does not reset $!" do + ScratchPad.record [] + + exc = RuntimeError.new("foo") + thread = Thread.new do + begin + raise exc + ensure + ScratchPad << $! + begin + Thread.current.kill + ensure + ScratchPad << $! + end + end + end + thread.join + ScratchPad.recorded.should == [exc, exc] + end + + it "cannot be rescued" do + thread = Thread.new do + begin + Thread.current.kill + rescue Exception + ScratchPad.record :in_rescue + end + ScratchPad.record :end_of_thread_block + end + + thread.join + ScratchPad.recorded.should == nil + end + + it "kills the entire thread when a fiber is active" do + t = Thread.new do + Fiber.new do + sleep + end.resume + ScratchPad.record :fiber_resumed + end + Thread.pass while t.status and t.status != "sleep" + t.kill + t.join + ScratchPad.recorded.should == nil + end + + it "kills other fibers of that thread without running their ensure clauses" do + t = Thread.new do + f = Fiber.new do + ScratchPad.record :fiber_resumed + begin + Fiber.yield + ensure + ScratchPad.record :fiber_ensure + end + end + f.resume + sleep + end + Thread.pass until t.stop? + t.kill + t.join + ScratchPad.recorded.should == :fiber_resumed + end + + # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed + quarantine! do + it "killing dying running does nothing" do + in_ensure_clause = false + exit_loop = true + t = ThreadSpecs.dying_thread_ensures do + in_ensure_clause = true + loop { if exit_loop then break end } + ScratchPad.record :after_stop + end + + Thread.pass until in_ensure_clause == true + 10.times { t.kill; Thread.pass } + exit_loop = true + t.join + ScratchPad.recorded.should == :after_stop + end + end + + quarantine! do + + it "propagates inner exception to Thread.join if there is an outer ensure clause" do + thread = ThreadSpecs.dying_thread_with_outer_ensure(:kill) { } + -> { thread.join }.should.raise(RuntimeError, "In dying thread") + end + + it "runs all outer ensure clauses even if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(:kill) { ScratchPad.record :in_outer_ensure_clause } + ScratchPad.recorded.should == :in_outer_ensure_clause + end + + it "sets $! in outer ensure clause if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(:kill) { ScratchPad.record $! } + ScratchPad.recorded.to_s.should == "In dying thread" + end + end + + it "can be rescued by outer rescue clause when inner ensure clause raises exception" do + thread = Thread.new do + begin + begin + Thread.current.kill + ensure + raise "In dying thread" + end + rescue Exception + ScratchPad.record $! + end + :end_of_thread_block + end + + thread.value.should == :end_of_thread_block + ScratchPad.recorded.to_s.should == "In dying thread" + end + + it "is deferred if ensure clause does Thread.stop" do + ThreadSpecs.wakeup_dying_sleeping_thread(:kill) { Thread.stop; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + + # Hangs on 1.8.6.114 OS X, possibly also on Linux + quarantine! do + it "is deferred if ensure clause sleeps" do + ThreadSpecs.wakeup_dying_sleeping_thread(:kill) { sleep; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + end + + # This case occurred in JRuby where native threads are used to provide + # the same behavior as MRI green threads. Key to this issue was the fact + # that the thread which called #exit in its block was also being explicitly + # sent #join from outside the thread. The 100.times provides a certain + # probability that the deadlock will occur. It was sufficient to reliably + # reproduce the deadlock in JRuby. + it "does not deadlock when called from within the thread while being joined from without" do + 100.times do + t = Thread.new { Thread.stop; Thread.current.kill } + Thread.pass while t.status and t.status != "sleep" + t.wakeup.should == t + t.join.should == t + end + end end describe "Thread.kill" do diff --git a/spec/ruby/core/thread/shared/exit.rb b/spec/ruby/core/thread/shared/exit.rb deleted file mode 100644 index a294c3a4d6..0000000000 --- a/spec/ruby/core/thread/shared/exit.rb +++ /dev/null @@ -1,219 +0,0 @@ -describe :thread_exit, shared: true do - before :each do - ScratchPad.clear - end - - # This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19390874/job/wv1bsm8skd4e1pxl - # TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard. - platform_is_not :mingw do - - it "kills sleeping thread" do - sleeping_thread = Thread.new do - sleep - ScratchPad.record :after_sleep - end - Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep" - sleeping_thread.send(@method) - sleeping_thread.join - ScratchPad.recorded.should == nil - end - - it "kills current thread" do - thread = Thread.new do - Thread.current.send(@method) - ScratchPad.record :after_sleep - end - thread.join - ScratchPad.recorded.should == nil - end - - it "runs ensure clause" do - thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record :in_ensure_clause } - thread.join - ScratchPad.recorded.should == :in_ensure_clause - end - - it "runs nested ensure clauses" do - ScratchPad.record [] - @outer = Thread.new do - begin - @inner = Thread.new do - begin - sleep - ensure - ScratchPad << :inner_ensure_clause - end - end - sleep - ensure - ScratchPad << :outer_ensure_clause - @inner.send(@method) - @inner.join - end - end - Thread.pass while @outer.status and @outer.status != "sleep" - Thread.pass until @inner - Thread.pass while @inner.status and @inner.status != "sleep" - @outer.send(@method) - @outer.join - ScratchPad.recorded.should.include?(:inner_ensure_clause) - ScratchPad.recorded.should.include?(:outer_ensure_clause) - end - - it "does not set $!" do - thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record $! } - thread.join - ScratchPad.recorded.should == nil - end - - it "does not reset $!" do - ScratchPad.record [] - - exc = RuntimeError.new("foo") - thread = Thread.new do - begin - raise exc - ensure - ScratchPad << $! - begin - Thread.current.send(@method) - ensure - ScratchPad << $! - end - end - end - thread.join - ScratchPad.recorded.should == [exc, exc] - end - - it "cannot be rescued" do - thread = Thread.new do - begin - Thread.current.send(@method) - rescue Exception - ScratchPad.record :in_rescue - end - ScratchPad.record :end_of_thread_block - end - - thread.join - ScratchPad.recorded.should == nil - end - - it "kills the entire thread when a fiber is active" do - t = Thread.new do - Fiber.new do - sleep - end.resume - ScratchPad.record :fiber_resumed - end - Thread.pass while t.status and t.status != "sleep" - t.send(@method) - t.join - ScratchPad.recorded.should == nil - end - - it "kills other fibers of that thread without running their ensure clauses" do - t = Thread.new do - f = Fiber.new do - ScratchPad.record :fiber_resumed - begin - Fiber.yield - ensure - ScratchPad.record :fiber_ensure - end - end - f.resume - sleep - end - Thread.pass until t.stop? - t.send(@method) - t.join - ScratchPad.recorded.should == :fiber_resumed - end - - # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed - quarantine! do - it "killing dying running does nothing" do - in_ensure_clause = false - exit_loop = true - t = ThreadSpecs.dying_thread_ensures do - in_ensure_clause = true - loop { if exit_loop then break end } - ScratchPad.record :after_stop - end - - Thread.pass until in_ensure_clause == true - 10.times { t.send(@method); Thread.pass } - exit_loop = true - t.join - ScratchPad.recorded.should == :after_stop - end - end - - quarantine! do - - it "propagates inner exception to Thread.join if there is an outer ensure clause" do - thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { } - -> { thread.join }.should.raise(RuntimeError, "In dying thread") - end - - it "runs all outer ensure clauses even if inner ensure clause raises exception" do - ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record :in_outer_ensure_clause } - ScratchPad.recorded.should == :in_outer_ensure_clause - end - - it "sets $! in outer ensure clause if inner ensure clause raises exception" do - ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record $! } - ScratchPad.recorded.to_s.should == "In dying thread" - end - end - - it "can be rescued by outer rescue clause when inner ensure clause raises exception" do - thread = Thread.new do - begin - begin - Thread.current.send(@method) - ensure - raise "In dying thread" - end - rescue Exception - ScratchPad.record $! - end - :end_of_thread_block - end - - thread.value.should == :end_of_thread_block - ScratchPad.recorded.to_s.should == "In dying thread" - end - - it "is deferred if ensure clause does Thread.stop" do - ThreadSpecs.wakeup_dying_sleeping_thread(@method) { Thread.stop; ScratchPad.record :after_sleep } - ScratchPad.recorded.should == :after_sleep - end - - # Hangs on 1.8.6.114 OS X, possibly also on Linux - quarantine! do - it "is deferred if ensure clause sleeps" do - ThreadSpecs.wakeup_dying_sleeping_thread(@method) { sleep; ScratchPad.record :after_sleep } - ScratchPad.recorded.should == :after_sleep - end - end - - # This case occurred in JRuby where native threads are used to provide - # the same behavior as MRI green threads. Key to this issue was the fact - # that the thread which called #exit in its block was also being explicitly - # sent #join from outside the thread. The 100.times provides a certain - # probability that the deadlock will occur. It was sufficient to reliably - # reproduce the deadlock in JRuby. - it "does not deadlock when called from within the thread while being joined from without" do - 100.times do - t = Thread.new { Thread.stop; Thread.current.send(@method) } - Thread.pass while t.status and t.status != "sleep" - t.wakeup.should == t - t.join.should == t - end - end - - end # platform_is_not :mingw -end diff --git a/spec/ruby/core/thread/shared/start.rb b/spec/ruby/core/thread/shared/start.rb deleted file mode 100644 index c5d62ab098..0000000000 --- a/spec/ruby/core/thread/shared/start.rb +++ /dev/null @@ -1,41 +0,0 @@ -describe :thread_start, shared: true do - before :each do - ScratchPad.clear - end - - it "raises an ArgumentError if not passed a block" do - -> { - Thread.send(@method) - }.should.raise(ArgumentError) - end - - it "spawns a new Thread running the block" do - run = false - t = Thread.send(@method) { run = true } - t.should.is_a?(Thread) - t.join - - run.should == true - end - - it "respects Thread subclasses" do - c = Class.new(Thread) - t = c.send(@method) { } - t.should.is_a?(c) - - t.join - end - - it "does not call #initialize" do - c = Class.new(Thread) do - def initialize - ScratchPad.record :bad - end - end - - t = c.send(@method) { } - t.join - - ScratchPad.recorded.should == nil - end -end diff --git a/spec/ruby/core/thread/shared/to_s.rb b/spec/ruby/core/thread/shared/to_s.rb deleted file mode 100644 index 27e53ba4b8..0000000000 --- a/spec/ruby/core/thread/shared/to_s.rb +++ /dev/null @@ -1,53 +0,0 @@ -require_relative '../fixtures/classes' - -describe :thread_to_s, shared: true do - it "returns a description including file and line number" do - thread, line = Thread.new { "hello" }, __LINE__ - thread.join - thread.send(@method).should =~ /^#<Thread:([^ ]*?) #{Regexp.escape __FILE__}:#{line} \w+>$/ - end - - it "has a binary encoding" do - ThreadSpecs.status_of_current_thread.send(@method).encoding.should == Encoding::BINARY - end - - it "can check it's own status" do - ThreadSpecs.status_of_current_thread.send(@method).should.include?('run') - end - - it "describes a running thread" do - ThreadSpecs.status_of_running_thread.send(@method).should.include?('run') - end - - it "describes a sleeping thread" do - ThreadSpecs.status_of_sleeping_thread.send(@method).should.include?('sleep') - end - - it "describes a blocked thread" do - ThreadSpecs.status_of_blocked_thread.send(@method).should.include?('sleep') - end - - it "describes a completed thread" do - ThreadSpecs.status_of_completed_thread.send(@method).should.include?('dead') - end - - it "describes a killed thread" do - ThreadSpecs.status_of_killed_thread.send(@method).should.include?('dead') - end - - it "describes a thread with an uncaught exception" do - ThreadSpecs.status_of_thread_with_uncaught_exception.send(@method).should.include?('dead') - end - - it "describes a dying sleeping thread" do - ThreadSpecs.status_of_dying_sleeping_thread.send(@method).should.include?('sleep') - end - - it "reports aborting on a killed thread" do - ThreadSpecs.status_of_dying_running_thread.send(@method).should.include?('aborting') - end - - it "reports aborting on a killed thread after sleep" do - ThreadSpecs.status_of_dying_thread_after_sleep.send(@method).should.include?('aborting') - end -end diff --git a/spec/ruby/core/thread/start_spec.rb b/spec/ruby/core/thread/start_spec.rb index 3dd040f98b..a2ee52180b 100644 --- a/spec/ruby/core/thread/start_spec.rb +++ b/spec/ruby/core/thread/start_spec.rb @@ -1,9 +1,43 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/start' describe "Thread.start" do - describe "Thread.start" do - it_behaves_like :thread_start, :start + before :each do + ScratchPad.clear + end + + it "raises an ArgumentError if not passed a block" do + -> { + Thread.start + }.should.raise(ArgumentError) + end + + it "spawns a new Thread running the block" do + run = false + t = Thread.start { run = true } + t.should.is_a?(Thread) + t.join + + run.should == true + end + + it "respects Thread subclasses" do + c = Class.new(Thread) + t = c.start { } + t.should.is_a?(c) + + t.join + end + + it "does not call #initialize" do + c = Class.new(Thread) do + def initialize + ScratchPad.record :bad + end + end + + t = c.start { } + t.join + + ScratchPad.recorded.should == nil end end diff --git a/spec/ruby/core/thread/terminate_spec.rb b/spec/ruby/core/thread/terminate_spec.rb index cf6cab472b..68c431a0c0 100644 --- a/spec/ruby/core/thread/terminate_spec.rb +++ b/spec/ruby/core/thread/terminate_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/exit' describe "Thread#terminate" do - it_behaves_like :thread_exit, :terminate + it "is an alias of Thread#kill" do + Thread.instance_method(:terminate).should == Thread.instance_method(:kill) + end end diff --git a/spec/ruby/core/thread/to_s_spec.rb b/spec/ruby/core/thread/to_s_spec.rb index cb182a017f..2aef426de8 100644 --- a/spec/ruby/core/thread/to_s_spec.rb +++ b/spec/ruby/core/thread/to_s_spec.rb @@ -1,6 +1,54 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' +require_relative 'fixtures/classes' describe "Thread#to_s" do - it_behaves_like :thread_to_s, :to_s + it "returns a description including file and line number" do + thread, line = Thread.new { "hello" }, __LINE__ + thread.join + thread.to_s.should =~ /^#<Thread:([^ ]*?) #{Regexp.escape __FILE__}:#{line} \w+>$/ + end + + it "has a binary encoding" do + ThreadSpecs.status_of_current_thread.to_s.encoding.should == Encoding::BINARY + end + + it "can check it's own status" do + ThreadSpecs.status_of_current_thread.to_s.should.include?('run') + end + + it "describes a running thread" do + ThreadSpecs.status_of_running_thread.to_s.should.include?('run') + end + + it "describes a sleeping thread" do + ThreadSpecs.status_of_sleeping_thread.to_s.should.include?('sleep') + end + + it "describes a blocked thread" do + ThreadSpecs.status_of_blocked_thread.to_s.should.include?('sleep') + end + + it "describes a completed thread" do + ThreadSpecs.status_of_completed_thread.to_s.should.include?('dead') + end + + it "describes a killed thread" do + ThreadSpecs.status_of_killed_thread.to_s.should.include?('dead') + end + + it "describes a thread with an uncaught exception" do + ThreadSpecs.status_of_thread_with_uncaught_exception.to_s.should.include?('dead') + end + + it "describes a dying sleeping thread" do + ThreadSpecs.status_of_dying_sleeping_thread.to_s.should.include?('sleep') + end + + it "reports aborting on a killed thread" do + ThreadSpecs.status_of_dying_running_thread.to_s.should.include?('aborting') + end + + it "reports aborting on a killed thread after sleep" do + ThreadSpecs.status_of_dying_thread_after_sleep.to_s.should.include?('aborting') + end end diff --git a/spec/ruby/core/time/asctime_spec.rb b/spec/ruby/core/time/asctime_spec.rb index a41ee531e4..17e155787a 100644 --- a/spec/ruby/core/time/asctime_spec.rb +++ b/spec/ruby/core/time/asctime_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/asctime' describe "Time#asctime" do - it_behaves_like :time_asctime, :asctime + it "returns a canonical string representation of time" do + t = Time.now + t.asctime.should == t.strftime("%a %b %e %H:%M:%S %Y") + end end diff --git a/spec/ruby/core/time/ctime_spec.rb b/spec/ruby/core/time/ctime_spec.rb index 57e7cfd9ce..b609b03974 100644 --- a/spec/ruby/core/time/ctime_spec.rb +++ b/spec/ruby/core/time/ctime_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/asctime' describe "Time#ctime" do - it_behaves_like :time_asctime, :ctime + it "is an alias of Time#asctime" do + Time.instance_method(:ctime).should == Time.instance_method(:asctime) + end end diff --git a/spec/ruby/core/time/day_spec.rb b/spec/ruby/core/time/day_spec.rb index 895bcd7a86..3dec17644c 100644 --- a/spec/ruby/core/time/day_spec.rb +++ b/spec/ruby/core/time/day_spec.rb @@ -1,6 +1,17 @@ require_relative '../../spec_helper' -require_relative 'shared/day' describe "Time#day" do - it_behaves_like :time_day, :day + it "returns the day of the month (1..n) for a local Time" do + with_timezone("CET", 1) do + Time.local(1970, 1, 1).day.should == 1 + end + end + + it "returns the day of the month for a UTC Time" do + Time.utc(1970, 1, 1).day.should == 1 + end + + it "returns the day of the month for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).day.should == 1 + end end diff --git a/spec/ruby/core/time/dst_spec.rb b/spec/ruby/core/time/dst_spec.rb index 436240aae5..42daf86875 100644 --- a/spec/ruby/core/time/dst_spec.rb +++ b/spec/ruby/core/time/dst_spec.rb @@ -1,6 +1,10 @@ require_relative '../../spec_helper' -require_relative 'shared/isdst' describe "Time#dst?" do - it_behaves_like :time_isdst, :dst? + it "returns whether time is during daylight saving time" do + with_timezone("America/Los_Angeles") do + Time.local(2007, 9, 9, 0, 0, 0).dst?.should == true + Time.local(2007, 1, 9, 0, 0, 0).dst?.should == false + end + end end diff --git a/spec/ruby/core/time/getgm_spec.rb b/spec/ruby/core/time/getgm_spec.rb index b5d45b1d9f..7698156c8c 100644 --- a/spec/ruby/core/time/getgm_spec.rb +++ b/spec/ruby/core/time/getgm_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/getgm' describe "Time#getgm" do - it_behaves_like :time_getgm, :getgm + it "is an alias of Time#getutc" do + Time.instance_method(:getgm).should == Time.instance_method(:getutc) + end end diff --git a/spec/ruby/core/time/getutc_spec.rb b/spec/ruby/core/time/getutc_spec.rb index 0cd9c17b00..1d49059a71 100644 --- a/spec/ruby/core/time/getutc_spec.rb +++ b/spec/ruby/core/time/getutc_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' -require_relative 'shared/getgm' describe "Time#getutc" do - it_behaves_like :time_getgm, :getutc + it "returns a new time which is the utc representation of time" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("CST", -6) do + t = Time.local(2007, 1, 9, 6, 0, 0) + t.getutc.should == Time.gm(2007, 1, 9, 12, 0, 0) + end + end end diff --git a/spec/ruby/core/time/gm_spec.rb b/spec/ruby/core/time/gm_spec.rb index 26dffbcedc..fbabede6ba 100644 --- a/spec/ruby/core/time/gm_spec.rb +++ b/spec/ruby/core/time/gm_spec.rb @@ -1,10 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/gm' -require_relative 'shared/time_params' describe "Time.gm" do - it_behaves_like :time_gm, :gm - it_behaves_like :time_params, :gm - it_behaves_like :time_params_10_arg, :gm - it_behaves_like :time_params_microseconds, :gm + it "is an alias of Time.utc" do + Time.method(:gm).should == Time.method(:utc) + end end diff --git a/spec/ruby/core/time/gmt_offset_spec.rb b/spec/ruby/core/time/gmt_offset_spec.rb index df417e6e4e..1769981753 100644 --- a/spec/ruby/core/time/gmt_offset_spec.rb +++ b/spec/ruby/core/time/gmt_offset_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/gmt_offset' describe "Time#gmt_offset" do - it_behaves_like :time_gmt_offset, :gmt_offset + it "is an alias of Time#utc_offset" do + Time.instance_method(:gmt_offset).should == Time.instance_method(:utc_offset) + end end diff --git a/spec/ruby/core/time/gmt_spec.rb b/spec/ruby/core/time/gmt_spec.rb index 840f59e0e8..38e98cc43c 100644 --- a/spec/ruby/core/time/gmt_spec.rb +++ b/spec/ruby/core/time/gmt_spec.rb @@ -1,8 +1,7 @@ require_relative '../../spec_helper' describe "Time#gmt?" do - it "returns true if time represents a time in UTC (GMT)" do - Time.now.should_not.gmt? - Time.now.gmtime.should.gmt? + it "is an alias of Time#utc?" do + Time.instance_method(:gmt?).should == Time.instance_method(:utc?) end end diff --git a/spec/ruby/core/time/gmtime_spec.rb b/spec/ruby/core/time/gmtime_spec.rb index d965cd541d..e7e1d4ffb2 100644 --- a/spec/ruby/core/time/gmtime_spec.rb +++ b/spec/ruby/core/time/gmtime_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/gmtime' describe "Time#gmtime" do - it_behaves_like :time_gmtime, :gmtime + it "is an alias of Time#utc" do + Time.instance_method(:gmtime).should == Time.instance_method(:utc) + end end diff --git a/spec/ruby/core/time/gmtoff_spec.rb b/spec/ruby/core/time/gmtoff_spec.rb index fa28520e9e..c7d801a681 100644 --- a/spec/ruby/core/time/gmtoff_spec.rb +++ b/spec/ruby/core/time/gmtoff_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/gmt_offset' describe "Time#gmtoff" do - it_behaves_like :time_gmt_offset, :gmtoff + it "is an alias of Time#utc_offset" do + Time.instance_method(:gmtoff).should == Time.instance_method(:utc_offset) + end end diff --git a/spec/ruby/core/time/isdst_spec.rb b/spec/ruby/core/time/isdst_spec.rb index 173230ca07..759953cca7 100644 --- a/spec/ruby/core/time/isdst_spec.rb +++ b/spec/ruby/core/time/isdst_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/isdst' describe "Time#isdst" do - it_behaves_like :time_isdst, :isdst + it "is an alias of Time#dst?" do + Time.instance_method(:isdst).should == Time.instance_method(:dst?) + end end diff --git a/spec/ruby/core/time/iso8601_spec.rb b/spec/ruby/core/time/iso8601_spec.rb index ad60c3bb32..a6efc57b28 100644 --- a/spec/ruby/core/time/iso8601_spec.rb +++ b/spec/ruby/core/time/iso8601_spec.rb @@ -1,6 +1,33 @@ require_relative '../../spec_helper' -require_relative 'shared/xmlschema' describe "Time#iso8601" do - it_behaves_like :time_xmlschema, :iso8601 + ruby_version_is "3.4" do + it "generates ISO-8601 strings in Z for UTC times" do + t = Time.utc(1985, 4, 12, 23, 20, 50, 521245) + t.iso8601.should == "1985-04-12T23:20:50Z" + t.iso8601(2).should == "1985-04-12T23:20:50.52Z" + t.iso8601(9).should == "1985-04-12T23:20:50.521245000Z" + end + + it "generates ISO-8601 string with timezone offset for non-UTC times" do + t = Time.new(1985, 4, 12, 23, 20, 50, "+02:00") + t.iso8601.should == "1985-04-12T23:20:50+02:00" + t.iso8601(2).should == "1985-04-12T23:20:50.00+02:00" + end + + it "year is always at least 4 digits" do + t = Time.utc(12, 4, 12) + t.iso8601.should == "0012-04-12T00:00:00Z" + end + + it "year can be more than 4 digits" do + t = Time.utc(40_000, 4, 12) + t.iso8601.should == "40000-04-12T00:00:00Z" + end + + it "year can be negative" do + t = Time.utc(-2000, 4, 12) + t.iso8601.should == "-2000-04-12T00:00:00Z" + end + end end diff --git a/spec/ruby/core/time/mday_spec.rb b/spec/ruby/core/time/mday_spec.rb index 3c21939890..78021785f9 100644 --- a/spec/ruby/core/time/mday_spec.rb +++ b/spec/ruby/core/time/mday_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/day' describe "Time#mday" do - it_behaves_like :time_day, :mday + it "is an alias of Time#day" do + Time.instance_method(:mday).should == Time.instance_method(:day) + end end diff --git a/spec/ruby/core/time/mktime_spec.rb b/spec/ruby/core/time/mktime_spec.rb index 78a6a6e772..83bf12293a 100644 --- a/spec/ruby/core/time/mktime_spec.rb +++ b/spec/ruby/core/time/mktime_spec.rb @@ -1,11 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/local' -require_relative 'shared/time_params' describe "Time.mktime" do - it_behaves_like :time_local, :mktime - it_behaves_like :time_local_10_arg, :mktime - it_behaves_like :time_params, :mktime - it_behaves_like :time_params_10_arg, :mktime - it_behaves_like :time_params_microseconds, :mktime + it "is an alias of Time.local" do + Time.method(:mktime).should == Time.method(:local) + end end diff --git a/spec/ruby/core/time/mon_spec.rb b/spec/ruby/core/time/mon_spec.rb index f41b39648b..d57549dadd 100644 --- a/spec/ruby/core/time/mon_spec.rb +++ b/spec/ruby/core/time/mon_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/month' describe "Time#mon" do - it_behaves_like :time_month, :mon + it "is an alias of Time#month" do + Time.instance_method(:mon).should == Time.instance_method(:month) + end end diff --git a/spec/ruby/core/time/month_spec.rb b/spec/ruby/core/time/month_spec.rb index 81e20384ab..eae0e85acd 100644 --- a/spec/ruby/core/time/month_spec.rb +++ b/spec/ruby/core/time/month_spec.rb @@ -1,6 +1,17 @@ require_relative '../../spec_helper' -require_relative 'shared/month' describe "Time#month" do - it_behaves_like :time_month, :month + it "returns the month of the year for a local Time" do + with_timezone("CET", 1) do + Time.local(1970, 1).month.should == 1 + end + end + + it "returns the month of the year for a UTC Time" do + Time.utc(1970, 1).month.should == 1 + end + + it "returns the four digit year for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).month.should == 1 + end end diff --git a/spec/ruby/core/time/shared/asctime.rb b/spec/ruby/core/time/shared/asctime.rb deleted file mode 100644 index d096666863..0000000000 --- a/spec/ruby/core/time/shared/asctime.rb +++ /dev/null @@ -1,6 +0,0 @@ -describe :time_asctime, shared: true do - it "returns a canonical string representation of time" do - t = Time.now - t.send(@method).should == t.strftime("%a %b %e %H:%M:%S %Y") - end -end diff --git a/spec/ruby/core/time/shared/day.rb b/spec/ruby/core/time/shared/day.rb deleted file mode 100644 index 472dc959c1..0000000000 --- a/spec/ruby/core/time/shared/day.rb +++ /dev/null @@ -1,15 +0,0 @@ -describe :time_day, shared: true do - it "returns the day of the month (1..n) for a local Time" do - with_timezone("CET", 1) do - Time.local(1970, 1, 1).send(@method).should == 1 - end - end - - it "returns the day of the month for a UTC Time" do - Time.utc(1970, 1, 1).send(@method).should == 1 - end - - it "returns the day of the month for a Time with a fixed offset" do - Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1 - end -end diff --git a/spec/ruby/core/time/shared/getgm.rb b/spec/ruby/core/time/shared/getgm.rb deleted file mode 100644 index 3576365772..0000000000 --- a/spec/ruby/core/time/shared/getgm.rb +++ /dev/null @@ -1,9 +0,0 @@ -describe :time_getgm, shared: true do - it "returns a new time which is the utc representation of time" do - # Testing with America/Regina here because it doesn't have DST. - with_timezone("CST", -6) do - t = Time.local(2007, 1, 9, 6, 0, 0) - t.send(@method).should == Time.gm(2007, 1, 9, 12, 0, 0) - end - end -end diff --git a/spec/ruby/core/time/shared/gm.rb b/spec/ruby/core/time/shared/gm.rb deleted file mode 100644 index 0ee602c837..0000000000 --- a/spec/ruby/core/time/shared/gm.rb +++ /dev/null @@ -1,70 +0,0 @@ -describe :time_gm, shared: true do - it "creates a time based on given values, interpreted as UTC (GMT)" do - Time.send(@method, 2000,"jan",1,20,15,1).inspect.should == "2000-01-01 20:15:01 UTC" - end - - it "creates a time based on given C-style gmtime arguments, interpreted as UTC (GMT)" do - time = Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored) - time.inspect.should == "2000-01-01 20:15:01 UTC" - end - - it "interprets pre-Gregorian reform dates using Gregorian proleptic calendar" do - Time.send(@method, 1582, 10, 4, 12).to_i.should == -12220200000 # 2299150j - end - - it "interprets Julian-Gregorian gap dates using Gregorian proleptic calendar" do - Time.send(@method, 1582, 10, 14, 12).to_i.should == -12219336000 # 2299160j - end - - it "interprets post-Gregorian reform dates using Gregorian calendar" do - Time.send(@method, 1582, 10, 15, 12).to_i.should == -12219249600 # 2299161j - end - - it "handles fractional usec close to rounding limit" do - time = Time.send(@method, 2000, 1, 1, 12, 30, 0, 9999r/10000) - - time.usec.should == 0 - time.nsec.should == 999 - end - - guard -> { - with_timezone 'right/UTC' do - (Time.gm(1972, 6, 30, 23, 59, 59) + 1).sec == 60 - end - } do - it "handles real leap seconds in zone 'right/UTC'" do - with_timezone 'right/UTC' do - time = Time.send(@method, 1972, 6, 30, 23, 59, 60) - - time.sec.should == 60 - time.min.should == 59 - time.hour.should == 23 - time.day.should == 30 - time.month.should == 6 - end - end - end - - it "handles bad leap seconds by carrying values forward" do - with_timezone 'UTC' do - time = Time.send(@method, 2017, 7, 5, 23, 59, 60) - time.sec.should == 0 - time.min.should == 0 - time.hour.should == 0 - time.day.should == 6 - time.month.should == 7 - end - end - - it "handles a value of 60 for seconds by carrying values forward in zone 'UTC'" do - with_timezone 'UTC' do - time = Time.send(@method, 1972, 6, 30, 23, 59, 60) - - time.sec.should == 0 - time.min.should == 0 - time.hour.should == 0 - time.day.should == 1 - time.month.should == 7 - end - end -end diff --git a/spec/ruby/core/time/shared/gmt_offset.rb b/spec/ruby/core/time/shared/gmt_offset.rb deleted file mode 100644 index 839566c249..0000000000 --- a/spec/ruby/core/time/shared/gmt_offset.rb +++ /dev/null @@ -1,59 +0,0 @@ -describe :time_gmt_offset, shared: true do - it "returns the offset in seconds between the timezone of time and UTC" do - with_timezone("AST", 3) do - Time.new.send(@method).should == 10800 - end - end - - it "returns 0 when the date is UTC" do - with_timezone("AST", 3) do - Time.new.utc.send(@method).should == 0 - end - end - - platform_is_not :windows do - it "returns the correct offset for US Eastern time zone around daylight savings time change" do - # "2010-03-14 01:59:59 -0500" + 1 ==> "2010-03-14 03:00:00 -0400" - with_timezone("EST5EDT") do - t = Time.local(2010,3,14,1,59,59) - t.send(@method).should == -5*60*60 - (t + 1).send(@method).should == -4*60*60 - end - end - - it "returns the correct offset for Hawaii around daylight savings time change" do - # "2010-03-14 01:59:59 -1000" + 1 ==> "2010-03-14 02:00:00 -1000" - with_timezone("Pacific/Honolulu") do - t = Time.local(2010,3,14,1,59,59) - t.send(@method).should == -10*60*60 - (t + 1).send(@method).should == -10*60*60 - end - end - - it "returns the correct offset for New Zealand around daylight savings time change" do - # "2010-04-04 02:59:59 +1300" + 1 ==> "2010-04-04 02:00:00 +1200" - with_timezone("Pacific/Auckland") do - t = Time.local(2010,4,4,1,59,59) + (60 * 60) - t.send(@method).should == 13*60*60 - (t + 1).send(@method).should == 12*60*60 - end - end - end - - it "returns offset as Rational" do - Time.new(2010,4,4,1,59,59,7245).send(@method).should == 7245 - Time.new(2010,4,4,1,59,59,7245.5).send(@method).should == Rational(14491,2) - end - - context 'given positive offset' do - it 'returns a positive offset' do - Time.new(2013,3,17,nil,nil,nil,"+03:00").send(@method).should == 10800 - end - end - - context 'given negative offset' do - it 'returns a negative offset' do - Time.new(2013,3,17,nil,nil,nil,"-03:00").send(@method).should == -10800 - end - end -end diff --git a/spec/ruby/core/time/shared/gmtime.rb b/spec/ruby/core/time/shared/gmtime.rb deleted file mode 100644 index aa76b436cc..0000000000 --- a/spec/ruby/core/time/shared/gmtime.rb +++ /dev/null @@ -1,40 +0,0 @@ -describe :time_gmtime, shared: true do - it "converts self to UTC, modifying the receiver" do - # Testing with America/Regina here because it doesn't have DST. - with_timezone("CST", -6) do - t = Time.local(2007, 1, 9, 6, 0, 0) - t.send(@method) - # Time#== compensates for time zones, so check all parts separately - t.year.should == 2007 - t.month.should == 1 - t.mday.should == 9 - t.hour.should == 12 - t.min.should == 0 - t.sec.should == 0 - t.zone.should == "UTC" - end - end - - it "returns self" do - with_timezone("CST", -6) do - t = Time.local(2007, 1, 9, 12, 0, 0) - t.send(@method).should.equal?(t) - end - end - - describe "on a frozen time" do - it "does not raise an error if already in UTC" do - time = Time.gm(2007, 1, 9, 12, 0, 0) - time.freeze - time.send(@method).should.equal?(time) - end - - it "raises a FrozenError if the time is not UTC" do - with_timezone("CST", -6) do - time = Time.now - time.freeze - -> { time.send(@method) }.should.raise(FrozenError) - end - end - end -end diff --git a/spec/ruby/core/time/shared/isdst.rb b/spec/ruby/core/time/shared/isdst.rb deleted file mode 100644 index bc6d139230..0000000000 --- a/spec/ruby/core/time/shared/isdst.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :time_isdst, shared: true do - it "dst? returns whether time is during daylight saving time" do - with_timezone("America/Los_Angeles") do - Time.local(2007, 9, 9, 0, 0, 0).send(@method).should == true - Time.local(2007, 1, 9, 0, 0, 0).send(@method).should == false - end - end -end diff --git a/spec/ruby/core/time/shared/month.rb b/spec/ruby/core/time/shared/month.rb deleted file mode 100644 index 31ca679557..0000000000 --- a/spec/ruby/core/time/shared/month.rb +++ /dev/null @@ -1,15 +0,0 @@ -describe :time_month, shared: true do - it "returns the month of the year for a local Time" do - with_timezone("CET", 1) do - Time.local(1970, 1).send(@method).should == 1 - end - end - - it "returns the month of the year for a UTC Time" do - Time.utc(1970, 1).send(@method).should == 1 - end - - it "returns the four digit year for a Time with a fixed offset" do - Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1 - end -end diff --git a/spec/ruby/core/time/shared/to_i.rb b/spec/ruby/core/time/shared/to_i.rb deleted file mode 100644 index 06c966b708..0000000000 --- a/spec/ruby/core/time/shared/to_i.rb +++ /dev/null @@ -1,16 +0,0 @@ -describe :time_to_i, shared: true do - it "returns the value of time as an integer number of seconds since epoch" do - Time.at(0).send(@method).should == 0 - end - - it "doesn't return an actual number of seconds in time" do - Time.at(65.5).send(@method).should == 65 - end - - it "rounds fractional seconds toward zero" do - t = Time.utc(1960, 1, 1, 0, 0, 0, 999_999) - - t.to_f.to_i.should == -315619199 - t.to_i.should == -315619200 - end -end diff --git a/spec/ruby/core/time/shared/xmlschema.rb b/spec/ruby/core/time/shared/xmlschema.rb deleted file mode 100644 index d68c18df36..0000000000 --- a/spec/ruby/core/time/shared/xmlschema.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe :time_xmlschema, shared: true do - ruby_version_is "3.4" do - it "generates ISO-8601 strings in Z for UTC times" do - t = Time.utc(1985, 4, 12, 23, 20, 50, 521245) - t.send(@method).should == "1985-04-12T23:20:50Z" - t.send(@method, 2).should == "1985-04-12T23:20:50.52Z" - t.send(@method, 9).should == "1985-04-12T23:20:50.521245000Z" - end - - it "generates ISO-8601 string with timeone offset for non-UTC times" do - t = Time.new(1985, 4, 12, 23, 20, 50, "+02:00") - t.send(@method).should == "1985-04-12T23:20:50+02:00" - t.send(@method, 2).should == "1985-04-12T23:20:50.00+02:00" - end - - it "year is always at least 4 digits" do - t = Time.utc(12, 4, 12) - t.send(@method).should == "0012-04-12T00:00:00Z" - end - - it "year can be more than 4 digits" do - t = Time.utc(40_000, 4, 12) - t.send(@method).should == "40000-04-12T00:00:00Z" - end - - it "year can be negative" do - t = Time.utc(-2000, 4, 12) - t.send(@method).should == "-2000-04-12T00:00:00Z" - end - end -end diff --git a/spec/ruby/core/time/to_i_spec.rb b/spec/ruby/core/time/to_i_spec.rb index 54929d1e18..00c4215d31 100644 --- a/spec/ruby/core/time/to_i_spec.rb +++ b/spec/ruby/core/time/to_i_spec.rb @@ -1,6 +1,18 @@ require_relative '../../spec_helper' -require_relative 'shared/to_i' describe "Time#to_i" do - it_behaves_like :time_to_i, :to_i + it "returns the value of time as an integer number of seconds since epoch" do + Time.at(0).to_i.should == 0 + end + + it "doesn't return an actual number of seconds in time" do + Time.at(65.5).to_i.should == 65 + end + + it "rounds fractional seconds toward zero" do + t = Time.utc(1960, 1, 1, 0, 0, 0, 999_999) + + t.to_f.to_i.should == -315619199 + t.to_i.should == -315619200 + end end diff --git a/spec/ruby/core/time/tv_nsec_spec.rb b/spec/ruby/core/time/tv_nsec_spec.rb index feb7998b16..802138bf3a 100644 --- a/spec/ruby/core/time/tv_nsec_spec.rb +++ b/spec/ruby/core/time/tv_nsec_spec.rb @@ -1,5 +1,7 @@ require_relative '../../spec_helper' describe "Time#tv_nsec" do - it "needs to be reviewed for spec completeness" + it "is an alias of Time#nsec" do + Time.instance_method(:tv_nsec).should == Time.instance_method(:nsec) + end end diff --git a/spec/ruby/core/time/tv_sec_spec.rb b/spec/ruby/core/time/tv_sec_spec.rb index f83e1fbfdd..21bdb53ee6 100644 --- a/spec/ruby/core/time/tv_sec_spec.rb +++ b/spec/ruby/core/time/tv_sec_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_i' describe "Time#tv_sec" do - it_behaves_like :time_to_i, :tv_sec + it "is an alias of Time#to_i" do + Time.instance_method(:tv_sec).should == Time.instance_method(:to_i) + end end diff --git a/spec/ruby/core/time/tv_usec_spec.rb b/spec/ruby/core/time/tv_usec_spec.rb index f0de4f4a9c..e922ee5625 100644 --- a/spec/ruby/core/time/tv_usec_spec.rb +++ b/spec/ruby/core/time/tv_usec_spec.rb @@ -1,5 +1,7 @@ require_relative '../../spec_helper' describe "Time#tv_usec" do - it "needs to be reviewed for spec completeness" + it "is an alias of Time#usec" do + Time.instance_method(:tv_usec).should == Time.instance_method(:usec) + end end diff --git a/spec/ruby/core/time/utc_offset_spec.rb b/spec/ruby/core/time/utc_offset_spec.rb index 17c031b8c6..8d2fff2012 100644 --- a/spec/ruby/core/time/utc_offset_spec.rb +++ b/spec/ruby/core/time/utc_offset_spec.rb @@ -1,6 +1,61 @@ require_relative '../../spec_helper' -require_relative 'shared/gmt_offset' describe "Time#utc_offset" do - it_behaves_like :time_gmt_offset, :utc_offset + it "returns the offset in seconds between the timezone of time and UTC" do + with_timezone("AST", 3) do + Time.new.utc_offset.should == 10800 + end + end + + it "returns 0 when the date is UTC" do + with_timezone("AST", 3) do + Time.new.utc.utc_offset.should == 0 + end + end + + platform_is_not :windows do + it "returns the correct offset for US Eastern time zone around daylight savings time change" do + # "2010-03-14 01:59:59 -0500" + 1 ==> "2010-03-14 03:00:00 -0400" + with_timezone("EST5EDT") do + t = Time.local(2010,3,14,1,59,59) + t.utc_offset.should == -5*60*60 + (t + 1).utc_offset.should == -4*60*60 + end + end + + it "returns the correct offset for Hawaii around daylight savings time change" do + # "2010-03-14 01:59:59 -1000" + 1 ==> "2010-03-14 02:00:00 -1000" + with_timezone("Pacific/Honolulu") do + t = Time.local(2010,3,14,1,59,59) + t.utc_offset.should == -10*60*60 + (t + 1).utc_offset.should == -10*60*60 + end + end + + it "returns the correct offset for New Zealand around daylight savings time change" do + # "2010-04-04 02:59:59 +1300" + 1 ==> "2010-04-04 02:00:00 +1200" + with_timezone("Pacific/Auckland") do + t = Time.local(2010,4,4,1,59,59) + (60 * 60) + t.utc_offset.should == 13*60*60 + (t + 1).utc_offset.should == 12*60*60 + end + end + end + + it "returns offset as Rational" do + Time.new(2010,4,4,1,59,59,7245).utc_offset.should == 7245 + Time.new(2010,4,4,1,59,59,7245.5).utc_offset.should == Rational(14491,2) + end + + context 'given positive offset' do + it 'returns a positive offset' do + Time.new(2013,3,17,nil,nil,nil,"+03:00").utc_offset.should == 10800 + end + end + + context 'given negative offset' do + it 'returns a negative offset' do + Time.new(2013,3,17,nil,nil,nil,"-03:00").utc_offset.should == -10800 + end + end end diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb index ab3c0df657..35c1daa9e5 100644 --- a/spec/ruby/core/time/utc_spec.rb +++ b/spec/ruby/core/time/utc_spec.rb @@ -1,6 +1,4 @@ require_relative '../../spec_helper' -require_relative 'shared/gm' -require_relative 'shared/gmtime' require_relative 'shared/time_params' describe "Time#utc?" do @@ -11,7 +9,7 @@ describe "Time#utc?" do it "treats time as UTC what was created in different ways" do Time.now.utc.utc?.should == true - Time.now.gmtime.utc?.should == true + Time.now.utc.utc?.should == true Time.now.getgm.utc?.should == true Time.now.getutc.utc?.should == true Time.utc(2022).utc?.should == true @@ -55,12 +53,117 @@ describe "Time#utc?" do end describe "Time.utc" do - it_behaves_like :time_gm, :utc it_behaves_like :time_params, :utc it_behaves_like :time_params_10_arg, :utc it_behaves_like :time_params_microseconds, :utc + + it "creates a time based on given values, interpreted as UTC (GMT)" do + Time.utc(2000,"jan",1,20,15,1).inspect.should == "2000-01-01 20:15:01 UTC" + end + + it "creates a time based on given C-style gmtime arguments, interpreted as UTC (GMT)" do + time = Time.utc(1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored) + time.inspect.should == "2000-01-01 20:15:01 UTC" + end + + it "interprets pre-Gregorian reform dates using Gregorian proleptic calendar" do + Time.utc(1582, 10, 4, 12).to_i.should == -12220200000 # 2299150j + end + + it "interprets Julian-Gregorian gap dates using Gregorian proleptic calendar" do + Time.utc(1582, 10, 14, 12).to_i.should == -12219336000 # 2299160j + end + + it "interprets post-Gregorian reform dates using Gregorian calendar" do + Time.utc(1582, 10, 15, 12).to_i.should == -12219249600 # 2299161j + end + + it "handles fractional usec close to rounding limit" do + time = Time.utc(2000, 1, 1, 12, 30, 0, 9999r/10000) + + time.usec.should == 0 + time.nsec.should == 999 + end + + guard -> { + with_timezone 'right/UTC' do + (Time.utc(1972, 6, 30, 23, 59, 59) + 1).sec == 60 + end + } do + it "handles real leap seconds in zone 'right/UTC'" do + with_timezone 'right/UTC' do + time = Time.utc(1972, 6, 30, 23, 59, 60) + + time.sec.should == 60 + time.min.should == 59 + time.hour.should == 23 + time.day.should == 30 + time.month.should == 6 + end + end + end + + it "handles bad leap seconds by carrying values forward" do + with_timezone 'UTC' do + time = Time.utc(2017, 7, 5, 23, 59, 60) + time.sec.should == 0 + time.min.should == 0 + time.hour.should == 0 + time.day.should == 6 + time.month.should == 7 + end + end + + it "handles a value of 60 for seconds by carrying values forward in zone 'UTC'" do + with_timezone 'UTC' do + time = Time.utc(1972, 6, 30, 23, 59, 60) + + time.sec.should == 0 + time.min.should == 0 + time.hour.should == 0 + time.day.should == 1 + time.month.should == 7 + end + end end describe "Time#utc" do - it_behaves_like :time_gmtime, :utc + it "converts self to UTC, modifying the receiver" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("CST", -6) do + t = Time.local(2007, 1, 9, 6, 0, 0) + t.utc + # Time#== compensates for time zones, so check all parts separately + t.year.should == 2007 + t.month.should == 1 + t.mday.should == 9 + t.hour.should == 12 + t.min.should == 0 + t.sec.should == 0 + t.zone.should == "UTC" + end + end + + it "returns self" do + with_timezone("CST", -6) do + t = Time.local(2007, 1, 9, 12, 0, 0) + t.utc.should.equal?(t) + end + end + + describe "on a frozen time" do + it "does not raise an error if already in UTC" do + time = Time.gm(2007, 1, 9, 12, 0, 0) + time.freeze + time.utc.should.equal?(time) + end + + it "raises a FrozenError if the time is not UTC" do + with_timezone("CST", -6) do + time = Time.now + time.freeze + -> { time.utc }.should.raise(FrozenError) + end + end + end end diff --git a/spec/ruby/core/time/xmlschema_spec.rb b/spec/ruby/core/time/xmlschema_spec.rb index bdf1dc7923..6a26861a45 100644 --- a/spec/ruby/core/time/xmlschema_spec.rb +++ b/spec/ruby/core/time/xmlschema_spec.rb @@ -1,6 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/xmlschema' describe "Time#xmlschema" do - it_behaves_like :time_xmlschema, :xmlschema + ruby_version_is "3.4" do + it "is an alias of Time#iso8601" do + Time.instance_method(:xmlschema).should == Time.instance_method(:iso8601) + end + end end diff --git a/spec/ruby/core/true/inspect_spec.rb b/spec/ruby/core/true/inspect_spec.rb index 09d1914856..b9f5390b5c 100644 --- a/spec/ruby/core/true/inspect_spec.rb +++ b/spec/ruby/core/true/inspect_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' describe "TrueClass#inspect" do - it "returns the string 'true'" do - true.inspect.should == "true" + it "is an alias of TrueClass#to_s" do + true.method(:inspect).should == true.method(:to_s) end end diff --git a/spec/ruby/core/unboundmethod/eql_spec.rb b/spec/ruby/core/unboundmethod/eql_spec.rb index cf2c6b5aae..3b299d047a 100644 --- a/spec/ruby/core/unboundmethod/eql_spec.rb +++ b/spec/ruby/core/unboundmethod/eql_spec.rb @@ -1,5 +1,7 @@ require_relative '../../spec_helper' describe "UnboundMethod#eql?" do - it "needs to be reviewed for spec completeness" + it "is an alias of UnboundMethod#==" do + UnboundMethod.instance_method(:eql?).should == UnboundMethod.instance_method(:==) + end end diff --git a/spec/ruby/core/unboundmethod/inspect_spec.rb b/spec/ruby/core/unboundmethod/inspect_spec.rb index 3abed94f7f..b0fcfd00ea 100644 --- a/spec/ruby/core/unboundmethod/inspect_spec.rb +++ b/spec/ruby/core/unboundmethod/inspect_spec.rb @@ -1,9 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/to_s' -require_relative '../method/shared/aliased_inspect' describe "UnboundMethod#inspect" do - it_behaves_like :unboundmethod_to_s, :inspect - it_behaves_like :method_to_s_aliased, :inspect, -> meth { meth.unbind } + it "is an alias of UnboundMethod#to_s" do + UnboundMethod.instance_method(:inspect).should == UnboundMethod.instance_method(:to_s) + end end diff --git a/spec/ruby/core/unboundmethod/shared/to_s.rb b/spec/ruby/core/unboundmethod/shared/to_s.rb deleted file mode 100644 index 848c1eba2e..0000000000 --- a/spec/ruby/core/unboundmethod/shared/to_s.rb +++ /dev/null @@ -1,33 +0,0 @@ -require_relative '../../../spec_helper' -require_relative '../fixtures/classes' - -describe :unboundmethod_to_s, shared: true do - before :each do - @from_module = UnboundMethodSpecs::Methods.instance_method(:from_mod) - @from_method = UnboundMethodSpecs::Methods.new.method(:from_mod).unbind - end - - it "returns a String" do - @from_module.send(@method).should.is_a?(String) - @from_method.send(@method).should.is_a?(String) - end - - it "the String reflects that this is an UnboundMethod object" do - @from_module.send(@method).should =~ /\bUnboundMethod\b/ - @from_method.send(@method).should =~ /\bUnboundMethod\b/ - end - - it "the String shows the method name, Module defined in and Module extracted from" do - @from_module.send(@method).should =~ /\bfrom_mod\b/ - @from_module.send(@method).should =~ /\bUnboundMethodSpecs::Mod\b/ - end - - it "returns a String including all details" do - @from_module.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod" - @from_method.send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod" - end - - it "does not show the defining module if it is the same as the origin" do - UnboundMethodSpecs::A.instance_method(:baz).send(@method).should.start_with? "#<UnboundMethod: UnboundMethodSpecs::A#baz" - end -end diff --git a/spec/ruby/core/unboundmethod/to_s_spec.rb b/spec/ruby/core/unboundmethod/to_s_spec.rb index 615d88675b..b90f315c3a 100644 --- a/spec/ruby/core/unboundmethod/to_s_spec.rb +++ b/spec/ruby/core/unboundmethod/to_s_spec.rb @@ -1,9 +1,36 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/to_s' require_relative '../method/shared/aliased_inspect' describe "UnboundMethod#to_s" do - it_behaves_like :unboundmethod_to_s, :to_s it_behaves_like :method_to_s_aliased, :to_s, -> meth { meth.unbind } + + before :each do + @from_module = UnboundMethodSpecs::Methods.instance_method(:from_mod) + @from_method = UnboundMethodSpecs::Methods.new.method(:from_mod).unbind + end + + it "returns a String" do + @from_module.to_s.should.is_a?(String) + @from_method.to_s.should.is_a?(String) + end + + it "the String reflects that this is an UnboundMethod object" do + @from_module.to_s.should =~ /\bUnboundMethod\b/ + @from_method.to_s.should =~ /\bUnboundMethod\b/ + end + + it "the String shows the method name, Module defined in and Module extracted from" do + @from_module.to_s.should =~ /\bfrom_mod\b/ + @from_module.to_s.should =~ /\bUnboundMethodSpecs::Mod\b/ + end + + it "returns a String including all details" do + @from_module.to_s.should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod" + @from_method.to_s.should.start_with? "#<UnboundMethod: UnboundMethodSpecs::Mod#from_mod" + end + + it "does not show the defining module if it is the same as the origin" do + UnboundMethodSpecs::A.instance_method(:baz).to_s.should.start_with? "#<UnboundMethod: UnboundMethodSpecs::A#baz" + end end diff --git a/spec/ruby/language/def_spec.rb b/spec/ruby/language/def_spec.rb index 82c89a0d08..a589b4750b 100644 --- a/spec/ruby/language/def_spec.rb +++ b/spec/ruby/language/def_spec.rb @@ -306,32 +306,21 @@ end describe "Redefining a singleton method" do it "does not inherit a previously set visibility" do o = Object.new + sc = o.singleton_class - class << o; private; def foo; end; end; - - class << o; should have_private_instance_method(:foo); end - - class << o; def foo; end; end; - - class << o; should_not have_private_instance_method(:foo); end - class << o; should have_instance_method(:foo); end - - end -end - -describe "Redefining a singleton method" do - it "does not inherit a previously set visibility" do - o = Object.new - - class << o; private; def foo; end; end; - - class << o; should have_private_instance_method(:foo); end + class << o + private + def foo; end + end - class << o; def foo; end; end; + sc.private_instance_methods(false).should.include?(:foo) - class << o; should_not have_private_instance_method(:foo); end - class << o; should have_instance_method(:foo); end + class << o + def foo; end + end + sc.private_instance_methods(false).should_not.include?(:foo) + sc.should.method_defined?(:foo) end end diff --git a/spec/ruby/language/delegation_spec.rb b/spec/ruby/language/delegation_spec.rb index 3d917993f5..efcf37aabc 100644 --- a/spec/ruby/language/delegation_spec.rb +++ b/spec/ruby/language/delegation_spec.rb @@ -59,6 +59,20 @@ describe "delegation with def(...)" do a.new.delegate(1, b: 2).should == Range.new([[], {}, nil], nil, true) end + + it "passes non-keyword trailing hash through without modification" do + a = Class.new(DelegationSpecs::Target) + a.class_eval(<<-RUBY) + def delegate(...) + target_single(...) + end + RUBY + + h = {a:1} + h.freeze + h2 = a.new.delegate(h) + h2.should.equal?(h) + end end describe "delegation with def(x, ...)" do diff --git a/spec/ruby/language/fixtures/delegation.rb b/spec/ruby/language/fixtures/delegation.rb index da2b024791..049a923e66 100644 --- a/spec/ruby/language/fixtures/delegation.rb +++ b/spec/ruby/language/fixtures/delegation.rb @@ -7,5 +7,9 @@ module DelegationSpecs def target_block(*args, **kwargs) yield [kwargs, args] end + + def target_single(arg) + arg + end end end diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb index d57b924bcf..f14f8ba93e 100644 --- a/spec/ruby/language/predefined_spec.rb +++ b/spec/ruby/language/predefined_spec.rb @@ -1103,7 +1103,7 @@ describe "Execution variable $:" do end describe "Global variable $\"" do - it "is an alias for $LOADED_FEATURES" do + it "is an alias of $LOADED_FEATURES" do $".should.equal? $LOADED_FEATURES end diff --git a/spec/ruby/library/bigdecimal/case_compare_spec.rb b/spec/ruby/library/bigdecimal/case_compare_spec.rb index fac6714356..1f1d223d6a 100644 --- a/spec/ruby/library/bigdecimal/case_compare_spec.rb +++ b/spec/ruby/library/bigdecimal/case_compare_spec.rb @@ -1,7 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' +require 'bigdecimal' describe "BigDecimal#===" do - it_behaves_like :bigdecimal_eql, :=== + it "is an alias of BigDecimal#==" do + BigDecimal.instance_method(:===).should == BigDecimal.instance_method(:==) + end end diff --git a/spec/ruby/library/bigdecimal/clone_spec.rb b/spec/ruby/library/bigdecimal/clone_spec.rb index b3a1c61d6a..979cc4c8e9 100644 --- a/spec/ruby/library/bigdecimal/clone_spec.rb +++ b/spec/ruby/library/bigdecimal/clone_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/clone' +require 'bigdecimal' -describe "BigDecimal#dup" do - it_behaves_like :bigdecimal_clone, :clone +describe "BigDecimal#clone" do + it "is an alias of BigDecimal#dup" do + BigDecimal.instance_method(:clone).should == BigDecimal.instance_method(:dup) + end end diff --git a/spec/ruby/library/bigdecimal/dup_spec.rb b/spec/ruby/library/bigdecimal/dup_spec.rb index bfabaf6e8b..dbf79ce5a6 100644 --- a/spec/ruby/library/bigdecimal/dup_spec.rb +++ b/spec/ruby/library/bigdecimal/dup_spec.rb @@ -1,6 +1,14 @@ require_relative '../../spec_helper' -require_relative 'shared/clone' +require 'bigdecimal' describe "BigDecimal#dup" do - it_behaves_like :bigdecimal_clone, :dup + before :each do + @obj = BigDecimal("1.2345") + end + + it "returns self" do + copy = @obj.dup + + copy.should.equal?(@obj) + end end diff --git a/spec/ruby/library/bigdecimal/eql_spec.rb b/spec/ruby/library/bigdecimal/eql_spec.rb index 1be5862751..0346175e1e 100644 --- a/spec/ruby/library/bigdecimal/eql_spec.rb +++ b/spec/ruby/library/bigdecimal/eql_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' +require 'bigdecimal' describe "BigDecimal#eql?" do - it_behaves_like :bigdecimal_eql, :eql? + it "is an alias of BigDecimal#==" do + BigDecimal.instance_method(:eql?).should == BigDecimal.instance_method(:==) + end end diff --git a/spec/ruby/library/bigdecimal/equal_value_spec.rb b/spec/ruby/library/bigdecimal/equal_value_spec.rb index 933060eada..d1133765b1 100644 --- a/spec/ruby/library/bigdecimal/equal_value_spec.rb +++ b/spec/ruby/library/bigdecimal/equal_value_spec.rb @@ -1,7 +1,63 @@ require_relative '../../spec_helper' -require_relative 'shared/eql' +require 'bigdecimal' describe "BigDecimal#==" do - it_behaves_like :bigdecimal_eql, :== + before :each do + @bg6543_21 = BigDecimal("6543.21") + @bg5667_19 = BigDecimal("5667.19") + @a = BigDecimal("1.0000000000000000000000000000000000000000005") + @b = BigDecimal("1.00000000000000000000000000000000000000000005") + @bigint = BigDecimal("1000.0") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + end + + it "tests for equality" do + (@bg6543_21 == @bg6543_21).should == true + (@a == @a).should == true + (@a == @b).should == false + (@bg6543_21 == @a).should == false + (@bigint == 1000).should == true + end + + it "returns false for NaN as it is never equal to any number" do + (@nan == @nan).should == false + (@a == @nan).should == false + (@nan == @a).should == false + (@nan == @infinity).should == false + (@nan == @infinity_minus).should == false + (@infinity == @nan).should == false + (@infinity_minus == @nan).should == false + end + + it "returns true for infinity values with the same sign" do + (@infinity == @infinity).should == true + (@infinity == BigDecimal("Infinity")).should == true + (BigDecimal("Infinity") == @infinity).should == true + + (@infinity_minus == @infinity_minus).should == true + (@infinity_minus == BigDecimal("-Infinity")).should == true + (BigDecimal("-Infinity") == @infinity_minus).should == true + end + + it "returns false for infinity values with different signs" do + (@infinity == @infinity_minus).should == false + (@infinity_minus == @infinity).should == false + end + + it "returns false when infinite value compared to finite one" do + (@infinity == @a).should == false + (@infinity_minus == @a).should == false + + (@a == @infinity).should == false + (@a == @infinity_minus).should == false + end + + it "returns false when compared objects that can not be coerced into BigDecimal" do + (@infinity == nil).should == false + (@bigint == nil).should == false + (@nan == nil).should == false + end end diff --git a/spec/ruby/library/bigdecimal/modulo_spec.rb b/spec/ruby/library/bigdecimal/modulo_spec.rb index 035d31bd98..d6b4f91b6d 100644 --- a/spec/ruby/library/bigdecimal/modulo_spec.rb +++ b/spec/ruby/library/bigdecimal/modulo_spec.rb @@ -1,12 +1,21 @@ require_relative '../../spec_helper' require_relative 'shared/modulo' +require 'bigdecimal' describe "BigDecimal#%" do it_behaves_like :bigdecimal_modulo, :% - it_behaves_like :bigdecimal_modulo_zerodivisionerror, :% + + it "raises ZeroDivisionError if other is zero" do + bd5667 = BigDecimal("5667.19") + + -> { bd5667 % 0 }.should.raise(ZeroDivisionError) + -> { bd5667 % BigDecimal("0") }.should.raise(ZeroDivisionError) + -> { @zero % @zero }.should.raise(ZeroDivisionError) + end end describe "BigDecimal#modulo" do - it_behaves_like :bigdecimal_modulo, :modulo - it_behaves_like :bigdecimal_modulo_zerodivisionerror, :modulo + it "is an alias of BigDecimal#%" do + BigDecimal.instance_method(:modulo).should == BigDecimal.instance_method(:%) + end end diff --git a/spec/ruby/library/bigdecimal/shared/clone.rb b/spec/ruby/library/bigdecimal/shared/clone.rb deleted file mode 100644 index 03de6d0fc3..0000000000 --- a/spec/ruby/library/bigdecimal/shared/clone.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'bigdecimal' - -describe :bigdecimal_clone, shared: true do - before :each do - @obj = BigDecimal("1.2345") - end - - it "returns self" do - copy = @obj.public_send(@method) - - copy.should.equal?(@obj) - end -end diff --git a/spec/ruby/library/bigdecimal/shared/eql.rb b/spec/ruby/library/bigdecimal/shared/eql.rb deleted file mode 100644 index 8e3e388bab..0000000000 --- a/spec/ruby/library/bigdecimal/shared/eql.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'bigdecimal' - -describe :bigdecimal_eql, shared: true do - before :each do - @bg6543_21 = BigDecimal("6543.21") - @bg5667_19 = BigDecimal("5667.19") - @a = BigDecimal("1.0000000000000000000000000000000000000000005") - @b = BigDecimal("1.00000000000000000000000000000000000000000005") - @bigint = BigDecimal("1000.0") - @nan = BigDecimal("NaN") - @infinity = BigDecimal("Infinity") - @infinity_minus = BigDecimal("-Infinity") - end - - it "tests for equality" do - @bg6543_21.send(@method, @bg6543_21).should == true - @a.send(@method, @a).should == true - @a.send(@method, @b).should == false - @bg6543_21.send(@method, @a).should == false - @bigint.send(@method, 1000).should == true - end - - it "returns false for NaN as it is never equal to any number" do - @nan.send(@method, @nan).should == false - @a.send(@method, @nan).should == false - @nan.send(@method, @a).should == false - @nan.send(@method, @infinity).should == false - @nan.send(@method, @infinity_minus).should == false - @infinity.send(@method, @nan).should == false - @infinity_minus.send(@method, @nan).should == false - end - - it "returns true for infinity values with the same sign" do - @infinity.send(@method, @infinity).should == true - @infinity.send(@method, BigDecimal("Infinity")).should == true - BigDecimal("Infinity").send(@method, @infinity).should == true - - @infinity_minus.send(@method, @infinity_minus).should == true - @infinity_minus.send(@method, BigDecimal("-Infinity")).should == true - BigDecimal("-Infinity").send(@method, @infinity_minus).should == true - end - - it "returns false for infinity values with different signs" do - @infinity.send(@method, @infinity_minus).should == false - @infinity_minus.send(@method, @infinity).should == false - end - - it "returns false when infinite value compared to finite one" do - @infinity.send(@method, @a).should == false - @infinity_minus.send(@method, @a).should == false - - @a.send(@method, @infinity).should == false - @a.send(@method, @infinity_minus).should == false - end - - it "returns false when compared objects that can not be coerced into BigDecimal" do - @infinity.send(@method, nil).should == false - @bigint.send(@method, nil).should == false - @nan.send(@method, nil).should == false - end -end diff --git a/spec/ruby/library/bigdecimal/shared/modulo.rb b/spec/ruby/library/bigdecimal/shared/modulo.rb index 63470d0977..5b5e3503c4 100644 --- a/spec/ruby/library/bigdecimal/shared/modulo.rb +++ b/spec/ruby/library/bigdecimal/shared/modulo.rb @@ -119,13 +119,3 @@ describe :bigdecimal_modulo, shared: true do }.should.raise(TypeError) end end - -describe :bigdecimal_modulo_zerodivisionerror, shared: true do - it "raises ZeroDivisionError if other is zero" do - bd5667 = BigDecimal("5667.19") - - -> { bd5667.send(@method, 0) }.should.raise(ZeroDivisionError) - -> { bd5667.send(@method, BigDecimal("0")) }.should.raise(ZeroDivisionError) - -> { @zero.send(@method, @zero) }.should.raise(ZeroDivisionError) - end -end diff --git a/spec/ruby/library/bigdecimal/shared/to_int.rb b/spec/ruby/library/bigdecimal/shared/to_int.rb deleted file mode 100644 index 3c9f3b4f97..0000000000 --- a/spec/ruby/library/bigdecimal/shared/to_int.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'bigdecimal' - -describe :bigdecimal_to_int, shared: true do - it "raises FloatDomainError if BigDecimal is infinity or NaN" do - -> { BigDecimal("Infinity").send(@method) }.should.raise(FloatDomainError) - -> { BigDecimal("NaN").send(@method) }.should.raise(FloatDomainError) - end - - it "returns Integer otherwise" do - BigDecimal("3E-20001").send(@method).should == 0 - BigDecimal("2E4000").send(@method).should == 2 * 10 ** 4000 - BigDecimal("2").send(@method).should == 2 - BigDecimal("2E10").send(@method).should == 20000000000 - BigDecimal("3.14159").send(@method).should == 3 - end -end diff --git a/spec/ruby/library/bigdecimal/to_i_spec.rb b/spec/ruby/library/bigdecimal/to_i_spec.rb index e5e65c562e..b6d9e43e57 100644 --- a/spec/ruby/library/bigdecimal/to_i_spec.rb +++ b/spec/ruby/library/bigdecimal/to_i_spec.rb @@ -1,7 +1,17 @@ require_relative '../../spec_helper' -require_relative 'shared/to_int' require 'bigdecimal' describe "BigDecimal#to_i" do - it_behaves_like :bigdecimal_to_int, :to_i + it "raises FloatDomainError if BigDecimal is infinity or NaN" do + -> { BigDecimal("Infinity").to_i }.should.raise(FloatDomainError) + -> { BigDecimal("NaN").to_i }.should.raise(FloatDomainError) + end + + it "returns Integer otherwise" do + BigDecimal("3E-20001").to_i.should == 0 + BigDecimal("2E4000").to_i.should == 2 * 10 ** 4000 + BigDecimal("2").to_i.should == 2 + BigDecimal("2E10").to_i.should == 20000000000 + BigDecimal("3.14159").to_i.should == 3 + end end diff --git a/spec/ruby/library/bigdecimal/to_int_spec.rb b/spec/ruby/library/bigdecimal/to_int_spec.rb index 4df6749845..18f3620f5e 100644 --- a/spec/ruby/library/bigdecimal/to_int_spec.rb +++ b/spec/ruby/library/bigdecimal/to_int_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/to_int' require 'bigdecimal' - describe "BigDecimal#to_int" do - it_behaves_like :bigdecimal_to_int, :to_int + it "is an alias of BigDecimal#to_i" do + BigDecimal.instance_method(:to_int).should == BigDecimal.instance_method(:to_i) + end end diff --git a/spec/ruby/library/date/asctime_spec.rb b/spec/ruby/library/date/asctime_spec.rb index 67d158cc01..e268ffe098 100644 --- a/spec/ruby/library/date/asctime_spec.rb +++ b/spec/ruby/library/date/asctime_spec.rb @@ -2,5 +2,8 @@ require_relative '../../spec_helper' require 'date' describe "Date#asctime" do - it "needs to be reviewed for spec completeness" + it "returns a canonical string representation of date" do + d = Date.today + d.asctime.should == d.strftime("%a %b %e %H:%M:%S %Y") + end end diff --git a/spec/ruby/library/date/commercial_spec.rb b/spec/ruby/library/date/commercial_spec.rb index d7fc34d74a..8e2df79b90 100644 --- a/spec/ruby/library/date/commercial_spec.rb +++ b/spec/ruby/library/date/commercial_spec.rb @@ -1,12 +1,5 @@ require 'date' require_relative '../../spec_helper' -require_relative 'shared/commercial' - -describe "Date#commercial" do - - it_behaves_like :date_commercial, :commercial - -end # reference: # October 1582 (the Gregorian calendar, Civil Date) @@ -15,3 +8,42 @@ end # 17 18 19 20 21 22 23 # 24 25 26 27 28 29 30 # 31 +describe "Date.commercial" do + it "creates a Date for Julian Day Number day 0 by default" do + d = Date.commercial + d.year.should == -4712 + d.month.should == 1 + d.day.should == 1 + end + + it "creates a Date for the monday in the year and week given" do + d = Date.commercial(2000, 1) + d.year.should == 2000 + d.month.should == 1 + d.day.should == 3 + d.cwday.should == 1 + end + + it "creates a Date for the correct day given the year, week and day number" do + d = Date.commercial(2004, 1, 1) + d.year.should == 2003 + d.month.should == 12 + d.day.should == 29 + d.cwday.should == 1 + d.cweek.should == 1 + d.cwyear.should == 2004 + end + + it "creates only Date objects for valid weeks" do + -> { Date.commercial(2004, 53, 1) }.should_not.raise(ArgumentError) + -> { Date.commercial(2004, 53, 0) }.should.raise(ArgumentError) + -> { Date.commercial(2004, 53, 8) }.should.raise(ArgumentError) + -> { Date.commercial(2004, 54, 1) }.should.raise(ArgumentError) + -> { Date.commercial(2004, 0, 1) }.should.raise(ArgumentError) + + -> { Date.commercial(2003, 52, 1) }.should_not.raise(ArgumentError) + -> { Date.commercial(2003, 53, 1) }.should.raise(ArgumentError) + -> { Date.commercial(2003, 52, 0) }.should.raise(ArgumentError) + -> { Date.commercial(2003, 52, 8) }.should.raise(ArgumentError) + end +end diff --git a/spec/ruby/library/date/ctime_spec.rb b/spec/ruby/library/date/ctime_spec.rb index 3faa7c6380..330076a735 100644 --- a/spec/ruby/library/date/ctime_spec.rb +++ b/spec/ruby/library/date/ctime_spec.rb @@ -2,5 +2,7 @@ require_relative '../../spec_helper' require 'date' describe "Date#ctime" do - it "needs to be reviewed for spec completeness" + it "is an alias of Date#asctime" do + Date.instance_method(:ctime).should == Date.instance_method(:asctime) + end end diff --git a/spec/ruby/library/date/jd_spec.rb b/spec/ruby/library/date/jd_spec.rb index 336b783e8d..e5cfe10eff 100644 --- a/spec/ruby/library/date/jd_spec.rb +++ b/spec/ruby/library/date/jd_spec.rb @@ -1,15 +1,22 @@ require_relative '../../spec_helper' -require_relative 'shared/jd' require 'date' describe "Date#jd" do - it "determines the Julian day for a Date object" do Date.civil(2008, 1, 16).jd.should == 2454482 end - end describe "Date.jd" do - it_behaves_like :date_jd, :jd + it "constructs a Date object if passed a Julian day" do + Date.jd(2454482).should == Date.civil(2008, 1, 16) + end + + it "returns a Date object representing Julian day 0 (-4712-01-01) if no arguments passed" do + Date.jd.should == Date.civil(-4712, 1, 1) + end + + it "constructs a Date object if passed a negative number" do + Date.jd(-1).should == Date.civil(-4713, 12, 31) + end end diff --git a/spec/ruby/library/date/mday_spec.rb b/spec/ruby/library/date/mday_spec.rb index 53f6f98169..32fd8fc7f1 100644 --- a/spec/ruby/library/date/mday_spec.rb +++ b/spec/ruby/library/date/mday_spec.rb @@ -2,5 +2,7 @@ require_relative '../../spec_helper' require 'date' describe "Date#mday" do - it "needs to be reviewed for spec completeness" + it "is an alias of Date#day" do + Date.instance_method(:mday).should == Date.instance_method(:day) + end end diff --git a/spec/ruby/library/date/mon_spec.rb b/spec/ruby/library/date/mon_spec.rb index 616d72cf88..15754ffb1f 100644 --- a/spec/ruby/library/date/mon_spec.rb +++ b/spec/ruby/library/date/mon_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/month' require 'date' describe "Date#mon" do - it_behaves_like :date_month, :mon + it "is an alias of Date#month" do + Date.instance_method(:mon).should == Date.instance_method(:month) + end end diff --git a/spec/ruby/library/date/month_spec.rb b/spec/ruby/library/date/month_spec.rb index f493ec8119..e040f9a94c 100644 --- a/spec/ruby/library/date/month_spec.rb +++ b/spec/ruby/library/date/month_spec.rb @@ -1,7 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/month' require 'date' describe "Date#month" do - it_behaves_like :date_month, :month + it "returns the month" do + m = Date.new(2000, 7, 1).month + m.should == 7 + end end diff --git a/spec/ruby/library/date/ordinal_spec.rb b/spec/ruby/library/date/ordinal_spec.rb index ec490fd49c..c8bf715163 100644 --- a/spec/ruby/library/date/ordinal_spec.rb +++ b/spec/ruby/library/date/ordinal_spec.rb @@ -1,7 +1,17 @@ require 'date' require_relative '../../spec_helper' -require_relative 'shared/ordinal' describe "Date.ordinal" do - it_behaves_like :date_ordinal, :ordinal + it "constructs a Date object from an ordinal date" do + # October 1582 (the Gregorian calendar, Ordinal Date) + # S M Tu W Th F S + # 274 275 276 277 278 279 + # 280 281 282 283 284 285 286 + # 287 288 289 290 291 292 293 + # 294 + Date.ordinal(1582, 274).should == Date.civil(1582, 10, 1) + Date.ordinal(1582, 277).should == Date.civil(1582, 10, 4) + Date.ordinal(1582, 278).should == Date.civil(1582, 10, 15) + Date.ordinal(1582, 287, Date::ENGLAND).should == Date.civil(1582, 10, 14, Date::ENGLAND) + end end diff --git a/spec/ruby/library/date/shared/commercial.rb b/spec/ruby/library/date/shared/commercial.rb deleted file mode 100644 index f53d83235a..0000000000 --- a/spec/ruby/library/date/shared/commercial.rb +++ /dev/null @@ -1,39 +0,0 @@ -describe :date_commercial, shared: true do - it "creates a Date for Julian Day Number day 0 by default" do - d = Date.send(@method) - d.year.should == -4712 - d.month.should == 1 - d.day.should == 1 - end - - it "creates a Date for the monday in the year and week given" do - d = Date.send(@method, 2000, 1) - d.year.should == 2000 - d.month.should == 1 - d.day.should == 3 - d.cwday.should == 1 - end - - it "creates a Date for the correct day given the year, week and day number" do - d = Date.send(@method, 2004, 1, 1) - d.year.should == 2003 - d.month.should == 12 - d.day.should == 29 - d.cwday.should == 1 - d.cweek.should == 1 - d.cwyear.should == 2004 - end - - it "creates only Date objects for valid weeks" do - -> { Date.send(@method, 2004, 53, 1) }.should_not.raise(ArgumentError) - -> { Date.send(@method, 2004, 53, 0) }.should.raise(ArgumentError) - -> { Date.send(@method, 2004, 53, 8) }.should.raise(ArgumentError) - -> { Date.send(@method, 2004, 54, 1) }.should.raise(ArgumentError) - -> { Date.send(@method, 2004, 0, 1) }.should.raise(ArgumentError) - - -> { Date.send(@method, 2003, 52, 1) }.should_not.raise(ArgumentError) - -> { Date.send(@method, 2003, 53, 1) }.should.raise(ArgumentError) - -> { Date.send(@method, 2003, 52, 0) }.should.raise(ArgumentError) - -> { Date.send(@method, 2003, 52, 8) }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/library/date/shared/jd.rb b/spec/ruby/library/date/shared/jd.rb deleted file mode 100644 index 511557b4f7..0000000000 --- a/spec/ruby/library/date/shared/jd.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe :date_jd, shared: true do - it "constructs a Date object if passed a Julian day" do - Date.send(@method, 2454482).should == Date.civil(2008, 1, 16) - end - - it "returns a Date object representing Julian day 0 (-4712-01-01) if no arguments passed" do - Date.send(@method).should == Date.civil(-4712, 1, 1) - end - - it "constructs a Date object if passed a negative number" do - Date.send(@method, -1).should == Date.civil(-4713, 12, 31) - end - -end diff --git a/spec/ruby/library/date/shared/month.rb b/spec/ruby/library/date/shared/month.rb deleted file mode 100644 index 5fcb2cbeb0..0000000000 --- a/spec/ruby/library/date/shared/month.rb +++ /dev/null @@ -1,6 +0,0 @@ -describe :date_month, shared: true do - it "returns the month" do - m = Date.new(2000, 7, 1).send(@method) - m.should == 7 - end -end diff --git a/spec/ruby/library/date/shared/ordinal.rb b/spec/ruby/library/date/shared/ordinal.rb deleted file mode 100644 index 4b182d5a25..0000000000 --- a/spec/ruby/library/date/shared/ordinal.rb +++ /dev/null @@ -1,22 +0,0 @@ -# reference: -# October 1582 (the Gregorian calendar, Civil Date) -# S M Tu W Th F S -# 1 2 3 4 15 16 -# 17 18 19 20 21 22 23 -# 24 25 26 27 28 29 30 -# 31 - -describe :date_ordinal, shared: true do - it "constructs a Date object from an ordinal date" do - # October 1582 (the Gregorian calendar, Ordinal Date) - # S M Tu W Th F S - # 274 275 276 277 278 279 - # 280 281 282 283 284 285 286 - # 287 288 289 290 291 292 293 - # 294 - Date.send(@method, 1582, 274).should == Date.civil(1582, 10, 1) - Date.send(@method, 1582, 277).should == Date.civil(1582, 10, 4) - Date.send(@method, 1582, 278).should == Date.civil(1582, 10, 15) - Date.send(@method, 1582, 287, Date::ENGLAND).should == Date.civil(1582, 10, 14, Date::ENGLAND) - end -end diff --git a/spec/ruby/library/date/shared/valid_civil.rb b/spec/ruby/library/date/shared/valid_civil.rb deleted file mode 100644 index 425fee4d2d..0000000000 --- a/spec/ruby/library/date/shared/valid_civil.rb +++ /dev/null @@ -1,36 +0,0 @@ -describe :date_valid_civil?, shared: true do - - # reference: - # October 1582 (the Gregorian calendar, Civil Date) - # S M Tu W Th F S - # 1 2 3 4 15 16 - # 17 18 19 20 21 22 23 - # 24 25 26 27 28 29 30 - # 31 - - it "returns true if it is a valid civil date" do - Date.send(@method, 1582, 10, 15).should == true - Date.send(@method, 1582, 10, 14, Date::ENGLAND).should == true - end - - it "returns false if it is not a valid civil date" do - Date.send(@method, 1582, 10, 14).should == false - end - - it "handles negative months and days" do - # October 1582 (the Gregorian calendar, Civil Date) - # S M Tu W Th F S - # -21 -20 -19 -18 -17 -16 - # -15 -14 -13 -12 -11 -10 -9 - # -8 -7 -6 -5 -4 -3 -2 - # -1 - Date.send(@method, 1582, -3, -22).should == false - Date.send(@method, 1582, -3, -21).should == true - Date.send(@method, 1582, -3, -18).should == true - Date.send(@method, 1582, -3, -17).should == true - - Date.send(@method, 2007, -11, -10).should == true - Date.send(@method, 2008, -11, -10).should == true - end - -end diff --git a/spec/ruby/library/date/shared/valid_commercial.rb b/spec/ruby/library/date/shared/valid_commercial.rb deleted file mode 100644 index 573b851fdd..0000000000 --- a/spec/ruby/library/date/shared/valid_commercial.rb +++ /dev/null @@ -1,34 +0,0 @@ -describe :date_valid_commercial?, shared: true do - - it "returns true if it is a valid commercial date" do - # October 1582 (the Gregorian calendar, Commercial Date) - # M Tu W Th F Sa Su - # 39: 1 2 3 4 5 6 7 - # 40: 1 2 3 4 5 6 7 - # 41: 1 2 3 4 5 6 7 - Date.send(@method, 1582, 39, 4).should == true - Date.send(@method, 1582, 39, 5).should == true - Date.send(@method, 1582, 41, 4).should == true - Date.send(@method, 1582, 41, 5).should == true - Date.send(@method, 1582, 41, 4, Date::ENGLAND).should == true - Date.send(@method, 1752, 37, 4, Date::ENGLAND).should == true - end - - it "returns false it is not a valid commercial date" do - Date.send(@method, 1999, 53, 1).should == false - end - - it "handles negative week and day numbers" do - # October 1582 (the Gregorian calendar, Commercial Date) - # M Tu W Th F Sa Su - # -12: -7 -6 -5 -4 -3 -2 -1 - # -11: -7 -6 -5 -4 -3 -2 -1 - # -10: -7 -6 -5 -4 -3 -2 -1 - Date.send(@method, 1582, -12, -4).should == true - Date.send(@method, 1582, -12, -3).should == true - Date.send(@method, 2007, -44, -2).should == true - Date.send(@method, 2008, -44, -2).should == true - Date.send(@method, 1999, -53, -1).should == false - end - -end diff --git a/spec/ruby/library/date/shared/valid_jd.rb b/spec/ruby/library/date/shared/valid_jd.rb deleted file mode 100644 index 0c01710208..0000000000 --- a/spec/ruby/library/date/shared/valid_jd.rb +++ /dev/null @@ -1,20 +0,0 @@ -describe :date_valid_jd?, shared: true do - it "returns true if passed a number value" do - Date.send(@method, -100).should == true - Date.send(@method, 100.0).should == true - Date.send(@method, 2**100).should == true - Date.send(@method, Rational(1,2)).should == true - end - - it "returns false if passed nil" do - Date.send(@method, nil).should == false - end - - it "returns false if passed symbol" do - Date.send(@method, :number).should == false - end - - it "returns false if passed false" do - Date.send(@method, false).should == false - end -end diff --git a/spec/ruby/library/date/shared/valid_ordinal.rb b/spec/ruby/library/date/shared/valid_ordinal.rb deleted file mode 100644 index 1ed961be23..0000000000 --- a/spec/ruby/library/date/shared/valid_ordinal.rb +++ /dev/null @@ -1,26 +0,0 @@ -describe :date_valid_ordinal?, shared: true do - it "determines if the date is a valid ordinal date" do - # October 1582 (the Gregorian calendar, Ordinal Date) - # S M Tu W Th F S - # 274 275 276 277 278 279 - # 280 281 282 283 284 285 286 - # 287 288 289 290 291 292 293 - # 294 - Date.send(@method, 1582, 277).should == true - Date.send(@method, 1582, 278).should == true - Date.send(@method, 1582, 287).should == true - Date.send(@method, 1582, 288).should == true - end - - it "handles negative day numbers" do - # October 1582 (the Gregorian calendar, Ordinal Date) - # S M Tu W Th F S - # -82 -81 -80 -79 -78 -77 - # -76 -75 -74 -73 -72 -71 -70 - # -69 -68 -67 -66 -65 -64 -63 - # -62 - Date.send(@method, 1582, -79).should == true - Date.send(@method, 1582, -78).should == true - Date.send(@method, 2007, -100).should == true - end -end diff --git a/spec/ruby/library/date/succ_spec.rb b/spec/ruby/library/date/succ_spec.rb index c4a902aa63..0b14d3bb73 100644 --- a/spec/ruby/library/date/succ_spec.rb +++ b/spec/ruby/library/date/succ_spec.rb @@ -2,5 +2,7 @@ require_relative '../../spec_helper' require 'date' describe "Date#succ" do - it "needs to be reviewed for spec completeness" + it "is an alias of Date#next" do + Date.instance_method(:succ).should == Date.instance_method(:next) + end end diff --git a/spec/ruby/library/date/valid_civil_spec.rb b/spec/ruby/library/date/valid_civil_spec.rb index 00f2c57205..8cffc80310 100644 --- a/spec/ruby/library/date/valid_civil_spec.rb +++ b/spec/ruby/library/date/valid_civil_spec.rb @@ -1,9 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/valid_civil' require 'date' -describe "Date#valid_civil?" do - - it_behaves_like :date_valid_civil?, :valid_civil? - +describe "Date.valid_civil?" do + it "is an alias of Date.valid_date?" do + Date.method(:valid_civil?).should == Date.method(:valid_date?) + end end diff --git a/spec/ruby/library/date/valid_commercial_spec.rb b/spec/ruby/library/date/valid_commercial_spec.rb index 7e96782b6b..21a91ad867 100644 --- a/spec/ruby/library/date/valid_commercial_spec.rb +++ b/spec/ruby/library/date/valid_commercial_spec.rb @@ -1,8 +1,35 @@ require_relative '../../spec_helper' -require_relative 'shared/valid_commercial' require 'date' -describe "Date#valid_commercial?" do +describe "Date.valid_commercial?" do + it "returns true if it is a valid commercial date" do + # October 1582 (the Gregorian calendar, Commercial Date) + # M Tu W Th F Sa Su + # 39: 1 2 3 4 5 6 7 + # 40: 1 2 3 4 5 6 7 + # 41: 1 2 3 4 5 6 7 + Date.valid_commercial?(1582, 39, 4).should == true + Date.valid_commercial?(1582, 39, 5).should == true + Date.valid_commercial?(1582, 41, 4).should == true + Date.valid_commercial?(1582, 41, 5).should == true + Date.valid_commercial?(1582, 41, 4, Date::ENGLAND).should == true + Date.valid_commercial?(1752, 37, 4, Date::ENGLAND).should == true + end - it_behaves_like :date_valid_commercial?, :valid_commercial? + it "returns false it is not a valid commercial date" do + Date.valid_commercial?(1999, 53, 1).should == false + end + + it "handles negative week and day numbers" do + # October 1582 (the Gregorian calendar, Commercial Date) + # M Tu W Th F Sa Su + # -12: -7 -6 -5 -4 -3 -2 -1 + # -11: -7 -6 -5 -4 -3 -2 -1 + # -10: -7 -6 -5 -4 -3 -2 -1 + Date.valid_commercial?(1582, -12, -4).should == true + Date.valid_commercial?(1582, -12, -3).should == true + Date.valid_commercial?(2007, -44, -2).should == true + Date.valid_commercial?(2008, -44, -2).should == true + Date.valid_commercial?(1999, -53, -1).should == false + end end diff --git a/spec/ruby/library/date/valid_date_spec.rb b/spec/ruby/library/date/valid_date_spec.rb index f12a71d966..f0d5ec7b4d 100644 --- a/spec/ruby/library/date/valid_date_spec.rb +++ b/spec/ruby/library/date/valid_date_spec.rb @@ -1,7 +1,36 @@ require_relative '../../spec_helper' -require_relative 'shared/valid_civil' require 'date' -describe "Date#valid_date?" do - it_behaves_like :date_valid_civil?, :valid_date? +describe "Date.valid_date?" do + # reference: + # October 1582 (the Gregorian calendar, Civil Date) + # S M Tu W Th F S + # 1 2 3 4 15 16 + # 17 18 19 20 21 22 23 + # 24 25 26 27 28 29 30 + # 31 + it "returns true if it is a valid civil date" do + Date.valid_date?(1582, 10, 15).should == true + Date.valid_date?(1582, 10, 14, Date::ENGLAND).should == true + end + + it "returns false if it is not a valid civil date" do + Date.valid_date?(1582, 10, 14).should == false + end + + it "handles negative months and days" do + # October 1582 (the Gregorian calendar, Civil Date) + # S M Tu W Th F S + # -21 -20 -19 -18 -17 -16 + # -15 -14 -13 -12 -11 -10 -9 + # -8 -7 -6 -5 -4 -3 -2 + # -1 + Date.valid_date?(1582, -3, -22).should == false + Date.valid_date?(1582, -3, -21).should == true + Date.valid_date?(1582, -3, -18).should == true + Date.valid_date?(1582, -3, -17).should == true + + Date.valid_date?(2007, -11, -10).should == true + Date.valid_date?(2008, -11, -10).should == true + end end diff --git a/spec/ruby/library/date/valid_jd_spec.rb b/spec/ruby/library/date/valid_jd_spec.rb index aecaaabcf4..46f22de497 100644 --- a/spec/ruby/library/date/valid_jd_spec.rb +++ b/spec/ruby/library/date/valid_jd_spec.rb @@ -1,9 +1,23 @@ require_relative '../../spec_helper' -require_relative 'shared/valid_jd' require 'date' describe "Date.valid_jd?" do + it "returns true if passed a number value" do + Date.valid_jd?(-100).should == true + Date.valid_jd?(100.0).should == true + Date.valid_jd?(2**100).should == true + Date.valid_jd?(Rational(1,2)).should == true + end - it_behaves_like :date_valid_jd?, :valid_jd? + it "returns false if passed nil" do + Date.valid_jd?(nil).should == false + end + it "returns false if passed symbol" do + Date.valid_jd?(:number).should == false + end + + it "returns false if passed false" do + Date.valid_jd?(false).should == false + end end diff --git a/spec/ruby/library/date/valid_ordinal_spec.rb b/spec/ruby/library/date/valid_ordinal_spec.rb index 58d548c704..bb5c259606 100644 --- a/spec/ruby/library/date/valid_ordinal_spec.rb +++ b/spec/ruby/library/date/valid_ordinal_spec.rb @@ -1,9 +1,29 @@ require_relative '../../spec_helper' -require_relative 'shared/valid_ordinal' require 'date' describe "Date.valid_ordinal?" do + it "determines if the date is a valid ordinal date" do + # October 1582 (the Gregorian calendar, Ordinal Date) + # S M Tu W Th F S + # 274 275 276 277 278 279 + # 280 281 282 283 284 285 286 + # 287 288 289 290 291 292 293 + # 294 + Date.valid_ordinal?(1582, 277).should == true + Date.valid_ordinal?(1582, 278).should == true + Date.valid_ordinal?(1582, 287).should == true + Date.valid_ordinal?(1582, 288).should == true + end - it_behaves_like :date_valid_ordinal?, :valid_ordinal? - + it "handles negative day numbers" do + # October 1582 (the Gregorian calendar, Ordinal Date) + # S M Tu W Th F S + # -82 -81 -80 -79 -78 -77 + # -76 -75 -74 -73 -72 -71 -70 + # -69 -68 -67 -66 -65 -64 -63 + # -62 + Date.valid_ordinal?(1582, -79).should == true + Date.valid_ordinal?(1582, -78).should == true + Date.valid_ordinal?(2007, -100).should == true + end end diff --git a/spec/ruby/library/datetime/hour_spec.rb b/spec/ruby/library/datetime/hour_spec.rb index 383a85fe60..5d8e75edcb 100644 --- a/spec/ruby/library/datetime/hour_spec.rb +++ b/spec/ruby/library/datetime/hour_spec.rb @@ -35,8 +35,7 @@ describe "DateTime#hour" do end it "raises an error for hour fractions smaller than -24" do - -> { new_datetime(hour: -24 - Rational(1,2)) }.should( - raise_error(ArgumentError)) + -> { new_datetime(hour: -24 - Rational(1,2)) }.should.raise(ArgumentError) end it "adds 1 to day, when 24 hours given" do diff --git a/spec/ruby/library/datetime/iso8601_spec.rb b/spec/ruby/library/datetime/iso8601_spec.rb index 457881277a..4368300fd5 100644 --- a/spec/ruby/library/datetime/iso8601_spec.rb +++ b/spec/ruby/library/datetime/iso8601_spec.rb @@ -6,5 +6,7 @@ describe "DateTime.iso8601" do end describe "DateTime#iso8601" do - it "needs to be reviewed for spec completeness" + it "is an alias of DateTime#isoxmlschema8601" do + DateTime.instance_method(:iso8601).should == DateTime.instance_method(:xmlschema) + end end diff --git a/spec/ruby/library/datetime/min_spec.rb b/spec/ruby/library/datetime/min_spec.rb index a1eaa214cb..ca995a7eed 100644 --- a/spec/ruby/library/datetime/min_spec.rb +++ b/spec/ruby/library/datetime/min_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/min' +require 'date' -describe "DateTime.min" do - it_behaves_like :datetime_min, :min +describe "DateTime#min" do + it "is an alias of DateTime#minute" do + DateTime.instance_method(:min).should == DateTime.instance_method(:minute) + end end diff --git a/spec/ruby/library/datetime/minute_spec.rb b/spec/ruby/library/datetime/minute_spec.rb index acdfeda345..6e99752de7 100644 --- a/spec/ruby/library/datetime/minute_spec.rb +++ b/spec/ruby/library/datetime/minute_spec.rb @@ -1,6 +1,40 @@ require_relative '../../spec_helper' -require_relative 'shared/min' +require 'date' -describe "DateTime.minute" do - it_behaves_like :datetime_min, :minute +describe "DateTime#minute" do + it "returns 0 if no argument is passed" do + DateTime.new.minute.should == 0 + end + + it "returns the minute passed as argument" do + new_datetime(minute: 5).minute.should == 5 + end + + it "adds 60 to negative minutes" do + new_datetime(minute: -20).minute.should == 40 + end + + it "raises an error for Rational" do + -> { new_datetime minute: 5 + Rational(1,2) }.should.raise(ArgumentError) + end + + it "raises an error for Float" do + -> { new_datetime minute: 5.5 }.should.raise(ArgumentError) + end + + it "raises an error for Rational" do + -> { new_datetime(hour: 2 + Rational(1,2)) }.should.raise(ArgumentError) + end + + it "raises an error, when the minute is smaller than -60" do + -> { new_datetime(minute: -61) }.should.raise(ArgumentError) + end + + it "raises an error, when the minute is greater or equal than 60" do + -> { new_datetime(minute: 60) }.should.raise(ArgumentError) + end + + it "raises an error for minute fractions smaller than -60" do + -> { new_datetime(minute: -60 - Rational(1,2))}.should.raise(ArgumentError) + end end diff --git a/spec/ruby/library/datetime/sec_spec.rb b/spec/ruby/library/datetime/sec_spec.rb index f681283c8e..f8a8b4646e 100644 --- a/spec/ruby/library/datetime/sec_spec.rb +++ b/spec/ruby/library/datetime/sec_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/sec' +require 'date' -describe "DateTime.sec" do - it_behaves_like :datetime_sec, :sec +describe "DateTime#sec" do + it "is an alias of DateTime#second" do + DateTime.instance_method(:sec).should == DateTime.instance_method(:second) + end end diff --git a/spec/ruby/library/datetime/second_fraction_spec.rb b/spec/ruby/library/datetime/second_fraction_spec.rb index d5393149ba..70f5abf560 100644 --- a/spec/ruby/library/datetime/second_fraction_spec.rb +++ b/spec/ruby/library/datetime/second_fraction_spec.rb @@ -2,5 +2,7 @@ require_relative '../../spec_helper' require 'date' describe "DateTime#second_fraction" do - it "needs to be reviewed for spec completeness" + it "is an alias of DateTime#sec_fraction" do + DateTime.instance_method(:second_fraction).should == DateTime.instance_method(:sec_fraction) + end end diff --git a/spec/ruby/library/datetime/second_spec.rb b/spec/ruby/library/datetime/second_spec.rb index 545c3f9109..9fb1965b73 100644 --- a/spec/ruby/library/datetime/second_spec.rb +++ b/spec/ruby/library/datetime/second_spec.rb @@ -1,6 +1,45 @@ require_relative '../../spec_helper' -require_relative 'shared/sec' +require 'date' describe "DateTime#second" do - it_behaves_like :datetime_sec, :second + it "returns 0 seconds if passed no arguments" do + d = DateTime.new + d.second.should == 0 + end + + it "returns the seconds passed in the arguments" do + new_datetime(second: 5).second.should == 5 + end + + it "adds 60 to negative values" do + new_datetime(second: -20).second.should == 40 + end + + it "returns the absolute value of a Rational" do + new_datetime(second: 5 + Rational(1,2)).second.should == 5 + end + + it "returns the absolute value of a float" do + new_datetime(second: 5.5).second.should == 5 + end + + it "raises an error when minute is given as a rational" do + -> { new_datetime(minute: 5 + Rational(1,2)) }.should.raise(ArgumentError) + end + + it "raises an error, when the second is smaller than -60" do + -> { new_datetime(second: -61) }.should.raise(ArgumentError) + end + + it "raises an error, when the second is greater or equal than 60" do + -> { new_datetime(second: 60) }.should.raise(ArgumentError) + end + + it "raises an error for second fractions smaller than -60" do + -> { new_datetime(second: -60 - Rational(1,2))}.should.raise(ArgumentError) + end + + it "takes a second fraction near 60" do + new_datetime(second: 59 + Rational(1,2)).second.should == 59 + end end diff --git a/spec/ruby/library/datetime/shared/min.rb b/spec/ruby/library/datetime/shared/min.rb deleted file mode 100644 index 04e5f3457a..0000000000 --- a/spec/ruby/library/datetime/shared/min.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'date' - -describe :datetime_min, shared: true do - it "returns 0 if no argument is passed" do - DateTime.new.send(@method).should == 0 - end - - it "returns the minute passed as argument" do - new_datetime(minute: 5).send(@method).should == 5 - end - - it "adds 60 to negative minutes" do - new_datetime(minute: -20).send(@method).should == 40 - end - - it "raises an error for Rational" do - -> { new_datetime minute: 5 + Rational(1,2) }.should.raise(ArgumentError) - end - - it "raises an error for Float" do - -> { new_datetime minute: 5.5 }.should.raise(ArgumentError) - end - - it "raises an error for Rational" do - -> { new_datetime(hour: 2 + Rational(1,2)) }.should.raise(ArgumentError) - end - - it "raises an error, when the minute is smaller than -60" do - -> { new_datetime(minute: -61) }.should.raise(ArgumentError) - end - - it "raises an error, when the minute is greater or equal than 60" do - -> { new_datetime(minute: 60) }.should.raise(ArgumentError) - end - - it "raises an error for minute fractions smaller than -60" do - -> { new_datetime(minute: -60 - Rational(1,2))}.should( - raise_error(ArgumentError)) - end -end diff --git a/spec/ruby/library/datetime/shared/sec.rb b/spec/ruby/library/datetime/shared/sec.rb deleted file mode 100644 index 5af5db4fb2..0000000000 --- a/spec/ruby/library/datetime/shared/sec.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'date' - -describe :datetime_sec, shared: true do - it "returns 0 seconds if passed no arguments" do - d = DateTime.new - d.send(@method).should == 0 - end - - it "returns the seconds passed in the arguments" do - new_datetime(second: 5).send(@method).should == 5 - end - - it "adds 60 to negative values" do - new_datetime(second: -20).send(@method).should == 40 - end - - it "returns the absolute value of a Rational" do - new_datetime(second: 5 + Rational(1,2)).send(@method).should == 5 - end - - it "returns the absolute value of a float" do - new_datetime(second: 5.5).send(@method).should == 5 - end - - it "raises an error when minute is given as a rational" do - -> { new_datetime(minute: 5 + Rational(1,2)) }.should.raise(ArgumentError) - end - - it "raises an error, when the second is smaller than -60" do - -> { new_datetime(second: -61) }.should.raise(ArgumentError) - end - - it "raises an error, when the second is greater or equal than 60" do - -> { new_datetime(second: 60) }.should.raise(ArgumentError) - end - - it "raises an error for second fractions smaller than -60" do - -> { new_datetime(second: -60 - Rational(1,2))}.should( - raise_error(ArgumentError)) - end - - it "takes a second fraction near 60" do - new_datetime(second: 59 + Rational(1,2)).send(@method).should == 59 - end -end diff --git a/spec/ruby/library/digest/instance/append_spec.rb b/spec/ruby/library/digest/instance/append_spec.rb index 2499579298..7f4ce3d121 100644 --- a/spec/ruby/library/digest/instance/append_spec.rb +++ b/spec/ruby/library/digest/instance/append_spec.rb @@ -1,7 +1,11 @@ require_relative '../../../spec_helper' require 'digest' -require_relative 'shared/update' describe "Digest::Instance#<<" do - it_behaves_like :digest_instance_update, :<< + it "raises a RuntimeError if called" do + c = Class.new do + include Digest::Instance + end + -> { c.new << "test" }.should.raise(RuntimeError) + end end diff --git a/spec/ruby/library/digest/instance/shared/update.rb b/spec/ruby/library/digest/instance/shared/update.rb deleted file mode 100644 index e064a90087..0000000000 --- a/spec/ruby/library/digest/instance/shared/update.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :digest_instance_update, shared: true do - it "raises a RuntimeError if called" do - c = Class.new do - include Digest::Instance - end - -> { c.new.send(@method, "test") }.should.raise(RuntimeError) - end -end diff --git a/spec/ruby/library/digest/instance/update_spec.rb b/spec/ruby/library/digest/instance/update_spec.rb index 3bb4dd7f1b..d15b976213 100644 --- a/spec/ruby/library/digest/instance/update_spec.rb +++ b/spec/ruby/library/digest/instance/update_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' require 'digest' -require_relative 'shared/update' describe "Digest::Instance#update" do - it_behaves_like :digest_instance_update, :update + it "is an alias of Digest::Instance#<<" do + Digest::Instance.instance_method(:update).should == Digest::Instance.instance_method(:<<) + end end diff --git a/spec/ruby/library/digest/md5/append_spec.rb b/spec/ruby/library/digest/md5/append_spec.rb index 0abdc074a1..6f42e4f286 100644 --- a/spec/ruby/library/digest/md5/append_spec.rb +++ b/spec/ruby/library/digest/md5/append_spec.rb @@ -1,7 +1,10 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/update' describe "Digest::MD5#<<" do - it_behaves_like :md5_update, :<< + it "can update" do + cur_digest = Digest::MD5.new + cur_digest << MD5Constants::Contents + cur_digest.digest.should == MD5Constants::Digest + end end diff --git a/spec/ruby/library/digest/md5/length_spec.rb b/spec/ruby/library/digest/md5/length_spec.rb index b05b2a20fd..18bda51129 100644 --- a/spec/ruby/library/digest/md5/length_spec.rb +++ b/spec/ruby/library/digest/md5/length_spec.rb @@ -1,7 +1,11 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/length' describe "Digest::MD5#length" do - it_behaves_like :md5_length, :length + it "returns the length of the digest" do + cur_digest = Digest::MD5.new + cur_digest.length.should == MD5Constants::BlankDigest.size + cur_digest << MD5Constants::Contents + cur_digest.length.should == MD5Constants::Digest.size + end end diff --git a/spec/ruby/library/digest/md5/shared/length.rb b/spec/ruby/library/digest/md5/shared/length.rb deleted file mode 100644 index c5b2b97b58..0000000000 --- a/spec/ruby/library/digest/md5/shared/length.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :md5_length, shared: true do - it "returns the length of the digest" do - cur_digest = Digest::MD5.new - cur_digest.send(@method).should == MD5Constants::BlankDigest.size - cur_digest << MD5Constants::Contents - cur_digest.send(@method).should == MD5Constants::Digest.size - end -end diff --git a/spec/ruby/library/digest/md5/shared/update.rb b/spec/ruby/library/digest/md5/shared/update.rb deleted file mode 100644 index be8622aed5..0000000000 --- a/spec/ruby/library/digest/md5/shared/update.rb +++ /dev/null @@ -1,7 +0,0 @@ -describe :md5_update, shared: true do - it "can update" do - cur_digest = Digest::MD5.new - cur_digest.send @method, MD5Constants::Contents - cur_digest.digest.should == MD5Constants::Digest - end -end diff --git a/spec/ruby/library/digest/md5/size_spec.rb b/spec/ruby/library/digest/md5/size_spec.rb index 22e3272d36..54709234de 100644 --- a/spec/ruby/library/digest/md5/size_spec.rb +++ b/spec/ruby/library/digest/md5/size_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/length' describe "Digest::MD5#size" do - it_behaves_like :md5_length, :size + it "is an alias of Digest::MD5#length" do + Digest::MD5.instance_method(:size).should == Digest::MD5.instance_method(:length) + end end diff --git a/spec/ruby/library/digest/md5/update_spec.rb b/spec/ruby/library/digest/md5/update_spec.rb index 4773db308c..830ccfead6 100644 --- a/spec/ruby/library/digest/md5/update_spec.rb +++ b/spec/ruby/library/digest/md5/update_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/constants' -require_relative 'shared/update' +require 'digest' describe "Digest::MD5#update" do - it_behaves_like :md5_update, :update + it "is an alias of Digest::MD5#<<" do + Digest::MD5.instance_method(:update).should == Digest::MD5.instance_method(:<<) + end end diff --git a/spec/ruby/library/digest/sha256/append_spec.rb b/spec/ruby/library/digest/sha256/append_spec.rb index ab594c105f..f18b06c2a1 100644 --- a/spec/ruby/library/digest/sha256/append_spec.rb +++ b/spec/ruby/library/digest/sha256/append_spec.rb @@ -1,7 +1,10 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/update' describe "Digest::SHA256#<<" do - it_behaves_like :sha256_update, :<< + it "can update" do + cur_digest = Digest::SHA256.new + cur_digest << SHA256Constants::Contents + cur_digest.digest.should == SHA256Constants::Digest + end end diff --git a/spec/ruby/library/digest/sha256/length_spec.rb b/spec/ruby/library/digest/sha256/length_spec.rb index 181ac564ad..fc3db6548e 100644 --- a/spec/ruby/library/digest/sha256/length_spec.rb +++ b/spec/ruby/library/digest/sha256/length_spec.rb @@ -1,7 +1,11 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/length' describe "Digest::SHA256#length" do - it_behaves_like :sha256_length, :length + it "returns the length of the digest" do + cur_digest = Digest::SHA256.new + cur_digest.length.should == SHA256Constants::BlankDigest.size + cur_digest << SHA256Constants::Contents + cur_digest.length.should == SHA256Constants::Digest.size + end end diff --git a/spec/ruby/library/digest/sha256/shared/length.rb b/spec/ruby/library/digest/sha256/shared/length.rb deleted file mode 100644 index 996673a5bd..0000000000 --- a/spec/ruby/library/digest/sha256/shared/length.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :sha256_length, shared: true do - it "returns the length of the digest" do - cur_digest = Digest::SHA256.new - cur_digest.send(@method).should == SHA256Constants::BlankDigest.size - cur_digest << SHA256Constants::Contents - cur_digest.send(@method).should == SHA256Constants::Digest.size - end -end diff --git a/spec/ruby/library/digest/sha256/shared/update.rb b/spec/ruby/library/digest/sha256/shared/update.rb deleted file mode 100644 index 0edc07935b..0000000000 --- a/spec/ruby/library/digest/sha256/shared/update.rb +++ /dev/null @@ -1,7 +0,0 @@ -describe :sha256_update, shared: true do - it "can update" do - cur_digest = Digest::SHA256.new - cur_digest.send @method, SHA256Constants::Contents - cur_digest.digest.should == SHA256Constants::Digest - end -end diff --git a/spec/ruby/library/digest/sha256/size_spec.rb b/spec/ruby/library/digest/sha256/size_spec.rb index 1028263342..6102e1c8aa 100644 --- a/spec/ruby/library/digest/sha256/size_spec.rb +++ b/spec/ruby/library/digest/sha256/size_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/length' describe "Digest::SHA256#size" do - it_behaves_like :sha256_length, :size + it "is an alias of Digest::SHA256#length" do + Digest::SHA256.instance_method(:size).should == Digest::SHA256.instance_method(:length) + end end diff --git a/spec/ruby/library/digest/sha256/update_spec.rb b/spec/ruby/library/digest/sha256/update_spec.rb index 92316eb752..d6724936f1 100644 --- a/spec/ruby/library/digest/sha256/update_spec.rb +++ b/spec/ruby/library/digest/sha256/update_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/constants' -require_relative 'shared/update' +require 'digest' describe "Digest::SHA256#update" do - it_behaves_like :sha256_update, :update + it "is an alias of Digest::SHA256#<<" do + Digest::SHA256.instance_method(:update).should == Digest::SHA256.instance_method(:<<) + end end diff --git a/spec/ruby/library/digest/sha384/append_spec.rb b/spec/ruby/library/digest/sha384/append_spec.rb index 94c036cc3f..b9a862f1c2 100644 --- a/spec/ruby/library/digest/sha384/append_spec.rb +++ b/spec/ruby/library/digest/sha384/append_spec.rb @@ -1,7 +1,10 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/update' describe "Digest::SHA384#<<" do - it_behaves_like :sha384_update, :<< + it "can update" do + cur_digest = Digest::SHA384.new + cur_digest << SHA384Constants::Contents + cur_digest.digest.should == SHA384Constants::Digest + end end diff --git a/spec/ruby/library/digest/sha384/length_spec.rb b/spec/ruby/library/digest/sha384/length_spec.rb index 33fed492ef..e5cd6131fd 100644 --- a/spec/ruby/library/digest/sha384/length_spec.rb +++ b/spec/ruby/library/digest/sha384/length_spec.rb @@ -1,7 +1,11 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/length' describe "Digest::SHA384#length" do - it_behaves_like :sha384_length, :length + it "returns the length of the digest" do + cur_digest = Digest::SHA384.new + cur_digest.length.should == SHA384Constants::BlankDigest.size + cur_digest << SHA384Constants::Contents + cur_digest.length.should == SHA384Constants::Digest.size + end end diff --git a/spec/ruby/library/digest/sha384/shared/length.rb b/spec/ruby/library/digest/sha384/shared/length.rb deleted file mode 100644 index 0c88288bcf..0000000000 --- a/spec/ruby/library/digest/sha384/shared/length.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :sha384_length, shared: true do - it "returns the length of the digest" do - cur_digest = Digest::SHA384.new - cur_digest.send(@method).should == SHA384Constants::BlankDigest.size - cur_digest << SHA384Constants::Contents - cur_digest.send(@method).should == SHA384Constants::Digest.size - end -end diff --git a/spec/ruby/library/digest/sha384/shared/update.rb b/spec/ruby/library/digest/sha384/shared/update.rb deleted file mode 100644 index 1c6e31cf6a..0000000000 --- a/spec/ruby/library/digest/sha384/shared/update.rb +++ /dev/null @@ -1,7 +0,0 @@ -describe :sha384_update, shared: true do - it "can update" do - cur_digest = Digest::SHA384.new - cur_digest.send @method, SHA384Constants::Contents - cur_digest.digest.should == SHA384Constants::Digest - end -end diff --git a/spec/ruby/library/digest/sha384/size_spec.rb b/spec/ruby/library/digest/sha384/size_spec.rb index 4c3b14f7a0..40c291c623 100644 --- a/spec/ruby/library/digest/sha384/size_spec.rb +++ b/spec/ruby/library/digest/sha384/size_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/constants' -require_relative 'shared/length' +require 'digest' describe "Digest::SHA384#size" do - it_behaves_like :sha384_length, :size + it "is an alias of Digest::SHA384#length" do + Digest::SHA384.instance_method(:size).should == Digest::SHA384.instance_method(:length) + end end diff --git a/spec/ruby/library/digest/sha384/update_spec.rb b/spec/ruby/library/digest/sha384/update_spec.rb index a1d0dd6068..561dcad3ec 100644 --- a/spec/ruby/library/digest/sha384/update_spec.rb +++ b/spec/ruby/library/digest/sha384/update_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/constants' -require_relative 'shared/update' +require 'digest' describe "Digest::SHA384#update" do - it_behaves_like :sha384_update, :update + it "is an alias of Digest::SHA384#<<" do + Digest::SHA384.instance_method(:update).should == Digest::SHA384.instance_method(:<<) + end end diff --git a/spec/ruby/library/digest/sha512/append_spec.rb b/spec/ruby/library/digest/sha512/append_spec.rb index 9106e9685d..f297005403 100644 --- a/spec/ruby/library/digest/sha512/append_spec.rb +++ b/spec/ruby/library/digest/sha512/append_spec.rb @@ -1,7 +1,10 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/update' describe "Digest::SHA512#<<" do - it_behaves_like :sha512_update, :<< + it "can update" do + cur_digest = Digest::SHA512.new + cur_digest << SHA512Constants::Contents + cur_digest.digest.should == SHA512Constants::Digest + end end diff --git a/spec/ruby/library/digest/sha512/length_spec.rb b/spec/ruby/library/digest/sha512/length_spec.rb index e9fde90577..8e909482c5 100644 --- a/spec/ruby/library/digest/sha512/length_spec.rb +++ b/spec/ruby/library/digest/sha512/length_spec.rb @@ -1,7 +1,11 @@ require_relative '../../../spec_helper' require_relative 'shared/constants' -require_relative 'shared/length' describe "Digest::SHA512#length" do - it_behaves_like :sha512_length, :length + it "returns the length of the digest" do + cur_digest = Digest::SHA512.new + cur_digest.length.should == SHA512Constants::BlankDigest.size + cur_digest << SHA512Constants::Contents + cur_digest.length.should == SHA512Constants::Digest.size + end end diff --git a/spec/ruby/library/digest/sha512/shared/length.rb b/spec/ruby/library/digest/sha512/shared/length.rb deleted file mode 100644 index c0609d5386..0000000000 --- a/spec/ruby/library/digest/sha512/shared/length.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :sha512_length, shared: true do - it "returns the length of the digest" do - cur_digest = Digest::SHA512.new - cur_digest.send(@method).should == SHA512Constants::BlankDigest.size - cur_digest << SHA512Constants::Contents - cur_digest.send(@method).should == SHA512Constants::Digest.size - end -end diff --git a/spec/ruby/library/digest/sha512/shared/update.rb b/spec/ruby/library/digest/sha512/shared/update.rb deleted file mode 100644 index ca74dbf4df..0000000000 --- a/spec/ruby/library/digest/sha512/shared/update.rb +++ /dev/null @@ -1,7 +0,0 @@ -describe :sha512_update, shared: true do - it "can update" do - cur_digest = Digest::SHA512.new - cur_digest.send @method, SHA512Constants::Contents - cur_digest.digest.should == SHA512Constants::Digest - end -end diff --git a/spec/ruby/library/digest/sha512/size_spec.rb b/spec/ruby/library/digest/sha512/size_spec.rb index 6d0acdabdb..498d686802 100644 --- a/spec/ruby/library/digest/sha512/size_spec.rb +++ b/spec/ruby/library/digest/sha512/size_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/constants' -require_relative 'shared/length' +require 'digest' describe "Digest::SHA512#size" do - it_behaves_like :sha512_length, :size + it "is an alias of Digest::SHA512#length" do + Digest::SHA512.instance_method(:size).should == Digest::SHA512.instance_method(:length) + end end diff --git a/spec/ruby/library/digest/sha512/update_spec.rb b/spec/ruby/library/digest/sha512/update_spec.rb index 682d3a19bb..33edf216ac 100644 --- a/spec/ruby/library/digest/sha512/update_spec.rb +++ b/spec/ruby/library/digest/sha512/update_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' -require_relative 'shared/constants' -require_relative 'shared/update' +require 'digest' describe "Digest::SHA512#update" do - it_behaves_like :sha512_update, :update + it "is an alias of Digest::SHA512#<<" do + Digest::SHA512.instance_method(:update).should == Digest::SHA512.instance_method(:<<) + end end diff --git a/spec/ruby/library/getoptlong/each_option_spec.rb b/spec/ruby/library/getoptlong/each_option_spec.rb index c6d82af86d..3349554aaa 100644 --- a/spec/ruby/library/getoptlong/each_option_spec.rb +++ b/spec/ruby/library/getoptlong/each_option_spec.rb @@ -1,7 +1,21 @@ require_relative '../../spec_helper' require 'getoptlong' -require_relative 'shared/each' describe "GetoptLong#each_option" do - it_behaves_like :getoptlong_each, :each_option + before :each do + @opts = GetoptLong.new( + [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], + [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], + [ '--query', '-q', GetoptLong::NO_ARGUMENT ], + [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] + ) + end + + it "passes each_option argument/value pair to the block" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + pairs = [] + @opts.each_option { |arg, val| pairs << [ arg, val ] } + pairs.should == [ [ "--size", "10k" ], [ "--verbose", "" ], [ "--query", ""] ] + end + end end diff --git a/spec/ruby/library/getoptlong/each_spec.rb b/spec/ruby/library/getoptlong/each_spec.rb index d9022f02af..646c3297b5 100644 --- a/spec/ruby/library/getoptlong/each_spec.rb +++ b/spec/ruby/library/getoptlong/each_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' require 'getoptlong' -require_relative 'shared/each' describe "GetoptLong#each" do - it_behaves_like :getoptlong_each, :each + it "is an alias of GetoptLong#each_option" do + GetoptLong.instance_method(:each).should == GetoptLong.instance_method(:each_option) + end end diff --git a/spec/ruby/library/getoptlong/get_option_spec.rb b/spec/ruby/library/getoptlong/get_option_spec.rb index 3cb2044379..1d80e3622e 100644 --- a/spec/ruby/library/getoptlong/get_option_spec.rb +++ b/spec/ruby/library/getoptlong/get_option_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' require 'getoptlong' -require_relative 'shared/get' describe "GetoptLong#get_option" do - it_behaves_like :getoptlong_get, :get_option + it "is an alias of GetoptLong#get" do + GetoptLong.instance_method(:get_option).should == GetoptLong.instance_method(:get) + end end diff --git a/spec/ruby/library/getoptlong/get_spec.rb b/spec/ruby/library/getoptlong/get_spec.rb index a8ec586fc9..bfc6697a5a 100644 --- a/spec/ruby/library/getoptlong/get_spec.rb +++ b/spec/ruby/library/getoptlong/get_spec.rb @@ -1,7 +1,65 @@ require_relative '../../spec_helper' require 'getoptlong' -require_relative 'shared/get' describe "GetoptLong#get" do - it_behaves_like :getoptlong_get, :get + before :each do + @opts = GetoptLong.new( + [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], + [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], + [ '--query', '-q', GetoptLong::NO_ARGUMENT ], + [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] + ) + @opts.quiet = true # silence using $deferr + end + + it "returns the next option name and its argument as an Array" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + @opts.get.should == [ "--size", "10k" ] + @opts.get.should == [ "--verbose", "" ] + @opts.get.should == [ "--query", ""] + @opts.get.should == nil + end + end + + it "shifts ARGV on each call" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + @opts.get + ARGV.should == [ "-v", "-q", "a.txt", "b.txt" ] + + @opts.get + ARGV.should == [ "-q", "a.txt", "b.txt" ] + + @opts.get + ARGV.should == [ "a.txt", "b.txt" ] + + @opts.get + ARGV.should == [ "a.txt", "b.txt" ] + end + end + + it "terminates processing when encountering '--'" do + argv [ "--size", "10k", "--", "-v", "-q", "a.txt", "b.txt" ] do + @opts.get + ARGV.should == ["--", "-v", "-q", "a.txt", "b.txt"] + + @opts.get + ARGV.should == ["-v", "-q", "a.txt", "b.txt"] + + @opts.get + ARGV.should == ["-v", "-q", "a.txt", "b.txt"] + end + end + + it "raises a if an argument was required, but none given" do + argv [ "--size" ] do + -> { @opts.get }.should.raise(GetoptLong::MissingArgument) + end + end + + # https://bugs.ruby-lang.org/issues/13858 + it "returns multiline argument" do + argv [ "--size=\n10k\n" ] do + @opts.get.should == [ "--size", "\n10k\n" ] + end + end end diff --git a/spec/ruby/library/getoptlong/shared/each.rb b/spec/ruby/library/getoptlong/shared/each.rb deleted file mode 100644 index b534e24c0f..0000000000 --- a/spec/ruby/library/getoptlong/shared/each.rb +++ /dev/null @@ -1,18 +0,0 @@ -describe :getoptlong_each, shared: true do - before :each do - @opts = GetoptLong.new( - [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], - [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], - [ '--query', '-q', GetoptLong::NO_ARGUMENT ], - [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] - ) - end - - it "passes each argument/value pair to the block" do - argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do - pairs = [] - @opts.send(@method) { |arg, val| pairs << [ arg, val ] } - pairs.should == [ [ "--size", "10k" ], [ "--verbose", "" ], [ "--query", ""] ] - end - end -end diff --git a/spec/ruby/library/getoptlong/shared/get.rb b/spec/ruby/library/getoptlong/shared/get.rb deleted file mode 100644 index 8d24c4c255..0000000000 --- a/spec/ruby/library/getoptlong/shared/get.rb +++ /dev/null @@ -1,62 +0,0 @@ -describe :getoptlong_get, shared: true do - before :each do - @opts = GetoptLong.new( - [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], - [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], - [ '--query', '-q', GetoptLong::NO_ARGUMENT ], - [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] - ) - @opts.quiet = true # silence using $deferr - end - - it "returns the next option name and its argument as an Array" do - argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do - @opts.send(@method).should == [ "--size", "10k" ] - @opts.send(@method).should == [ "--verbose", "" ] - @opts.send(@method).should == [ "--query", ""] - @opts.send(@method).should == nil - end - end - - it "shifts ARGV on each call" do - argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do - @opts.send(@method) - ARGV.should == [ "-v", "-q", "a.txt", "b.txt" ] - - @opts.send(@method) - ARGV.should == [ "-q", "a.txt", "b.txt" ] - - @opts.send(@method) - ARGV.should == [ "a.txt", "b.txt" ] - - @opts.send(@method) - ARGV.should == [ "a.txt", "b.txt" ] - end - end - - it "terminates processing when encountering '--'" do - argv [ "--size", "10k", "--", "-v", "-q", "a.txt", "b.txt" ] do - @opts.send(@method) - ARGV.should == ["--", "-v", "-q", "a.txt", "b.txt"] - - @opts.send(@method) - ARGV.should == ["-v", "-q", "a.txt", "b.txt"] - - @opts.send(@method) - ARGV.should == ["-v", "-q", "a.txt", "b.txt"] - end - end - - it "raises a if an argument was required, but none given" do - argv [ "--size" ] do - -> { @opts.send(@method) }.should.raise(GetoptLong::MissingArgument) - end - end - - # https://bugs.ruby-lang.org/issues/13858 - it "returns multiline argument" do - argv [ "--size=\n10k\n" ] do - @opts.send(@method).should == [ "--size", "\n10k\n" ] - end - end -end diff --git a/spec/ruby/library/matrix/I_spec.rb b/spec/ruby/library/matrix/I_spec.rb index 6eeffe8e98..ca5e79279a 100644 --- a/spec/ruby/library/matrix/I_spec.rb +++ b/spec/ruby/library/matrix/I_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/identity' +require 'matrix' describe "Matrix.I" do - it_behaves_like :matrix_identity, :I + it "is an alias of Matrix.identity" do + Matrix.method(:I).should == Matrix.method(:identity) + end end diff --git a/spec/ruby/library/matrix/collect_spec.rb b/spec/ruby/library/matrix/collect_spec.rb index bba640213b..664c3f3038 100644 --- a/spec/ruby/library/matrix/collect_spec.rb +++ b/spec/ruby/library/matrix/collect_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/collect' +require 'matrix' describe "Matrix#collect" do - it_behaves_like :collect, :collect + it "is an alias of Matrix#map" do + Matrix.instance_method(:collect).should == Matrix.instance_method(:map) + end end diff --git a/spec/ruby/library/matrix/conj_spec.rb b/spec/ruby/library/matrix/conj_spec.rb index ecee95c255..eff5986fc4 100644 --- a/spec/ruby/library/matrix/conj_spec.rb +++ b/spec/ruby/library/matrix/conj_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/conjugate' +require 'matrix' describe "Matrix#conj" do - it_behaves_like :matrix_conjugate, :conj + it "is an alias of Matrix#conjugate" do + Matrix.instance_method(:conj).should == Matrix.instance_method(:conjugate) + end end diff --git a/spec/ruby/library/matrix/conjugate_spec.rb b/spec/ruby/library/matrix/conjugate_spec.rb index 682bd41d94..46077d4fa9 100644 --- a/spec/ruby/library/matrix/conjugate_spec.rb +++ b/spec/ruby/library/matrix/conjugate_spec.rb @@ -1,6 +1,20 @@ require_relative '../../spec_helper' -require_relative 'shared/conjugate' +require_relative 'fixtures/classes' describe "Matrix#conjugate" do - it_behaves_like :matrix_conjugate, :conjugate + it "returns a matrix with all entries 'conjugated'" do + Matrix[ [1, 2], [3, 4] ].conjugate.should == Matrix[ [1, 2], [3, 4] ] + Matrix[ [1.9, Complex(1,1)], [3, 4] ].conjugate.should == Matrix[ [1.9, Complex(1,-1)], [3, 4] ] + end + + it "returns empty matrices on the same size if empty" do + Matrix.empty(0, 3).conjugate.should == Matrix.empty(0, 3) + Matrix.empty(3, 0).conjugate.should == Matrix.empty(3, 0) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.conjugate.should.instance_of?(MatrixSub) + end + end end diff --git a/spec/ruby/library/matrix/det_spec.rb b/spec/ruby/library/matrix/det_spec.rb index aa7086cacf..fc4b1c252a 100644 --- a/spec/ruby/library/matrix/det_spec.rb +++ b/spec/ruby/library/matrix/det_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/determinant' require 'matrix' describe "Matrix#det" do - it_behaves_like :determinant, :det + it "is an alias of Matrix#determinant" do + Matrix.instance_method(:det).should == Matrix.instance_method(:determinant) + end end diff --git a/spec/ruby/library/matrix/determinant_spec.rb b/spec/ruby/library/matrix/determinant_spec.rb index 825c9907b1..603e13ba28 100644 --- a/spec/ruby/library/matrix/determinant_spec.rb +++ b/spec/ruby/library/matrix/determinant_spec.rb @@ -1,7 +1,39 @@ require_relative '../../spec_helper' -require_relative 'shared/determinant' require 'matrix' describe "Matrix#determinant" do - it_behaves_like :determinant, :determinant + it "returns the determinant of a square Matrix" do + m = Matrix[ [7,6], [3,9] ] + m.determinant.should == 45 + + m = Matrix[ [9, 8], [6,5] ] + m.determinant.should == -3 + + m = Matrix[ [9,8,3], [4,20,5], [1,1,1] ] + m.determinant.should == 95 + end + + it "returns the determinant of a single-element Matrix" do + m = Matrix[ [2] ] + m.determinant.should == 2 + end + + it "returns 1 for an empty Matrix" do + m = Matrix[ ] + m.determinant.should == 1 + end + + it "returns the determinant even for Matrices containing 0 as first entry" do + Matrix[[0,1],[1,0]].determinant.should == -1 + end + + it "raises an error for rectangular matrices" do + -> { + Matrix[[1], [2], [3]].determinant + }.should.raise(Matrix::ErrDimensionMismatch) + + -> { + Matrix.empty(3,0).determinant + }.should.raise(Matrix::ErrDimensionMismatch) + end end diff --git a/spec/ruby/library/matrix/identity_spec.rb b/spec/ruby/library/matrix/identity_spec.rb index 646462bc47..afefd27565 100644 --- a/spec/ruby/library/matrix/identity_spec.rb +++ b/spec/ruby/library/matrix/identity_spec.rb @@ -1,6 +1,20 @@ require_relative '../../spec_helper' -require_relative 'shared/identity' +require_relative 'fixtures/classes' +require 'matrix' describe "Matrix.identity" do - it_behaves_like :matrix_identity, :identity + it "returns a Matrix" do + Matrix.identity(2).should.is_a?(Matrix) + end + + it "returns a n x n identity matrix" do + Matrix.identity(3).should == Matrix.scalar(3, 1) + Matrix.identity(100).should == Matrix.scalar(100, 1) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.identity(2).should.instance_of?(MatrixSub) + end + end end diff --git a/spec/ruby/library/matrix/imag_spec.rb b/spec/ruby/library/matrix/imag_spec.rb index 1c988753d8..9d6cc2e953 100644 --- a/spec/ruby/library/matrix/imag_spec.rb +++ b/spec/ruby/library/matrix/imag_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/imaginary' +require 'matrix' describe "Matrix#imag" do - it_behaves_like :matrix_imaginary, :imag + it "is an alias of Matrix#imaginary" do + Matrix.instance_method(:imag).should == Matrix.instance_method(:imaginary) + end end diff --git a/spec/ruby/library/matrix/imaginary_spec.rb b/spec/ruby/library/matrix/imaginary_spec.rb index ceae4bbe8d..bbd06677b7 100644 --- a/spec/ruby/library/matrix/imaginary_spec.rb +++ b/spec/ruby/library/matrix/imaginary_spec.rb @@ -1,6 +1,21 @@ require_relative '../../spec_helper' -require_relative 'shared/imaginary' +require_relative 'fixtures/classes' +require 'matrix' describe "Matrix#imaginary" do - it_behaves_like :matrix_imaginary, :imaginary + it "returns a matrix with the imaginary part of the elements of the receiver" do + Matrix[ [1, 2], [3, 4] ].imaginary.should == Matrix[ [0, 0], [0, 0] ] + Matrix[ [1.9, Complex(1,1)], [Complex(-2,0.42), 4] ].imaginary.should == Matrix[ [0, 1], [0.42, 0] ] + end + + it "returns empty matrices on the same size if empty" do + Matrix.empty(0, 3).imaginary.should == Matrix.empty(0, 3) + Matrix.empty(3, 0).imaginary.should == Matrix.empty(3, 0) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.imaginary.should.instance_of?(MatrixSub) + end + end end diff --git a/spec/ruby/library/matrix/inv_spec.rb b/spec/ruby/library/matrix/inv_spec.rb index 82879a6d82..02684030d2 100644 --- a/spec/ruby/library/matrix/inv_spec.rb +++ b/spec/ruby/library/matrix/inv_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'spec_helper' -require_relative 'shared/inverse' +require 'matrix' describe "Matrix#inv" do - it_behaves_like :inverse, :inv + it "is an alias of Matrix#inverse" do + Matrix.instance_method(:inv).should == Matrix.instance_method(:inverse) + end end diff --git a/spec/ruby/library/matrix/inverse_spec.rb b/spec/ruby/library/matrix/inverse_spec.rb index fa3fa7de8a..38b01b28fb 100644 --- a/spec/ruby/library/matrix/inverse_spec.rb +++ b/spec/ruby/library/matrix/inverse_spec.rb @@ -1,7 +1,39 @@ require_relative '../../spec_helper' require_relative 'spec_helper' -require_relative 'shared/inverse' +require_relative 'fixtures/classes' +require 'matrix' describe "Matrix#inverse" do - it_behaves_like :inverse, :inverse + it "returns a Matrix" do + Matrix[ [1,2], [2,1] ].inverse.should.instance_of?(Matrix) + end + + it "returns the inverse of the Matrix" do + Matrix[ + [1, 3, 3], [1, 4, 3], [1, 3, 4] + ].inverse.should == + Matrix[ + [7, -3, -3], [-1, 1, 0], [-1, 0, 1] + ] + end + + it "returns the inverse of the Matrix (other case)" do + Matrix[ + [1, 2, 3], [0, 1, 4], [5, 6, 0] + ].inverse.should be_close_to_matrix([ + [-24, 18, 5], [20, -15, -4], [-5, 4, 1] + ]) + end + + it "raises a ErrDimensionMismatch if the Matrix is not square" do + ->{ + Matrix[ [1,2,3], [1,2,3] ].inverse + }.should.raise(Matrix::ErrDimensionMismatch) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.inverse.should.instance_of?(MatrixSub) + end + end end diff --git a/spec/ruby/library/matrix/map_spec.rb b/spec/ruby/library/matrix/map_spec.rb index bc07c48cda..bae96db381 100644 --- a/spec/ruby/library/matrix/map_spec.rb +++ b/spec/ruby/library/matrix/map_spec.rb @@ -1,6 +1,26 @@ require_relative '../../spec_helper' -require_relative 'shared/collect' +require_relative 'fixtures/classes' describe "Matrix#map" do - it_behaves_like :collect, :map + before :all do + @m = Matrix[ [1, 2], [1, 2] ] + end + + it "returns an instance of Matrix" do + @m.map{|n| n * 2 }.should.is_a?(Matrix) + end + + it "returns a Matrix where each element is the result of the block" do + @m.map { |n| n * 2 }.should == Matrix[ [2, 4], [2, 4] ] + end + + it "returns an enumerator if no block is given" do + @m.map.should.instance_of?(Enumerator) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.map{1}.should.instance_of?(MatrixSub) + end + end end diff --git a/spec/ruby/library/matrix/rect_spec.rb b/spec/ruby/library/matrix/rect_spec.rb index 83a0404e47..b0ca3f0421 100644 --- a/spec/ruby/library/matrix/rect_spec.rb +++ b/spec/ruby/library/matrix/rect_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/rectangular' +require 'matrix' describe "Matrix#rect" do - it_behaves_like :matrix_rectangular, :rect + it "is an alias of Matrix#rectangular" do + Matrix.instance_method(:rect).should == Matrix.instance_method(:rectangular) + end end diff --git a/spec/ruby/library/matrix/rectangular_spec.rb b/spec/ruby/library/matrix/rectangular_spec.rb index a235fac640..c0732f96bc 100644 --- a/spec/ruby/library/matrix/rectangular_spec.rb +++ b/spec/ruby/library/matrix/rectangular_spec.rb @@ -1,6 +1,19 @@ require_relative '../../spec_helper' -require_relative 'shared/rectangular' +require_relative 'fixtures/classes' +require 'matrix' describe "Matrix#rectangular" do - it_behaves_like :matrix_rectangular, :rectangular + it "returns [receiver.real, receiver.imag]" do + m = Matrix[ [1.2, Complex(1,2)], [Complex(-2,0.42), 4] ] + m.rectangular.should == [m.real, m.imag] + + m = Matrix.empty(3, 0) + m.rectangular.should == [m.real, m.imag] + end + + describe "for a subclass of Matrix" do + it "returns instances of that subclass" do + MatrixSub.ins.rectangular.each{|m| m.should.instance_of?(MatrixSub) } + end + end end diff --git a/spec/ruby/library/matrix/shared/collect.rb b/spec/ruby/library/matrix/shared/collect.rb deleted file mode 100644 index 3a4dbe3a36..0000000000 --- a/spec/ruby/library/matrix/shared/collect.rb +++ /dev/null @@ -1,26 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :collect, shared: true do - before :all do - @m = Matrix[ [1, 2], [1, 2] ] - end - - it "returns an instance of Matrix" do - @m.send(@method){|n| n * 2 }.should.is_a?(Matrix) - end - - it "returns a Matrix where each element is the result of the block" do - @m.send(@method) { |n| n * 2 }.should == Matrix[ [2, 4], [2, 4] ] - end - - it "returns an enumerator if no block is given" do - @m.send(@method).should.instance_of?(Enumerator) - end - - describe "for a subclass of Matrix" do - it "returns an instance of that subclass" do - MatrixSub.ins.send(@method){1}.should.instance_of?(MatrixSub) - end - end -end diff --git a/spec/ruby/library/matrix/shared/conjugate.rb b/spec/ruby/library/matrix/shared/conjugate.rb deleted file mode 100644 index ffdf5ebca1..0000000000 --- a/spec/ruby/library/matrix/shared/conjugate.rb +++ /dev/null @@ -1,20 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :matrix_conjugate, shared: true do - it "returns a matrix with all entries 'conjugated'" do - Matrix[ [1, 2], [3, 4] ].send(@method).should == Matrix[ [1, 2], [3, 4] ] - Matrix[ [1.9, Complex(1,1)], [3, 4] ].send(@method).should == Matrix[ [1.9, Complex(1,-1)], [3, 4] ] - end - - it "returns empty matrices on the same size if empty" do - Matrix.empty(0, 3).send(@method).should == Matrix.empty(0, 3) - Matrix.empty(3, 0).send(@method).should == Matrix.empty(3, 0) - end - - describe "for a subclass of Matrix" do - it "returns an instance of that subclass" do - MatrixSub.ins.send(@method).should.instance_of?(MatrixSub) - end - end -end diff --git a/spec/ruby/library/matrix/shared/determinant.rb b/spec/ruby/library/matrix/shared/determinant.rb deleted file mode 100644 index b7c86393ba..0000000000 --- a/spec/ruby/library/matrix/shared/determinant.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'matrix' - -describe :determinant, shared: true do - it "returns the determinant of a square Matrix" do - m = Matrix[ [7,6], [3,9] ] - m.send(@method).should == 45 - - m = Matrix[ [9, 8], [6,5] ] - m.send(@method).should == -3 - - m = Matrix[ [9,8,3], [4,20,5], [1,1,1] ] - m.send(@method).should == 95 - end - - it "returns the determinant of a single-element Matrix" do - m = Matrix[ [2] ] - m.send(@method).should == 2 - end - - it "returns 1 for an empty Matrix" do - m = Matrix[ ] - m.send(@method).should == 1 - end - - it "returns the determinant even for Matrices containing 0 as first entry" do - Matrix[[0,1],[1,0]].send(@method).should == -1 - end - - it "raises an error for rectangular matrices" do - -> { - Matrix[[1], [2], [3]].send(@method) - }.should.raise(Matrix::ErrDimensionMismatch) - - -> { - Matrix.empty(3,0).send(@method) - }.should.raise(Matrix::ErrDimensionMismatch) - end -end diff --git a/spec/ruby/library/matrix/shared/identity.rb b/spec/ruby/library/matrix/shared/identity.rb deleted file mode 100644 index df957b5a75..0000000000 --- a/spec/ruby/library/matrix/shared/identity.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :matrix_identity, shared: true do - it "returns a Matrix" do - Matrix.send(@method, 2).should.is_a?(Matrix) - end - - it "returns a n x n identity matrix" do - Matrix.send(@method, 3).should == Matrix.scalar(3, 1) - Matrix.send(@method, 100).should == Matrix.scalar(100, 1) - end - - describe "for a subclass of Matrix" do - it "returns an instance of that subclass" do - MatrixSub.send(@method, 2).should.instance_of?(MatrixSub) - end - end -end diff --git a/spec/ruby/library/matrix/shared/imaginary.rb b/spec/ruby/library/matrix/shared/imaginary.rb deleted file mode 100644 index 16615213a2..0000000000 --- a/spec/ruby/library/matrix/shared/imaginary.rb +++ /dev/null @@ -1,20 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :matrix_imaginary, shared: true do - it "returns a matrix with the imaginary part of the elements of the receiver" do - Matrix[ [1, 2], [3, 4] ].send(@method).should == Matrix[ [0, 0], [0, 0] ] - Matrix[ [1.9, Complex(1,1)], [Complex(-2,0.42), 4] ].send(@method).should == Matrix[ [0, 1], [0.42, 0] ] - end - - it "returns empty matrices on the same size if empty" do - Matrix.empty(0, 3).send(@method).should == Matrix.empty(0, 3) - Matrix.empty(3, 0).send(@method).should == Matrix.empty(3, 0) - end - - describe "for a subclass of Matrix" do - it "returns an instance of that subclass" do - MatrixSub.ins.send(@method).should.instance_of?(MatrixSub) - end - end -end diff --git a/spec/ruby/library/matrix/shared/inverse.rb b/spec/ruby/library/matrix/shared/inverse.rb deleted file mode 100644 index ac463cf680..0000000000 --- a/spec/ruby/library/matrix/shared/inverse.rb +++ /dev/null @@ -1,38 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :inverse, shared: true do - - it "returns a Matrix" do - Matrix[ [1,2], [2,1] ].send(@method).should.instance_of?(Matrix) - end - - it "returns the inverse of the Matrix" do - Matrix[ - [1, 3, 3], [1, 4, 3], [1, 3, 4] - ].send(@method).should == - Matrix[ - [7, -3, -3], [-1, 1, 0], [-1, 0, 1] - ] - end - - it "returns the inverse of the Matrix (other case)" do - Matrix[ - [1, 2, 3], [0, 1, 4], [5, 6, 0] - ].send(@method).should be_close_to_matrix([ - [-24, 18, 5], [20, -15, -4], [-5, 4, 1] - ]) - end - - it "raises a ErrDimensionMismatch if the Matrix is not square" do - ->{ - Matrix[ [1,2,3], [1,2,3] ].send(@method) - }.should.raise(Matrix::ErrDimensionMismatch) - end - - describe "for a subclass of Matrix" do - it "returns an instance of that subclass" do - MatrixSub.ins.send(@method).should.instance_of?(MatrixSub) - end - end -end diff --git a/spec/ruby/library/matrix/shared/rectangular.rb b/spec/ruby/library/matrix/shared/rectangular.rb deleted file mode 100644 index 0229e614c6..0000000000 --- a/spec/ruby/library/matrix/shared/rectangular.rb +++ /dev/null @@ -1,18 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :matrix_rectangular, shared: true do - it "returns [receiver.real, receiver.imag]" do - m = Matrix[ [1.2, Complex(1,2)], [Complex(-2,0.42), 4] ] - m.send(@method).should == [m.real, m.imag] - - m = Matrix.empty(3, 0) - m.send(@method).should == [m.real, m.imag] - end - - describe "for a subclass of Matrix" do - it "returns instances of that subclass" do - MatrixSub.ins.send(@method).each{|m| m.should.instance_of?(MatrixSub) } - end - end -end diff --git a/spec/ruby/library/matrix/shared/trace.rb b/spec/ruby/library/matrix/shared/trace.rb deleted file mode 100644 index c4a5491b22..0000000000 --- a/spec/ruby/library/matrix/shared/trace.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'matrix' - -describe :trace, shared: true do - it "returns the sum of diagonal elements in a square Matrix" do - Matrix[[7,6], [3,9]].trace.should == 16 - end - - it "returns the sum of diagonal elements in a rectangular Matrix" do - ->{ Matrix[[1,2,3], [4,5,6]].trace}.should.raise(Matrix::ErrDimensionMismatch) - end - -end diff --git a/spec/ruby/library/matrix/shared/transpose.rb b/spec/ruby/library/matrix/shared/transpose.rb deleted file mode 100644 index a0b495359b..0000000000 --- a/spec/ruby/library/matrix/shared/transpose.rb +++ /dev/null @@ -1,19 +0,0 @@ -require_relative '../fixtures/classes' -require 'matrix' - -describe :matrix_transpose, shared: true do - it "returns a transposed matrix" do - Matrix[[1, 2], [3, 4], [5, 6]].send(@method).should == Matrix[[1, 3, 5], [2, 4, 6]] - end - - it "can transpose empty matrices" do - m = Matrix[[], [], []] - m.send(@method).send(@method).should == m - end - - describe "for a subclass of Matrix" do - it "returns an instance of that subclass" do - MatrixSub.ins.send(@method).should.instance_of?(MatrixSub) - end - end -end diff --git a/spec/ruby/library/matrix/t_spec.rb b/spec/ruby/library/matrix/t_spec.rb index 6f1a5178e0..9411597e7c 100644 --- a/spec/ruby/library/matrix/t_spec.rb +++ b/spec/ruby/library/matrix/t_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/transpose' +require 'matrix' -describe "Matrix#transpose" do - it_behaves_like :matrix_transpose, :t +describe "Matrix#t" do + it "is an alias of Matrix#transpose" do + Matrix.instance_method(:t).should == Matrix.instance_method(:transpose) + end end diff --git a/spec/ruby/library/matrix/tr_spec.rb b/spec/ruby/library/matrix/tr_spec.rb index e17bd790d7..04d237d483 100644 --- a/spec/ruby/library/matrix/tr_spec.rb +++ b/spec/ruby/library/matrix/tr_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/trace' require 'matrix' describe "Matrix#tr" do - it_behaves_like :trace, :tr + it "is an alias of Matrix#trace" do + Matrix.instance_method(:tr).should == Matrix.instance_method(:trace) + end end diff --git a/spec/ruby/library/matrix/trace_spec.rb b/spec/ruby/library/matrix/trace_spec.rb index 290e7cb1f7..831278c838 100644 --- a/spec/ruby/library/matrix/trace_spec.rb +++ b/spec/ruby/library/matrix/trace_spec.rb @@ -1,7 +1,12 @@ require_relative '../../spec_helper' -require_relative 'shared/trace' require 'matrix' describe "Matrix#trace" do - it_behaves_like :trace, :trace + it "returns the sum of diagonal elements in a square Matrix" do + Matrix[[7,6], [3,9]].trace.should == 16 + end + + it "returns the sum of diagonal elements in a rectangular Matrix" do + ->{ Matrix[[1,2,3], [4,5,6]].trace}.should.raise(Matrix::ErrDimensionMismatch) + end end diff --git a/spec/ruby/library/matrix/transpose_spec.rb b/spec/ruby/library/matrix/transpose_spec.rb index 79600dd439..0b24ab32a7 100644 --- a/spec/ruby/library/matrix/transpose_spec.rb +++ b/spec/ruby/library/matrix/transpose_spec.rb @@ -1,6 +1,19 @@ require_relative '../../spec_helper' -require_relative 'shared/transpose' +require_relative 'fixtures/classes' describe "Matrix#transpose" do - it_behaves_like :matrix_transpose, :transpose + it "returns a transposed matrix" do + Matrix[[1, 2], [3, 4], [5, 6]].transpose.should == Matrix[[1, 3, 5], [2, 4, 6]] + end + + it "can transpose empty matrices" do + m = Matrix[[], [], []] + m.transpose.transpose.should == m + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.transpose.should.instance_of?(MatrixSub) + end + end end diff --git a/spec/ruby/library/matrix/unit_spec.rb b/spec/ruby/library/matrix/unit_spec.rb index 6a41d729c7..1121996122 100644 --- a/spec/ruby/library/matrix/unit_spec.rb +++ b/spec/ruby/library/matrix/unit_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/identity' +require 'matrix' describe "Matrix.unit" do - it_behaves_like :matrix_identity, :unit + it "is an alias of Matrix.identity" do + Matrix.method(:unit).should == Matrix.method(:identity) + end end diff --git a/spec/ruby/library/net-http/http/active_spec.rb b/spec/ruby/library/net-http/http/active_spec.rb index c260274594..ba870b39d2 100644 --- a/spec/ruby/library/net-http/http/active_spec.rb +++ b/spec/ruby/library/net-http/http/active_spec.rb @@ -1,8 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/http_server' -require_relative 'shared/started' describe "Net::HTTP#active?" do - it_behaves_like :net_http_started_p, :active? + it "is an alias of Net::HTTP#started?" do + Net::HTTP.instance_method(:active?).should == Net::HTTP.instance_method(:started?) + end end diff --git a/spec/ruby/library/net-http/http/get2_spec.rb b/spec/ruby/library/net-http/http/get2_spec.rb index 57c05ec64b..046443d73e 100644 --- a/spec/ruby/library/net-http/http/get2_spec.rb +++ b/spec/ruby/library/net-http/http/get2_spec.rb @@ -1,8 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/http_server' -require_relative 'shared/request_get' describe "Net::HTTP#get2" do - it_behaves_like :net_http_request_get, :get2 + it "is an alias of Net::HTTP#request_get" do + Net::HTTP.instance_method(:get2).should == Net::HTTP.instance_method(:request_get) + end end diff --git a/spec/ruby/library/net-http/http/head2_spec.rb b/spec/ruby/library/net-http/http/head2_spec.rb index 84cfff33d7..19c0cede9f 100644 --- a/spec/ruby/library/net-http/http/head2_spec.rb +++ b/spec/ruby/library/net-http/http/head2_spec.rb @@ -1,8 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/http_server' -require_relative 'shared/request_head' describe "Net::HTTP#head2" do - it_behaves_like :net_http_request_head, :head2 + it "is an alias of Net::HTTP#request_head" do + Net::HTTP.instance_method(:head2).should == Net::HTTP.instance_method(:request_head) + end end diff --git a/spec/ruby/library/net-http/http/is_version_1_1_spec.rb b/spec/ruby/library/net-http/http/is_version_1_1_spec.rb index bdb343f9e0..f4910ef1e4 100644 --- a/spec/ruby/library/net-http/http/is_version_1_1_spec.rb +++ b/spec/ruby/library/net-http/http/is_version_1_1_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'shared/version_1_1' describe "Net::HTTP.is_version_1_1?" do - it_behaves_like :net_http_version_1_1_p, :is_version_1_1? + it "is an alias of Net::HTTP.version_1_1?" do + Net::HTTP.method(:is_version_1_1?).should == Net::HTTP.method(:version_1_1?) + end end diff --git a/spec/ruby/library/net-http/http/is_version_1_2_spec.rb b/spec/ruby/library/net-http/http/is_version_1_2_spec.rb index 555bb205dd..555724babe 100644 --- a/spec/ruby/library/net-http/http/is_version_1_2_spec.rb +++ b/spec/ruby/library/net-http/http/is_version_1_2_spec.rb @@ -1,7 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'shared/version_1_2' describe "Net::HTTP.is_version_1_2?" do - it_behaves_like :net_http_version_1_2_p, :is_version_1_2? + it "is an alias of Net::HTTP.version_1_2?" do + Net::HTTP.method(:is_version_1_2?).should == Net::HTTP.method(:version_1_2?) + end end diff --git a/spec/ruby/library/net-http/http/post2_spec.rb b/spec/ruby/library/net-http/http/post2_spec.rb index abc998709f..68c2a9ea06 100644 --- a/spec/ruby/library/net-http/http/post2_spec.rb +++ b/spec/ruby/library/net-http/http/post2_spec.rb @@ -1,8 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/http_server' -require_relative 'shared/request_post' describe "Net::HTTP#post2" do - it_behaves_like :net_http_request_post, :post2 + it "is an alias of Net::HTTP#request_post" do + Net::HTTP.instance_method(:post2).should == Net::HTTP.instance_method(:request_post) + end end diff --git a/spec/ruby/library/net-http/http/put2_spec.rb b/spec/ruby/library/net-http/http/put2_spec.rb index 7b03a39d0b..237df67e82 100644 --- a/spec/ruby/library/net-http/http/put2_spec.rb +++ b/spec/ruby/library/net-http/http/put2_spec.rb @@ -1,8 +1,8 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/http_server' -require_relative 'shared/request_put' describe "Net::HTTP#put2" do - it_behaves_like :net_http_request_put, :put2 + it "is an alias of Net::HTTP#request_put" do + Net::HTTP.instance_method(:put2).should == Net::HTTP.instance_method(:request_put) + end end diff --git a/spec/ruby/library/net-http/http/request_get_spec.rb b/spec/ruby/library/net-http/http/request_get_spec.rb index 98025a14a1..1737e62439 100644 --- a/spec/ruby/library/net-http/http/request_get_spec.rb +++ b/spec/ruby/library/net-http/http/request_get_spec.rb @@ -1,8 +1,45 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/http_server' -require_relative 'shared/request_get' describe "Net::HTTP#request_get" do - it_behaves_like :net_http_request_get, :get2 + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a GET request to the passed path and returns the response" do + response = @http.request_get("/request") + response.body.should == "Request type: GET" + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_get("/request") + response.should.is_a?(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a GET request to the passed path and returns the response" do + response = @http.request_get("/request") {} + response.body.should == "Request type: GET" + end + + it "yields the response to the passed block" do + @http.request_get("/request") do |response| + response.body.should == "Request type: GET" + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_get("/request") {} + response.should.is_a?(Net::HTTPResponse) + end + end end diff --git a/spec/ruby/library/net-http/http/request_head_spec.rb b/spec/ruby/library/net-http/http/request_head_spec.rb index 8f514d4eee..7c46ebfc53 100644 --- a/spec/ruby/library/net-http/http/request_head_spec.rb +++ b/spec/ruby/library/net-http/http/request_head_spec.rb @@ -1,8 +1,45 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/http_server' -require_relative 'shared/request_head' describe "Net::HTTP#request_head" do - it_behaves_like :net_http_request_head, :request_head + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a head request to the passed path and returns the response" do + response = @http.request_head("/request") + response.body.should == nil + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_head("/request") + response.should.is_a?(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a head request to the passed path and returns the response" do + response = @http.request_head("/request") {} + response.body.should == nil + end + + it "yields the response to the passed block" do + @http.request_head("/request") do |response| + response.body.should == nil + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_head("/request") {} + response.should.is_a?(Net::HTTPResponse) + end + end end diff --git a/spec/ruby/library/net-http/http/request_post_spec.rb b/spec/ruby/library/net-http/http/request_post_spec.rb index 719bd5a7ee..8cfdd3469e 100644 --- a/spec/ruby/library/net-http/http/request_post_spec.rb +++ b/spec/ruby/library/net-http/http/request_post_spec.rb @@ -1,8 +1,45 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/http_server' -require_relative 'shared/request_post' describe "Net::HTTP#request_post" do - it_behaves_like :net_http_request_post, :request_post + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a post request to the passed path and returns the response" do + response = @http.request_post("/request", "test=test") + response.body.should == "Request type: POST" + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_post("/request", "test=test") + response.should.is_a?(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a post request to the passed path and returns the response" do + response = @http.request_post("/request", "test=test") {} + response.body.should == "Request type: POST" + end + + it "yields the response to the passed block" do + @http.request_post("/request", "test=test") do |response| + response.body.should == "Request type: POST" + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_post("/request", "test=test") {} + response.should.is_a?(Net::HTTPResponse) + end + end end diff --git a/spec/ruby/library/net-http/http/request_put_spec.rb b/spec/ruby/library/net-http/http/request_put_spec.rb index 9fcf3a98d6..b7388a21c8 100644 --- a/spec/ruby/library/net-http/http/request_put_spec.rb +++ b/spec/ruby/library/net-http/http/request_put_spec.rb @@ -1,8 +1,45 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/http_server' -require_relative 'shared/request_put' describe "Net::HTTP#request_put" do - it_behaves_like :net_http_request_put, :request_put + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a put request to the passed path and returns the response" do + response = @http.request_put("/request", "test=test") + response.body.should == "Request type: PUT" + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_put("/request", "test=test") + response.should.is_a?(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a put request to the passed path and returns the response" do + response = @http.request_put("/request", "test=test") {} + response.body.should == "Request type: PUT" + end + + it "yields the response to the passed block" do + @http.request_put("/request", "test=test") do |response| + response.body.should == "Request type: PUT" + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.request_put("/request", "test=test") {} + response.should.is_a?(Net::HTTPResponse) + end + end end diff --git a/spec/ruby/library/net-http/http/shared/request_get.rb b/spec/ruby/library/net-http/http/shared/request_get.rb deleted file mode 100644 index 4f2f152ea4..0000000000 --- a/spec/ruby/library/net-http/http/shared/request_get.rb +++ /dev/null @@ -1,41 +0,0 @@ -describe :net_http_request_get, shared: true do - before :each do - NetHTTPSpecs.start_server - @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) - end - - after :each do - @http.finish if @http.started? - NetHTTPSpecs.stop_server - end - - describe "when passed no block" do - it "sends a GET request to the passed path and returns the response" do - response = @http.send(@method, "/request") - response.body.should == "Request type: GET" - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request") - response.should.is_a?(Net::HTTPResponse) - end - end - - describe "when passed a block" do - it "sends a GET request to the passed path and returns the response" do - response = @http.send(@method, "/request") {} - response.body.should == "Request type: GET" - end - - it "yields the response to the passed block" do - @http.send(@method, "/request") do |response| - response.body.should == "Request type: GET" - end - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request") {} - response.should.is_a?(Net::HTTPResponse) - end - end -end diff --git a/spec/ruby/library/net-http/http/shared/request_head.rb b/spec/ruby/library/net-http/http/shared/request_head.rb deleted file mode 100644 index a5fe787d32..0000000000 --- a/spec/ruby/library/net-http/http/shared/request_head.rb +++ /dev/null @@ -1,41 +0,0 @@ -describe :net_http_request_head, shared: true do - before :each do - NetHTTPSpecs.start_server - @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) - end - - after :each do - @http.finish if @http.started? - NetHTTPSpecs.stop_server - end - - describe "when passed no block" do - it "sends a head request to the passed path and returns the response" do - response = @http.send(@method, "/request") - response.body.should == nil - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request") - response.should.is_a?(Net::HTTPResponse) - end - end - - describe "when passed a block" do - it "sends a head request to the passed path and returns the response" do - response = @http.send(@method, "/request") {} - response.body.should == nil - end - - it "yields the response to the passed block" do - @http.send(@method, "/request") do |response| - response.body.should == nil - end - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request") {} - response.should.is_a?(Net::HTTPResponse) - end - end -end diff --git a/spec/ruby/library/net-http/http/shared/request_post.rb b/spec/ruby/library/net-http/http/shared/request_post.rb deleted file mode 100644 index 73cfd577cf..0000000000 --- a/spec/ruby/library/net-http/http/shared/request_post.rb +++ /dev/null @@ -1,41 +0,0 @@ -describe :net_http_request_post, shared: true do - before :each do - NetHTTPSpecs.start_server - @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) - end - - after :each do - @http.finish if @http.started? - NetHTTPSpecs.stop_server - end - - describe "when passed no block" do - it "sends a post request to the passed path and returns the response" do - response = @http.send(@method, "/request", "test=test") - response.body.should == "Request type: POST" - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request", "test=test") - response.should.is_a?(Net::HTTPResponse) - end - end - - describe "when passed a block" do - it "sends a post request to the passed path and returns the response" do - response = @http.send(@method, "/request", "test=test") {} - response.body.should == "Request type: POST" - end - - it "yields the response to the passed block" do - @http.send(@method, "/request", "test=test") do |response| - response.body.should == "Request type: POST" - end - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request", "test=test") {} - response.should.is_a?(Net::HTTPResponse) - end - end -end diff --git a/spec/ruby/library/net-http/http/shared/request_put.rb b/spec/ruby/library/net-http/http/shared/request_put.rb deleted file mode 100644 index 3b64d7e055..0000000000 --- a/spec/ruby/library/net-http/http/shared/request_put.rb +++ /dev/null @@ -1,41 +0,0 @@ -describe :net_http_request_put, shared: true do - before :each do - NetHTTPSpecs.start_server - @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) - end - - after :each do - @http.finish if @http.started? - NetHTTPSpecs.stop_server - end - - describe "when passed no block" do - it "sends a put request to the passed path and returns the response" do - response = @http.send(@method, "/request", "test=test") - response.body.should == "Request type: PUT" - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request", "test=test") - response.should.is_a?(Net::HTTPResponse) - end - end - - describe "when passed a block" do - it "sends a put request to the passed path and returns the response" do - response = @http.send(@method, "/request", "test=test") {} - response.body.should == "Request type: PUT" - end - - it "yields the response to the passed block" do - @http.send(@method, "/request", "test=test") do |response| - response.body.should == "Request type: PUT" - end - end - - it "returns a Net::HTTPResponse object" do - response = @http.send(@method, "/request", "test=test") {} - response.should.is_a?(Net::HTTPResponse) - end - end -end diff --git a/spec/ruby/library/net-http/http/shared/started.rb b/spec/ruby/library/net-http/http/shared/started.rb deleted file mode 100644 index 0ab18a4e83..0000000000 --- a/spec/ruby/library/net-http/http/shared/started.rb +++ /dev/null @@ -1,26 +0,0 @@ -describe :net_http_started_p, shared: true do - before :each do - NetHTTPSpecs.start_server - @http = Net::HTTP.new("localhost", NetHTTPSpecs.port) - end - - after :each do - @http.finish if @http.started? - NetHTTPSpecs.stop_server - end - - it "returns true when self has been started" do - @http.start - @http.send(@method).should == true - end - - it "returns false when self has not been started yet" do - @http.send(@method).should == false - end - - it "returns false when self has been stopped again" do - @http.start - @http.finish - @http.send(@method).should == false - end -end diff --git a/spec/ruby/library/net-http/http/shared/version_1_1.rb b/spec/ruby/library/net-http/http/shared/version_1_1.rb deleted file mode 100644 index 84e7264eab..0000000000 --- a/spec/ruby/library/net-http/http/shared/version_1_1.rb +++ /dev/null @@ -1,6 +0,0 @@ -describe :net_http_version_1_1_p, shared: true do - it "returns the state of net/http 1.1 features" do - Net::HTTP.version_1_2 - Net::HTTP.send(@method).should == false - end -end diff --git a/spec/ruby/library/net-http/http/shared/version_1_2.rb b/spec/ruby/library/net-http/http/shared/version_1_2.rb deleted file mode 100644 index dcf5419704..0000000000 --- a/spec/ruby/library/net-http/http/shared/version_1_2.rb +++ /dev/null @@ -1,6 +0,0 @@ -describe :net_http_version_1_2_p, shared: true do - it "returns the state of net/http 1.2 features" do - Net::HTTP.version_1_2 - Net::HTTP.send(@method).should == true - end -end diff --git a/spec/ruby/library/net-http/http/started_spec.rb b/spec/ruby/library/net-http/http/started_spec.rb index cbb82ceefa..a0b46fcbd2 100644 --- a/spec/ruby/library/net-http/http/started_spec.rb +++ b/spec/ruby/library/net-http/http/started_spec.rb @@ -1,8 +1,30 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/http_server' -require_relative 'shared/started' describe "Net::HTTP#started?" do - it_behaves_like :net_http_started_p, :started? + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.new("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "returns true when self has been started" do + @http.start + @http.started?.should == true + end + + it "returns false when self has not been started yet" do + @http.started?.should == false + end + + it "returns false when self has been stopped again" do + @http.start + @http.finish + @http.started?.should == false + end end diff --git a/spec/ruby/library/net-http/http/version_1_1_spec.rb b/spec/ruby/library/net-http/http/version_1_1_spec.rb index 34a4ac8a6b..7f87bf30f9 100644 --- a/spec/ruby/library/net-http/http/version_1_1_spec.rb +++ b/spec/ruby/library/net-http/http/version_1_1_spec.rb @@ -1,7 +1,9 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'shared/version_1_1' describe "Net::HTTP.version_1_1?" do - it_behaves_like :net_http_version_1_1_p, :version_1_1? + it "returns the state of net/http 1.1 features" do + Net::HTTP.version_1_2 + Net::HTTP.version_1_1?.should == false + end end diff --git a/spec/ruby/library/net-http/http/version_1_2_spec.rb b/spec/ruby/library/net-http/http/version_1_2_spec.rb index 4918597234..73ca70ac7b 100644 --- a/spec/ruby/library/net-http/http/version_1_2_spec.rb +++ b/spec/ruby/library/net-http/http/version_1_2_spec.rb @@ -1,6 +1,5 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'shared/version_1_2' describe "Net::HTTP.version_1_2" do it "turns on net/http 1.2 features" do @@ -16,5 +15,8 @@ describe "Net::HTTP.version_1_2" do end describe "Net::HTTP.version_1_2?" do - it_behaves_like :net_http_version_1_2_p, :version_1_2? + it "returns the state of net/http 1.2 features" do + Net::HTTP.version_1_2 + Net::HTTP.version_1_2?.should == true + end end diff --git a/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb b/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb index 64a5cae89e..c009e9f7ea 100644 --- a/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb +++ b/spec/ruby/library/net-http/httpheader/canonical_each_spec.rb @@ -1,8 +1,9 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/classes' -require_relative 'shared/each_capitalized' describe "Net::HTTPHeader#canonical_each" do - it_behaves_like :net_httpheader_each_capitalized, :canonical_each + it "is an alias of Net::HTTPHeader#each_capitalized" do + Net::HTTPHeader.instance_method(:canonical_each).should == + Net::HTTPHeader.instance_method(:each_capitalized) + end end diff --git a/spec/ruby/library/net-http/httpheader/content_type_spec.rb b/spec/ruby/library/net-http/httpheader/content_type_spec.rb index c9c936ba0f..0ee43a6942 100644 --- a/spec/ruby/library/net-http/httpheader/content_type_spec.rb +++ b/spec/ruby/library/net-http/httpheader/content_type_spec.rb @@ -1,7 +1,6 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/set_content_type' describe "Net::HTTPHeader#content_type" do before :each do @@ -22,5 +21,8 @@ describe "Net::HTTPHeader#content_type" do end describe "Net::HTTPHeader#content_type=" do - it_behaves_like :net_httpheader_set_content_type, :content_type= + it "is an alias of Net::HTTPHeader#set_content_type" do + Net::HTTPHeader.instance_method(:content_type=).should == + Net::HTTPHeader.instance_method(:set_content_type) + end end diff --git a/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb b/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb index 1e853995ea..e24e778238 100644 --- a/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb +++ b/spec/ruby/library/net-http/httpheader/each_capitalized_spec.rb @@ -1,8 +1,35 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/each_capitalized' describe "Net::HTTPHeader#each_capitalized" do - it_behaves_like :net_httpheader_each_capitalized, :each_capitalized + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["my-header"] = "test" + @headers.add_field("my-Other-Header", "a") + @headers.add_field("My-Other-header", "b") + end + + describe "when passed a block" do + it "yields each header entry to the passed block (capitalized keys, values joined)" do + res = [] + @headers.each_capitalized do |key, value| + res << [key, value] + end + res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.each_capitalized + enumerator.should.instance_of?(Enumerator) + + res = [] + enumerator.each do |*key| + res << key + end + res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]] + end + end end diff --git a/spec/ruby/library/net-http/httpheader/each_header_spec.rb b/spec/ruby/library/net-http/httpheader/each_header_spec.rb index 869feebacf..63c1106d18 100644 --- a/spec/ruby/library/net-http/httpheader/each_header_spec.rb +++ b/spec/ruby/library/net-http/httpheader/each_header_spec.rb @@ -1,8 +1,35 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/each_header' describe "Net::HTTPHeader#each_header" do - it_behaves_like :net_httpheader_each_header, :each_header + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["My-Header"] = "test" + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + end + + describe "when passed a block" do + it "yields each header entry to the passed block (keys in lower case, values joined)" do + res = [] + @headers.each_header do |key, value| + res << [key, value] + end + res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.each_header + enumerator.should.instance_of?(Enumerator) + + res = [] + enumerator.each do |*key| + res << key + end + res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]] + end + end end diff --git a/spec/ruby/library/net-http/httpheader/each_key_spec.rb b/spec/ruby/library/net-http/httpheader/each_key_spec.rb index 1ad145629f..a5635da5db 100644 --- a/spec/ruby/library/net-http/httpheader/each_key_spec.rb +++ b/spec/ruby/library/net-http/httpheader/each_key_spec.rb @@ -1,8 +1,35 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/each_name' describe "Net::HTTPHeader#each_key" do - it_behaves_like :net_httpheader_each_name, :each_key + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["My-Header"] = "test" + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + end + + describe "when passed a block" do + it "yields each header key to the passed block (keys in lower case)" do + res = [] + @headers.each_key do |key| + res << key + end + res.sort.should == ["my-header", "my-other-header"] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.each_key + enumerator.should.instance_of?(Enumerator) + + res = [] + enumerator.each do |key| + res << key + end + res.sort.should == ["my-header", "my-other-header"] + end + end end diff --git a/spec/ruby/library/net-http/httpheader/each_name_spec.rb b/spec/ruby/library/net-http/httpheader/each_name_spec.rb index f819bd989d..02f9761f80 100644 --- a/spec/ruby/library/net-http/httpheader/each_name_spec.rb +++ b/spec/ruby/library/net-http/httpheader/each_name_spec.rb @@ -1,8 +1,10 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/each_name' describe "Net::HTTPHeader#each_name" do - it_behaves_like :net_httpheader_each_name, :each_name + it "is an alias of Net::HTTPHeader#each_key" do + Net::HTTPHeader.instance_method(:each_name).should == + Net::HTTPHeader.instance_method(:each_key) + end end diff --git a/spec/ruby/library/net-http/httpheader/each_spec.rb b/spec/ruby/library/net-http/httpheader/each_spec.rb index ff37249d0a..e219609b67 100644 --- a/spec/ruby/library/net-http/httpheader/each_spec.rb +++ b/spec/ruby/library/net-http/httpheader/each_spec.rb @@ -1,8 +1,9 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/classes' -require_relative 'shared/each_header' describe "Net::HTTPHeader#each" do - it_behaves_like :net_httpheader_each_header, :each + it "is an alias of Net::HTTPHeader#each_header" do + Net::HTTPHeader.instance_method(:each).should == + Net::HTTPHeader.instance_method(:each_header) + end end diff --git a/spec/ruby/library/net-http/httpheader/form_data_spec.rb b/spec/ruby/library/net-http/httpheader/form_data_spec.rb index acd913f53a..8d4974cde3 100644 --- a/spec/ruby/library/net-http/httpheader/form_data_spec.rb +++ b/spec/ruby/library/net-http/httpheader/form_data_spec.rb @@ -1,8 +1,10 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/set_form_data' describe "Net::HTTPHeader#form_data=" do - it_behaves_like :net_httpheader_set_form_data, :form_data= + it "is an alias of Net::HTTPHeader#set_form_data" do + Net::HTTPHeader.instance_method(:form_data=).should == + Net::HTTPHeader.instance_method(:set_form_data) + end end diff --git a/spec/ruby/library/net-http/httpheader/length_spec.rb b/spec/ruby/library/net-http/httpheader/length_spec.rb index 57e32742e4..1f059719e9 100644 --- a/spec/ruby/library/net-http/httpheader/length_spec.rb +++ b/spec/ruby/library/net-http/httpheader/length_spec.rb @@ -1,8 +1,9 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'fixtures/classes' -require_relative 'shared/size' describe "Net::HTTPHeader#length" do - it_behaves_like :net_httpheader_size, :length + it "is an alias of Net::HTTPHeader#size" do + Net::HTTPHeader.instance_method(:length).should == + Net::HTTPHeader.instance_method(:size) + end end diff --git a/spec/ruby/library/net-http/httpheader/range_spec.rb b/spec/ruby/library/net-http/httpheader/range_spec.rb index 0fc0feb5c9..8944e2d5f2 100644 --- a/spec/ruby/library/net-http/httpheader/range_spec.rb +++ b/spec/ruby/library/net-http/httpheader/range_spec.rb @@ -1,7 +1,6 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/set_range' describe "Net::HTTPHeader#range" do before :each do @@ -44,5 +43,8 @@ describe "Net::HTTPHeader#range" do end describe "Net::HTTPHeader#range=" do - it_behaves_like :net_httpheader_set_range, :range= + it "is an alias of Net::HTTPHeader#set_range" do + Net::HTTPHeader.instance_method(:range=).should == + Net::HTTPHeader.instance_method(:set_range) + end end diff --git a/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb b/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb index 7ec4f90b8e..3674061626 100644 --- a/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb +++ b/spec/ruby/library/net-http/httpheader/set_content_type_spec.rb @@ -1,8 +1,22 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/set_content_type' describe "Net::HTTPHeader#set_content_type" do - it_behaves_like :net_httpheader_set_content_type, :set_content_type + describe "when passed type, params" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "sets the 'Content-Type' header entry based on the passed type and params" do + @headers.set_content_type("text/html") + @headers["Content-Type"].should == "text/html" + + @headers.set_content_type("text/html", "charset" => "utf-8") + @headers["Content-Type"].should == "text/html; charset=utf-8" + + @headers.set_content_type("text/html", "charset" => "utf-8", "rubyspec" => "rocks") + @headers["Content-Type"].split(/; /).sort.should == %w[charset=utf-8 rubyspec=rocks text/html] + end + end end diff --git a/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb b/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb index 7aac19f045..093dc100d5 100644 --- a/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb +++ b/spec/ruby/library/net-http/httpheader/set_form_data_spec.rb @@ -1,8 +1,31 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/set_form_data' describe "Net::HTTPHeader#set_form_data" do - it_behaves_like :net_httpheader_set_form_data, :set_form_data + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + describe "when passed params" do + it "automatically set the 'Content-Type' to 'application/x-www-form-urlencoded'" do + @headers.set_form_data("cmd" => "search", "q" => "ruby", "max" => "50") + @headers["Content-Type"].should == "application/x-www-form-urlencoded" + end + + it "sets self's body based on the passed form parameters" do + @headers.set_form_data("cmd" => "search", "q" => "ruby", "max" => "50") + @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"] + end + end + + describe "when passed params, separator" do + it "sets self's body based on the passed form parameters and the passed separator" do + @headers.set_form_data({"cmd" => "search", "q" => "ruby", "max" => "50"}, "&") + @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"] + + @headers.set_form_data({"cmd" => "search", "q" => "ruby", "max" => "50"}, ";") + @headers.body.split(";").sort.should == ["cmd=search", "max=50", "q=ruby"] + end + end end diff --git a/spec/ruby/library/net-http/httpheader/set_range_spec.rb b/spec/ruby/library/net-http/httpheader/set_range_spec.rb index 0f98de55e6..d48ed1897a 100644 --- a/spec/ruby/library/net-http/httpheader/set_range_spec.rb +++ b/spec/ruby/library/net-http/httpheader/set_range_spec.rb @@ -1,8 +1,93 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/set_range' describe "Net::HTTPHeader#set_range" do - it_behaves_like :net_httpheader_set_range, :set_range + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + describe "when passed nil" do + it "returns nil" do + @headers.set_range(nil).should == nil + end + + it "deletes the 'Range' header entry" do + @headers["Range"] = "bytes 0-499/1234" + @headers.set_range(nil) + @headers["Range"].should == nil + end + end + + describe "when passed Numeric" do + it "sets the 'Range' header entry based on the passed Numeric" do + @headers.set_range(10) + @headers["Range"].should == "bytes=0-9" + + @headers.set_range(-10) + @headers["Range"].should == "bytes=-10" + + @headers.set_range(10.9) + @headers["Range"].should == "bytes=0-9" + end + end + + describe "when passed Range" do + it "sets the 'Range' header entry based on the passed Range" do + @headers.set_range(10..200) + @headers["Range"].should == "bytes=10-200" + + @headers.set_range(1..5) + @headers["Range"].should == "bytes=1-5" + + @headers.set_range(1...5) + @headers["Range"].should == "bytes=1-4" + + @headers.set_range(234..567) + @headers["Range"].should == "bytes=234-567" + + @headers.set_range(-5..-1) + @headers["Range"].should == "bytes=-5" + + @headers.set_range(1..-1) + @headers["Range"].should == "bytes=1-" + end + + it "raises a Net::HTTPHeaderSyntaxError when the first Range element is negative" do + -> { @headers.set_range(-10..5) }.should.raise(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when the last Range element is negative" do + -> { @headers.set_range(10..-5) }.should.raise(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when the last Range element is smaller than the first" do + -> { @headers.set_range(10..5) }.should.raise(Net::HTTPHeaderSyntaxError) + end + end + + describe "when passed start, end" do + it "sets the 'Range' header entry based on the passed start and length values" do + @headers.set_range(10, 200) + @headers["Range"].should == "bytes=10-209" + + @headers.set_range(1, 5) + @headers["Range"].should == "bytes=1-5" + + @headers.set_range(234, 567) + @headers["Range"].should == "bytes=234-800" + end + + it "raises a Net::HTTPHeaderSyntaxError when start is negative" do + -> { @headers.set_range(-10, 5) }.should.raise(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when start + length is negative" do + -> { @headers.set_range(10, -15) }.should.raise(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when length is negative" do + -> { @headers.set_range(10, -4) }.should.raise(Net::HTTPHeaderSyntaxError) + end + end end diff --git a/spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb b/spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb deleted file mode 100644 index c12df62787..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/each_capitalized.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe :net_httpheader_each_capitalized, shared: true do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - @headers["my-header"] = "test" - @headers.add_field("my-Other-Header", "a") - @headers.add_field("My-Other-header", "b") - end - - describe "when passed a block" do - it "yields each header entry to the passed block (capitalized keys, values joined)" do - res = [] - @headers.send(@method) do |key, value| - res << [key, value] - end - res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]] - end - end - - describe "when passed no block" do - it "returns an Enumerator" do - enumerator = @headers.send(@method) - enumerator.should.instance_of?(Enumerator) - - res = [] - enumerator.each do |*key| - res << key - end - res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]] - end - end -end diff --git a/spec/ruby/library/net-http/httpheader/shared/each_header.rb b/spec/ruby/library/net-http/httpheader/shared/each_header.rb deleted file mode 100644 index 5913665a4d..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/each_header.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe :net_httpheader_each_header, shared: true do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - @headers["My-Header"] = "test" - @headers.add_field("My-Other-Header", "a") - @headers.add_field("My-Other-Header", "b") - end - - describe "when passed a block" do - it "yields each header entry to the passed block (keys in lower case, values joined)" do - res = [] - @headers.send(@method) do |key, value| - res << [key, value] - end - res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]] - end - end - - describe "when passed no block" do - it "returns an Enumerator" do - enumerator = @headers.send(@method) - enumerator.should.instance_of?(Enumerator) - - res = [] - enumerator.each do |*key| - res << key - end - res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]] - end - end -end diff --git a/spec/ruby/library/net-http/httpheader/shared/each_name.rb b/spec/ruby/library/net-http/httpheader/shared/each_name.rb deleted file mode 100644 index 29c9400fef..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/each_name.rb +++ /dev/null @@ -1,31 +0,0 @@ -describe :net_httpheader_each_name, shared: true do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - @headers["My-Header"] = "test" - @headers.add_field("My-Other-Header", "a") - @headers.add_field("My-Other-Header", "b") - end - - describe "when passed a block" do - it "yields each header key to the passed block (keys in lower case)" do - res = [] - @headers.send(@method) do |key| - res << key - end - res.sort.should == ["my-header", "my-other-header"] - end - end - - describe "when passed no block" do - it "returns an Enumerator" do - enumerator = @headers.send(@method) - enumerator.should.instance_of?(Enumerator) - - res = [] - enumerator.each do |key| - res << key - end - res.sort.should == ["my-header", "my-other-header"] - end - end -end diff --git a/spec/ruby/library/net-http/httpheader/shared/set_content_type.rb b/spec/ruby/library/net-http/httpheader/shared/set_content_type.rb deleted file mode 100644 index b7359bdca6..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/set_content_type.rb +++ /dev/null @@ -1,18 +0,0 @@ -describe :net_httpheader_set_content_type, shared: true do - describe "when passed type, params" do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - end - - it "sets the 'Content-Type' header entry based on the passed type and params" do - @headers.send(@method, "text/html") - @headers["Content-Type"].should == "text/html" - - @headers.send(@method, "text/html", "charset" => "utf-8") - @headers["Content-Type"].should == "text/html; charset=utf-8" - - @headers.send(@method, "text/html", "charset" => "utf-8", "rubyspec" => "rocks") - @headers["Content-Type"].split(/; /).sort.should == %w[charset=utf-8 rubyspec=rocks text/html] - end - end -end diff --git a/spec/ruby/library/net-http/httpheader/shared/set_form_data.rb b/spec/ruby/library/net-http/httpheader/shared/set_form_data.rb deleted file mode 100644 index db20b18803..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/set_form_data.rb +++ /dev/null @@ -1,27 +0,0 @@ -describe :net_httpheader_set_form_data, shared: true do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - end - - describe "when passed params" do - it "automatically set the 'Content-Type' to 'application/x-www-form-urlencoded'" do - @headers.send(@method, "cmd" => "search", "q" => "ruby", "max" => "50") - @headers["Content-Type"].should == "application/x-www-form-urlencoded" - end - - it "sets self's body based on the passed form parameters" do - @headers.send(@method, "cmd" => "search", "q" => "ruby", "max" => "50") - @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"] - end - end - - describe "when passed params, separator" do - it "sets self's body based on the passed form parameters and the passed separator" do - @headers.send(@method, {"cmd" => "search", "q" => "ruby", "max" => "50"}, "&") - @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"] - - @headers.send(@method, {"cmd" => "search", "q" => "ruby", "max" => "50"}, ";") - @headers.body.split(";").sort.should == ["cmd=search", "max=50", "q=ruby"] - end - end -end diff --git a/spec/ruby/library/net-http/httpheader/shared/set_range.rb b/spec/ruby/library/net-http/httpheader/shared/set_range.rb deleted file mode 100644 index 9ab50a075e..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/set_range.rb +++ /dev/null @@ -1,89 +0,0 @@ -describe :net_httpheader_set_range, shared: true do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - end - - describe "when passed nil" do - it "returns nil" do - @headers.send(@method, nil).should == nil - end - - it "deletes the 'Range' header entry" do - @headers["Range"] = "bytes 0-499/1234" - @headers.send(@method, nil) - @headers["Range"].should == nil - end - end - - describe "when passed Numeric" do - it "sets the 'Range' header entry based on the passed Numeric" do - @headers.send(@method, 10) - @headers["Range"].should == "bytes=0-9" - - @headers.send(@method, -10) - @headers["Range"].should == "bytes=-10" - - @headers.send(@method, 10.9) - @headers["Range"].should == "bytes=0-9" - end - end - - describe "when passed Range" do - it "sets the 'Range' header entry based on the passed Range" do - @headers.send(@method, 10..200) - @headers["Range"].should == "bytes=10-200" - - @headers.send(@method, 1..5) - @headers["Range"].should == "bytes=1-5" - - @headers.send(@method, 1...5) - @headers["Range"].should == "bytes=1-4" - - @headers.send(@method, 234..567) - @headers["Range"].should == "bytes=234-567" - - @headers.send(@method, -5..-1) - @headers["Range"].should == "bytes=-5" - - @headers.send(@method, 1..-1) - @headers["Range"].should == "bytes=1-" - end - - it "raises a Net::HTTPHeaderSyntaxError when the first Range element is negative" do - -> { @headers.send(@method, -10..5) }.should.raise(Net::HTTPHeaderSyntaxError) - end - - it "raises a Net::HTTPHeaderSyntaxError when the last Range element is negative" do - -> { @headers.send(@method, 10..-5) }.should.raise(Net::HTTPHeaderSyntaxError) - end - - it "raises a Net::HTTPHeaderSyntaxError when the last Range element is smaller than the first" do - -> { @headers.send(@method, 10..5) }.should.raise(Net::HTTPHeaderSyntaxError) - end - end - - describe "when passed start, end" do - it "sets the 'Range' header entry based on the passed start and length values" do - @headers.send(@method, 10, 200) - @headers["Range"].should == "bytes=10-209" - - @headers.send(@method, 1, 5) - @headers["Range"].should == "bytes=1-5" - - @headers.send(@method, 234, 567) - @headers["Range"].should == "bytes=234-800" - end - - it "raises a Net::HTTPHeaderSyntaxError when start is negative" do - -> { @headers.send(@method, -10, 5) }.should.raise(Net::HTTPHeaderSyntaxError) - end - - it "raises a Net::HTTPHeaderSyntaxError when start + length is negative" do - -> { @headers.send(@method, 10, -15) }.should.raise(Net::HTTPHeaderSyntaxError) - end - - it "raises a Net::HTTPHeaderSyntaxError when length is negative" do - -> { @headers.send(@method, 10, -4) }.should.raise(Net::HTTPHeaderSyntaxError) - end - end -end diff --git a/spec/ruby/library/net-http/httpheader/shared/size.rb b/spec/ruby/library/net-http/httpheader/shared/size.rb deleted file mode 100644 index b38310a940..0000000000 --- a/spec/ruby/library/net-http/httpheader/shared/size.rb +++ /dev/null @@ -1,18 +0,0 @@ -describe :net_httpheader_size, shared: true do - before :each do - @headers = NetHTTPHeaderSpecs::Example.new - end - - it "returns the number of header entries in self" do - @headers.send(@method).should.eql?(0) - - @headers["a"] = "b" - @headers.send(@method).should.eql?(1) - - @headers["b"] = "b" - @headers.send(@method).should.eql?(2) - - @headers["c"] = "c" - @headers.send(@method).should.eql?(3) - end -end diff --git a/spec/ruby/library/net-http/httpheader/size_spec.rb b/spec/ruby/library/net-http/httpheader/size_spec.rb index 210060ce21..f84a0fb5ab 100644 --- a/spec/ruby/library/net-http/httpheader/size_spec.rb +++ b/spec/ruby/library/net-http/httpheader/size_spec.rb @@ -1,8 +1,22 @@ require_relative '../../../spec_helper' require 'net/http' require_relative 'fixtures/classes' -require_relative 'shared/size' describe "Net::HTTPHeader#size" do - it_behaves_like :net_httpheader_size, :size + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the number of header entries in self" do + @headers.size.should.eql?(0) + + @headers["a"] = "b" + @headers.size.should.eql?(1) + + @headers["b"] = "b" + @headers.size.should.eql?(2) + + @headers["c"] = "c" + @headers.size.should.eql?(3) + end end diff --git a/spec/ruby/library/net-http/httpresponse/body_spec.rb b/spec/ruby/library/net-http/httpresponse/body_spec.rb index ddfcd834c4..5b00913687 100644 --- a/spec/ruby/library/net-http/httpresponse/body_spec.rb +++ b/spec/ruby/library/net-http/httpresponse/body_spec.rb @@ -1,7 +1,22 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'shared/body' +require 'stringio' describe "Net::HTTPResponse#body" do - it_behaves_like :net_httpresponse_body, :body + before :each do + @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + @socket = Net::BufferedIO.new(StringIO.new("test body")) + end + + it "returns the read body" do + @res.reading_body(@socket, true) do + @res.body.should == "test body" + end + end + + it "returns the previously read body if called a second time" do + @res.reading_body(@socket, true) do + @res.body.should.equal?(@res.body) + end + end end diff --git a/spec/ruby/library/net-http/httpresponse/entity_spec.rb b/spec/ruby/library/net-http/httpresponse/entity_spec.rb index ca8c4b29c0..d2201db37b 100644 --- a/spec/ruby/library/net-http/httpresponse/entity_spec.rb +++ b/spec/ruby/library/net-http/httpresponse/entity_spec.rb @@ -1,7 +1,9 @@ require_relative '../../../spec_helper' require 'net/http' -require_relative 'shared/body' describe "Net::HTTPResponse#entity" do - it_behaves_like :net_httpresponse_body, :entity + it "is an alias of Net::HTTPResponse#body" do + Net::HTTPResponse.instance_method(:entity).should == + Net::HTTPResponse.instance_method(:body) + end end diff --git a/spec/ruby/library/net-http/httpresponse/shared/body.rb b/spec/ruby/library/net-http/httpresponse/shared/body.rb deleted file mode 100644 index 368774fb52..0000000000 --- a/spec/ruby/library/net-http/httpresponse/shared/body.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'stringio' - -describe :net_httpresponse_body, shared: true do - before :each do - @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") - @socket = Net::BufferedIO.new(StringIO.new("test body")) - end - - it "returns the read body" do - @res.reading_body(@socket, true) do - @res.send(@method).should == "test body" - end - end - - it "returns the previously read body if called a second time" do - @res.reading_body(@socket, true) do - @res.send(@method).should.equal?(@res.send(@method)) - end - end -end diff --git a/spec/ruby/library/openstruct/equal_value_spec.rb b/spec/ruby/library/openstruct/equal_value_spec.rb index c72c09ce14..ec30214fd3 100644 --- a/spec/ruby/library/openstruct/equal_value_spec.rb +++ b/spec/ruby/library/openstruct/equal_value_spec.rb @@ -1,5 +1,5 @@ require_relative '../../spec_helper' -require "ostruct" +require 'ostruct' require_relative 'fixtures/classes' describe "OpenStruct#==" do diff --git a/spec/ruby/library/openstruct/inspect_spec.rb b/spec/ruby/library/openstruct/inspect_spec.rb index e2fed41528..81da96d6bf 100644 --- a/spec/ruby/library/openstruct/inspect_spec.rb +++ b/spec/ruby/library/openstruct/inspect_spec.rb @@ -1,8 +1,8 @@ require_relative '../../spec_helper' require 'ostruct' -require_relative 'fixtures/classes' -require_relative 'shared/inspect' describe "OpenStruct#inspect" do - it_behaves_like :ostruct_inspect, :inspect + it "is an alias of OpenStruct#to_s" do + OpenStruct.instance_method(:inspect).should == OpenStruct.instance_method(:to_s) + end end diff --git a/spec/ruby/library/openstruct/shared/inspect.rb b/spec/ruby/library/openstruct/shared/inspect.rb deleted file mode 100644 index d5fffa0e2e..0000000000 --- a/spec/ruby/library/openstruct/shared/inspect.rb +++ /dev/null @@ -1,20 +0,0 @@ -describe :ostruct_inspect, shared: true do - it "returns a String representation of self" do - os = OpenStruct.new(name: "John Smith") - os.send(@method).should == "#<OpenStruct name=\"John Smith\">" - - os = OpenStruct.new(age: 20, name: "John Smith") - os.send(@method).should.is_a?(String) - end - - it "correctly handles self-referential OpenStructs" do - os = OpenStruct.new - os.self = os - os.send(@method).should == "#<OpenStruct self=#<OpenStruct ...>>" - end - - it "correctly handles OpenStruct subclasses" do - os = OpenStructSpecs::OpenStructSub.new(name: "John Smith") - os.send(@method).should == "#<OpenStructSpecs::OpenStructSub name=\"John Smith\">" - end -end diff --git a/spec/ruby/library/openstruct/to_s_spec.rb b/spec/ruby/library/openstruct/to_s_spec.rb index 73d91bf981..9131cd4897 100644 --- a/spec/ruby/library/openstruct/to_s_spec.rb +++ b/spec/ruby/library/openstruct/to_s_spec.rb @@ -1,8 +1,24 @@ require_relative '../../spec_helper' require 'ostruct' require_relative 'fixtures/classes' -require_relative 'shared/inspect' describe "OpenStruct#to_s" do - it_behaves_like :ostruct_inspect, :to_s + it "returns a String representation of self" do + os = OpenStruct.new(name: "John Smith") + os.to_s.should == "#<OpenStruct name=\"John Smith\">" + + os = OpenStruct.new(age: 20, name: "John Smith") + os.to_s.should.is_a?(String) + end + + it "correctly handles self-referential OpenStructs" do + os = OpenStruct.new + os.self = os + os.to_s.should == "#<OpenStruct self=#<OpenStruct ...>>" + end + + it "correctly handles OpenStruct subclasses" do + os = OpenStructSpecs::OpenStructSub.new(name: "John Smith") + os.to_s.should == "#<OpenStructSpecs::OpenStructSub name=\"John Smith\">" + end end diff --git a/spec/ruby/library/pathname/case_compare_spec.rb b/spec/ruby/library/pathname/case_compare_spec.rb new file mode 100644 index 0000000000..0cf799dd23 --- /dev/null +++ b/spec/ruby/library/pathname/case_compare_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require 'pathname' + +describe "Pathname#===" do + it "is an alias of Pathname#==" do + Pathname.instance_method(:===).should == Pathname.instance_method(:==) + end +end diff --git a/spec/ruby/library/pathname/divide_spec.rb b/spec/ruby/library/pathname/divide_spec.rb index 8af79d0c8f..e5afc9f864 100644 --- a/spec/ruby/library/pathname/divide_spec.rb +++ b/spec/ruby/library/pathname/divide_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/plus' +require 'pathname' describe "Pathname#/" do - it_behaves_like :pathname_plus, :/ + it "is an alias of Pathname#+" do + Pathname.instance_method(:/).should == Pathname.instance_method(:+) + end end diff --git a/spec/ruby/library/pathname/plus_spec.rb b/spec/ruby/library/pathname/plus_spec.rb index 57e472c266..76316df9d2 100644 --- a/spec/ruby/library/pathname/plus_spec.rb +++ b/spec/ruby/library/pathname/plus_spec.rb @@ -1,6 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/plus' +require 'pathname' describe "Pathname#+" do - it_behaves_like :pathname_plus, :+ + it "appends a pathname to self" do + p = Pathname.new("/usr") + (p + "bin/ruby").should == Pathname.new("/usr/bin/ruby") + end end diff --git a/spec/ruby/library/pathname/shared/plus.rb b/spec/ruby/library/pathname/shared/plus.rb deleted file mode 100644 index b3b896ea43..0000000000 --- a/spec/ruby/library/pathname/shared/plus.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'pathname' - -describe :pathname_plus, shared: true do - it "appends a pathname to self" do - p = Pathname.new("/usr") - p.send(@method, "bin/ruby").should == Pathname.new("/usr/bin/ruby") - end -end diff --git a/spec/ruby/library/prime/next_spec.rb b/spec/ruby/library/prime/next_spec.rb index 39c4ae16ae..07e80ab3a5 100644 --- a/spec/ruby/library/prime/next_spec.rb +++ b/spec/ruby/library/prime/next_spec.rb @@ -1,7 +1,11 @@ require_relative '../../spec_helper' -require_relative 'shared/next' require 'prime' describe "Prime#next" do - it_behaves_like :prime_next, :next + it "returns the element at the current position and moves forward" do + p = Prime.instance.each + p.next.should == 2 + p.next.should == 3 + p.next.next.should == 6 + end end diff --git a/spec/ruby/library/prime/shared/next.rb b/spec/ruby/library/prime/shared/next.rb deleted file mode 100644 index f79b2c051e..0000000000 --- a/spec/ruby/library/prime/shared/next.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe :prime_next, shared: true do - it "returns the element at the current position and moves forward" do - p = Prime.instance.each - p.next.should == 2 - p.next.should == 3 - p.next.next.should == 6 - end -end diff --git a/spec/ruby/library/prime/succ_spec.rb b/spec/ruby/library/prime/succ_spec.rb index 34c18d2ba0..86f76c2513 100644 --- a/spec/ruby/library/prime/succ_spec.rb +++ b/spec/ruby/library/prime/succ_spec.rb @@ -1,7 +1,9 @@ require_relative '../../spec_helper' -require_relative 'shared/next' require 'prime' describe "Prime#succ" do - it_behaves_like :prime_next, :succ + it "is an alias of Prime#next" do + p = Prime.instance.each + p.method(:succ).should == p.method(:next) + end end diff --git a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb deleted file mode 100644 index 70d6bfbbfe..0000000000 --- a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb +++ /dev/null @@ -1,47 +0,0 @@ -describe :socket_addrinfo_to_sockaddr, shared: true do - describe "for an ipv4 socket" do - before :each do - @addrinfo = Addrinfo.tcp("127.0.0.1", 80) - end - - it "returns a sockaddr packed structure" do - @addrinfo.send(@method).should == Socket.sockaddr_in(80, '127.0.0.1') - end - end - - describe "for an ipv6 socket" do - before :each do - @addrinfo = Addrinfo.tcp("::1", 80) - end - - it "returns a sockaddr packed structure" do - @addrinfo.send(@method).should == Socket.sockaddr_in(80, '::1') - end - end - - describe "for a unix socket" do - before :each do - @addrinfo = Addrinfo.unix("/tmp/sock") - end - - it "returns a sockaddr packed structure" do - @addrinfo.send(@method).should == Socket.sockaddr_un('/tmp/sock') - end - end - - describe 'using a Addrinfo with just an IP address' do - it 'returns a String' do - addr = Addrinfo.ip('127.0.0.1') - - addr.send(@method).should == Socket.sockaddr_in(0, '127.0.0.1') - end - end - - describe 'using a Addrinfo without an IP and port' do - it 'returns a String' do - addr = Addrinfo.new(['AF_INET', 0, '', '']) - - addr.send(@method).should == Socket.sockaddr_in(0, '') - end - end -end diff --git a/spec/ruby/library/socket/addrinfo/to_s_spec.rb b/spec/ruby/library/socket/addrinfo/to_s_spec.rb index ddf994e051..5c1c82793c 100644 --- a/spec/ruby/library/socket/addrinfo/to_s_spec.rb +++ b/spec/ruby/library/socket/addrinfo/to_s_spec.rb @@ -1,6 +1,7 @@ require_relative '../spec_helper' -require_relative 'shared/to_sockaddr' describe "Addrinfo#to_s" do - it_behaves_like :socket_addrinfo_to_sockaddr, :to_s + it "is an alias of Addrinfo#to_sockaddr" do + Addrinfo.instance_method(:to_s).should == Addrinfo.instance_method(:to_sockaddr) + end end diff --git a/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb index b9f75454bd..c703c7b28f 100644 --- a/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb +++ b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb @@ -1,6 +1,49 @@ require_relative '../spec_helper' -require_relative 'shared/to_sockaddr' describe "Addrinfo#to_sockaddr" do - it_behaves_like :socket_addrinfo_to_sockaddr, :to_sockaddr + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns a sockaddr packed structure" do + @addrinfo.to_sockaddr.should == Socket.sockaddr_in(80, '127.0.0.1') + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns a sockaddr packed structure" do + @addrinfo.to_sockaddr.should == Socket.sockaddr_in(80, '::1') + end + end + + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns a sockaddr packed structure" do + @addrinfo.to_sockaddr.should == Socket.sockaddr_un('/tmp/sock') + end + end + + describe 'using a Addrinfo with just an IP address' do + it 'returns a String' do + addr = Addrinfo.ip('127.0.0.1') + + addr.to_sockaddr.should == Socket.sockaddr_in(0, '127.0.0.1') + end + end + + describe 'using a Addrinfo without an IP and port' do + it 'returns a String' do + addr = Addrinfo.new(['AF_INET', 0, '', '']) + + addr.to_sockaddr.should == Socket.sockaddr_in(0, '') + end + end end diff --git a/spec/ruby/library/socket/shared/pack_sockaddr.rb b/spec/ruby/library/socket/shared/pack_sockaddr.rb deleted file mode 100644 index db6f39612d..0000000000 --- a/spec/ruby/library/socket/shared/pack_sockaddr.rb +++ /dev/null @@ -1,92 +0,0 @@ -# coding: utf-8 -describe :socket_pack_sockaddr_in, shared: true do - it "packs and unpacks" do - sockaddr_in = Socket.public_send(@method, 0, nil) - port, addr = Socket.unpack_sockaddr_in(sockaddr_in) - ["127.0.0.1", "::1"].include?(addr).should == true - port.should == 0 - - sockaddr_in = Socket.public_send(@method, 0, '') - Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '0.0.0.0'] - - sockaddr_in = Socket.public_send(@method, 80, '127.0.0.1') - Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] - - sockaddr_in = Socket.public_send(@method, '80', '127.0.0.1') - Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] - - sockaddr_in = Socket.public_send(@method, nil, '127.0.0.1') - Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1'] - - sockaddr_in = Socket.public_send(@method, 80, Socket::INADDR_ANY) - Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '0.0.0.0'] - end - - it 'resolves the service name to a port' do - sockaddr_in = Socket.public_send(@method, 'http', '127.0.0.1') - Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] - end - - describe 'using an IPv4 address' do - it 'returns a String of 16 bytes' do - str = Socket.public_send(@method, 80, '127.0.0.1') - - str.should.instance_of?(String) - str.bytesize.should == 16 - end - end - - describe 'using an IPv6 address' do - it 'returns a String of 28 bytes' do - str = Socket.public_send(@method, 80, '::1') - - str.should.instance_of?(String) - str.bytesize.should == 28 - end - end -end - -describe :socket_pack_sockaddr_un, shared: true do - it 'should be idempotent' do - bytes = Socket.public_send(@method, '/tmp/foo').bytes - bytes[2..9].should == [47, 116, 109, 112, 47, 102, 111, 111] - bytes[10..-1].all?(&:zero?).should == true - end - - it "packs and unpacks" do - sockaddr_un = Socket.public_send(@method, '/tmp/s') - Socket.unpack_sockaddr_un(sockaddr_un).should == '/tmp/s' - end - - it "handles correctly paths with multibyte chars" do - sockaddr_un = Socket.public_send(@method, '/home/вася/sock') - path = Socket.unpack_sockaddr_un(sockaddr_un).encode('UTF-8', 'UTF-8') - path.should == '/home/вася/sock' - end - - platform_is :linux do - it 'returns a String of 110 bytes' do - str = Socket.public_send(@method, '/tmp/test.sock') - - str.should.instance_of?(String) - str.bytesize.should == 110 - end - end - - platform_is :bsd do - it 'returns a String of 106 bytes' do - str = Socket.public_send(@method, '/tmp/test.sock') - - str.should.instance_of?(String) - str.bytesize.should == 106 - end - end - - platform_is_not :aix do - it "raises ArgumentError for paths that are too long" do - # AIX doesn't raise error - long_path = 'a' * 110 - -> { Socket.public_send(@method, long_path) }.should.raise(ArgumentError) - end - end -end diff --git a/spec/ruby/library/socket/shared/socketpair.rb b/spec/ruby/library/socket/shared/socketpair.rb deleted file mode 100644 index 7fcd4d6b46..0000000000 --- a/spec/ruby/library/socket/shared/socketpair.rb +++ /dev/null @@ -1,138 +0,0 @@ -describe :socket_socketpair, shared: true do - platform_is_not :windows do - it "ensures the returned sockets are connected" do - s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, 1, 0) - s1.puts("test") - s2.gets.should == "test\n" - s1.close - s2.close - end - - it "responses with array of two sockets" do - begin - s1, s2 = Socket.public_send(@method, :UNIX, :STREAM) - - s1.should.instance_of?(Socket) - s2.should.instance_of?(Socket) - ensure - s1.close - s2.close - end - end - - describe 'using an Integer as the 1st and 2nd argument' do - it 'returns two Socket objects' do - s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, Socket::SOCK_STREAM) - - s1.should.instance_of?(Socket) - s2.should.instance_of?(Socket) - s1.close - s2.close - end - end - - describe 'using a Symbol as the 1st and 2nd argument' do - it 'returns two Socket objects' do - s1, s2 = Socket.public_send(@method, :UNIX, :STREAM) - - s1.should.instance_of?(Socket) - s2.should.instance_of?(Socket) - s1.close - s2.close - end - - it 'raises SocketError for an unknown address family' do - -> { Socket.public_send(@method, :CATS, :STREAM) }.should.raise(SocketError) - end - - it 'raises SocketError for an unknown socket type' do - -> { Socket.public_send(@method, :UNIX, :CATS) }.should.raise(SocketError) - end - end - - describe 'using a String as the 1st and 2nd argument' do - it 'returns two Socket objects' do - s1, s2 = Socket.public_send(@method, 'UNIX', 'STREAM') - - s1.should.instance_of?(Socket) - s2.should.instance_of?(Socket) - s1.close - s2.close - end - - it 'raises SocketError for an unknown address family' do - -> { Socket.public_send(@method, 'CATS', 'STREAM') }.should.raise(SocketError) - end - - it 'raises SocketError for an unknown socket type' do - -> { Socket.public_send(@method, 'UNIX', 'CATS') }.should.raise(SocketError) - end - end - - describe 'using an object that responds to #to_str as the 1st and 2nd argument' do - it 'returns two Socket objects' do - family = mock(:family) - type = mock(:type) - - family.stub!(:to_str).and_return('UNIX') - type.stub!(:to_str).and_return('STREAM') - - s1, s2 = Socket.public_send(@method, family, type) - - s1.should.instance_of?(Socket) - s2.should.instance_of?(Socket) - s1.close - s2.close - end - - it 'raises TypeError when #to_str does not return a String' do - family = mock(:family) - type = mock(:type) - - family.stub!(:to_str).and_return(Socket::AF_UNIX) - type.stub!(:to_str).and_return(Socket::SOCK_STREAM) - - -> { Socket.public_send(@method, family, type) }.should.raise(TypeError) - end - - it 'raises SocketError for an unknown address family' do - family = mock(:family) - type = mock(:type) - - family.stub!(:to_str).and_return('CATS') - type.stub!(:to_str).and_return('STREAM') - - -> { Socket.public_send(@method, family, type) }.should.raise(SocketError) - end - - it 'raises SocketError for an unknown socket type' do - family = mock(:family) - type = mock(:type) - - family.stub!(:to_str).and_return('UNIX') - type.stub!(:to_str).and_return('CATS') - - -> { Socket.public_send(@method, family, type) }.should.raise(SocketError) - end - end - - it 'accepts a custom protocol as an Integer as the 3rd argument' do - s1, s2 = Socket.public_send(@method, :UNIX, :STREAM, Socket::IPPROTO_IP) - s1.should.instance_of?(Socket) - s2.should.instance_of?(Socket) - s1.close - s2.close - end - - it 'connects the returned Socket objects' do - s1, s2 = Socket.public_send(@method, :UNIX, :STREAM) - begin - s1.write('hello') - s2.recv(5).should == 'hello' - ensure - s1.close - s2.close - end - end - end -end diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb index ef2a2d4ba9..17a737cacd 100644 --- a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb +++ b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' -require_relative '../fixtures/classes' -require_relative '../shared/pack_sockaddr' describe "Socket.pack_sockaddr_in" do - it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in + it "is an alias of Socket.sockaddr_in" do + Socket.method(:pack_sockaddr_in).should == Socket.method(:sockaddr_in) + end end diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb index 1ee0bc6157..34d4fc1f51 100644 --- a/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb +++ b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' -require_relative '../fixtures/classes' -require_relative '../shared/pack_sockaddr' -describe "Socket#pack_sockaddr_un" do - it_behaves_like :socket_pack_sockaddr_un, :pack_sockaddr_un +describe "Socket.pack_sockaddr_un" do + it "is an alias of Socket.sockaddr_un" do + Socket.method(:pack_sockaddr_un).should == Socket.method(:sockaddr_un) + end end diff --git a/spec/ruby/library/socket/socket/pair_spec.rb b/spec/ruby/library/socket/socket/pair_spec.rb index 8dd470a95e..91317a8d07 100644 --- a/spec/ruby/library/socket/socket/pair_spec.rb +++ b/spec/ruby/library/socket/socket/pair_spec.rb @@ -1,7 +1,141 @@ require_relative '../spec_helper' require_relative '../fixtures/classes' -require_relative '../shared/socketpair' describe "Socket.pair" do - it_behaves_like :socket_socketpair, :pair + platform_is_not :windows do + it "ensures the returned sockets are connected" do + s1, s2 = Socket.pair(Socket::AF_UNIX, 1, 0) + s1.puts("test") + s2.gets.should == "test\n" + s1.close + s2.close + end + + it "returns an array of two sockets" do + begin + s1, s2 = Socket.pair(:UNIX, :STREAM) + + s1.should.instance_of?(Socket) + s2.should.instance_of?(Socket) + ensure + s1.close + s2.close + end + end + + describe 'using an Integer as the 1st and 2nd argument' do + it 'returns two Socket objects' do + s1, s2 = Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM) + + s1.should.instance_of?(Socket) + s2.should.instance_of?(Socket) + s1.close + s2.close + end + end + + describe 'using a Symbol as the 1st and 2nd argument' do + it 'returns two Socket objects' do + s1, s2 = Socket.pair(:UNIX, :STREAM) + + s1.should.instance_of?(Socket) + s2.should.instance_of?(Socket) + s1.close + s2.close + end + + it 'raises SocketError for an unknown address family' do + -> { Socket.pair(:CATS, :STREAM) }.should.raise(SocketError) + end + + it 'raises SocketError for an unknown socket type' do + -> { Socket.pair(:UNIX, :CATS) }.should.raise(SocketError) + end + end + + describe 'using a String as the 1st and 2nd argument' do + it 'returns two Socket objects' do + s1, s2 = Socket.pair('UNIX', 'STREAM') + + s1.should.instance_of?(Socket) + s2.should.instance_of?(Socket) + s1.close + s2.close + end + + it 'raises SocketError for an unknown address family' do + -> { Socket.pair('CATS', 'STREAM') }.should.raise(SocketError) + end + + it 'raises SocketError for an unknown socket type' do + -> { Socket.pair('UNIX', 'CATS') }.should.raise(SocketError) + end + end + + describe 'using an object that responds to #to_str as the 1st and 2nd argument' do + it 'returns two Socket objects' do + family = mock(:family) + type = mock(:type) + + family.stub!(:to_str).and_return('UNIX') + type.stub!(:to_str).and_return('STREAM') + + s1, s2 = Socket.pair(family, type) + + s1.should.instance_of?(Socket) + s2.should.instance_of?(Socket) + s1.close + s2.close + end + + it 'raises TypeError when #to_str does not return a String' do + family = mock(:family) + type = mock(:type) + + family.stub!(:to_str).and_return(Socket::AF_UNIX) + type.stub!(:to_str).and_return(Socket::SOCK_STREAM) + + -> { Socket.pair(family, type) }.should.raise(TypeError) + end + + it 'raises SocketError for an unknown address family' do + family = mock(:family) + type = mock(:type) + + family.stub!(:to_str).and_return('CATS') + type.stub!(:to_str).and_return('STREAM') + + -> { Socket.pair(family, type) }.should.raise(SocketError) + end + + it 'raises SocketError for an unknown socket type' do + family = mock(:family) + type = mock(:type) + + family.stub!(:to_str).and_return('UNIX') + type.stub!(:to_str).and_return('CATS') + + -> { Socket.pair(family, type) }.should.raise(SocketError) + end + end + + it 'accepts a custom protocol as an Integer as the 3rd argument' do + s1, s2 = Socket.pair(:UNIX, :STREAM, Socket::IPPROTO_IP) + s1.should.instance_of?(Socket) + s2.should.instance_of?(Socket) + s1.close + s2.close + end + + it 'connects the returned Socket objects' do + s1, s2 = Socket.pair(:UNIX, :STREAM) + begin + s1.write('hello') + s2.recv(5).should == 'hello' + ensure + s1.close + s2.close + end + end + end end diff --git a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb index 8ee956ac26..9d3367cd69 100644 --- a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb +++ b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb @@ -1,7 +1,49 @@ require_relative '../spec_helper' require_relative '../fixtures/classes' -require_relative '../shared/pack_sockaddr' -describe "Socket#sockaddr_in" do - it_behaves_like :socket_pack_sockaddr_in, :sockaddr_in +describe "Socket.sockaddr_in" do + it "packs and unpacks" do + sockaddr_in = Socket.sockaddr_in(0, nil) + port, addr = Socket.unpack_sockaddr_in(sockaddr_in) + ["127.0.0.1", "::1"].include?(addr).should == true + port.should == 0 + + sockaddr_in = Socket.sockaddr_in(0, '') + Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '0.0.0.0'] + + sockaddr_in = Socket.sockaddr_in(80, '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] + + sockaddr_in = Socket.sockaddr_in('80', '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] + + sockaddr_in = Socket.sockaddr_in(nil, '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1'] + + sockaddr_in = Socket.sockaddr_in(80, Socket::INADDR_ANY) + Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '0.0.0.0'] + end + + it 'resolves the service name to a port' do + sockaddr_in = Socket.sockaddr_in('http', '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] + end + + describe 'using an IPv4 address' do + it 'returns a String of 16 bytes' do + str = Socket.sockaddr_in(80, '127.0.0.1') + + str.should.instance_of?(String) + str.bytesize.should == 16 + end + end + + describe 'using an IPv6 address' do + it 'returns a String of 28 bytes' do + str = Socket.sockaddr_in(80, '::1') + + str.should.instance_of?(String) + str.bytesize.should == 28 + end + end end diff --git a/spec/ruby/library/socket/socket/sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb index 8922ff4d6d..548dc526ff 100644 --- a/spec/ruby/library/socket/socket/sockaddr_un_spec.rb +++ b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb @@ -1,7 +1,47 @@ require_relative '../spec_helper' require_relative '../fixtures/classes' -require_relative '../shared/pack_sockaddr' -describe "Socket#sockaddr_un" do - it_behaves_like :socket_pack_sockaddr_un, :sockaddr_un +describe "Socket.sockaddr_un" do + it 'should be idempotent' do + bytes = Socket.sockaddr_un('/tmp/foo').bytes + bytes[2..9].should == [47, 116, 109, 112, 47, 102, 111, 111] + bytes[10..-1].all?(&:zero?).should == true + end + + it "packs and unpacks" do + sockaddr_un = Socket.sockaddr_un('/tmp/s') + Socket.unpack_sockaddr_un(sockaddr_un).should == '/tmp/s' + end + + it "handles correctly paths with multibyte chars" do + sockaddr_un = Socket.sockaddr_un('/home/вася/sock') + path = Socket.unpack_sockaddr_un(sockaddr_un).encode('UTF-8', 'UTF-8') + path.should == '/home/вася/sock' + end + + platform_is :linux do + it 'returns a String of 110 bytes' do + str = Socket.sockaddr_un('/tmp/test.sock') + + str.should.instance_of?(String) + str.bytesize.should == 110 + end + end + + platform_is :bsd do + it 'returns a String of 106 bytes' do + str = Socket.sockaddr_un('/tmp/test.sock') + + str.should.instance_of?(String) + str.bytesize.should == 106 + end + end + + platform_is_not :aix do + it "raises ArgumentError for paths that are too long" do + # AIX doesn't raise error + long_path = 'a' * 110 + -> { Socket.sockaddr_un(long_path) }.should.raise(ArgumentError) + end + end end diff --git a/spec/ruby/library/socket/socket/socketpair_spec.rb b/spec/ruby/library/socket/socket/socketpair_spec.rb index 551c376d49..191fb358cf 100644 --- a/spec/ruby/library/socket/socket/socketpair_spec.rb +++ b/spec/ruby/library/socket/socket/socketpair_spec.rb @@ -1,7 +1,7 @@ require_relative '../spec_helper' -require_relative '../fixtures/classes' -require_relative '../shared/socketpair' describe "Socket.socketpair" do - it_behaves_like :socket_socketpair, :socketpair + it "is an alias of Socket.pair" do + Socket.method(:socketpair).should == Socket.method(:pair) + end end diff --git a/spec/ruby/library/socket/unixsocket/pair_spec.rb b/spec/ruby/library/socket/unixsocket/pair_spec.rb index 9690142668..9f04f568fa 100644 --- a/spec/ruby/library/socket/unixsocket/pair_spec.rb +++ b/spec/ruby/library/socket/unixsocket/pair_spec.rb @@ -1,10 +1,8 @@ require_relative '../spec_helper' require_relative '../fixtures/classes' require_relative '../shared/partially_closable_sockets' -require_relative 'shared/pair' describe "UNIXSocket.pair" do - it_should_behave_like :unixsocket_pair it_should_behave_like :partially_closable_sockets before :each do @@ -15,4 +13,47 @@ describe "UNIXSocket.pair" do @s1.close @s2.close end + + it "returns two UNIXSockets" do + @s1.should.instance_of?(UNIXSocket) + @s2.should.instance_of?(UNIXSocket) + end + + it "returns a pair of connected sockets" do + @s1.puts "foo" + @s2.gets.should == "foo\n" + end + + platform_is_not :windows do + it "sets the socket paths to empty Strings" do + @s1.path.should == "" + @s2.path.should == "" + end + + it "sets the socket addresses to empty Strings" do + @s1.addr.should == ["AF_UNIX", ""] + @s2.addr.should == ["AF_UNIX", ""] + end + + it "sets the socket peer addresses to empty Strings" do + @s1.peeraddr.should == ["AF_UNIX", ""] + @s2.peeraddr.should == ["AF_UNIX", ""] + end + end + + platform_is :windows do + it "emulates unnamed sockets with a temporary file with a path" do + @s1.addr.should == ["AF_UNIX", @s1.path] + @s2.peeraddr.should == ["AF_UNIX", @s1.path] + end + + it "sets the peer address of first socket to an empty string" do + @s1.peeraddr.should == ["AF_UNIX", ""] + end + + it "sets the address and path of second socket to an empty string" do + @s2.addr.should == ["AF_UNIX", ""] + @s2.path.should == "" + end + end end diff --git a/spec/ruby/library/socket/unixsocket/shared/pair.rb b/spec/ruby/library/socket/unixsocket/shared/pair.rb deleted file mode 100644 index 49b6a6a413..0000000000 --- a/spec/ruby/library/socket/unixsocket/shared/pair.rb +++ /dev/null @@ -1,47 +0,0 @@ -require_relative '../../spec_helper' -require_relative '../../fixtures/classes' - -describe :unixsocket_pair, shared: true do - it "returns two UNIXSockets" do - @s1.should.instance_of?(UNIXSocket) - @s2.should.instance_of?(UNIXSocket) - end - - it "returns a pair of connected sockets" do - @s1.puts "foo" - @s2.gets.should == "foo\n" - end - - platform_is_not :windows do - it "sets the socket paths to empty Strings" do - @s1.path.should == "" - @s2.path.should == "" - end - - it "sets the socket addresses to empty Strings" do - @s1.addr.should == ["AF_UNIX", ""] - @s2.addr.should == ["AF_UNIX", ""] - end - - it "sets the socket peer addresses to empty Strings" do - @s1.peeraddr.should == ["AF_UNIX", ""] - @s2.peeraddr.should == ["AF_UNIX", ""] - end - end - - platform_is :windows do - it "emulates unnamed sockets with a temporary file with a path" do - @s1.addr.should == ["AF_UNIX", @s1.path] - @s2.peeraddr.should == ["AF_UNIX", @s1.path] - end - - it "sets the peer address of first socket to an empty string" do - @s1.peeraddr.should == ["AF_UNIX", ""] - end - - it "sets the address and path of second socket to an empty string" do - @s2.addr.should == ["AF_UNIX", ""] - @s2.path.should == "" - end - end -end diff --git a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb index c61fc00be4..a8bfb412e5 100644 --- a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb +++ b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb @@ -1,18 +1,7 @@ require_relative '../spec_helper' -require_relative '../fixtures/classes' -require_relative '../shared/partially_closable_sockets' -require_relative 'shared/pair' describe "UNIXSocket.socketpair" do - it_should_behave_like :unixsocket_pair - it_should_behave_like :partially_closable_sockets - - before :each do - @s1, @s2 = UNIXSocket.socketpair - end - - after :each do - @s1.close - @s2.close + it "is an alias of UNIXSocket.pair" do + UNIXSocket.method(:socketpair).should == UNIXSocket.method(:pair) end end diff --git a/spec/ruby/library/stringio/each_byte_spec.rb b/spec/ruby/library/stringio/each_byte_spec.rb index 6f82a32441..1be0081c1e 100644 --- a/spec/ruby/library/stringio/each_byte_spec.rb +++ b/spec/ruby/library/stringio/each_byte_spec.rb @@ -1,11 +1,51 @@ require_relative '../../spec_helper' require 'stringio' -require_relative 'shared/each_byte' describe "StringIO#each_byte" do - it_behaves_like :stringio_each_byte, :each_byte + before :each do + @io = StringIO.new("xyz") + end + + it "yields each character code in turn" do + seen = [] + @io.each_byte { |b| seen << b } + seen.should == [120, 121, 122] + end + + it "updates the position before each yield" do + seen = [] + @io.each_byte { |b| seen << @io.pos } + seen.should == [1, 2, 3] + end + + it "does not yield if the current position is out of bounds" do + @io.pos = 1000 + seen = nil + @io.each_byte { |b| seen = b } + seen.should == nil + end + + it "returns self" do + @io.each_byte {}.should.equal?(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.each_byte + enum.instance_of?(Enumerator).should == true + + seen = [] + enum.each { |b| seen << b } + seen.should == [120, 121, 122] + end end describe "StringIO#each_byte when self is not readable" do - it_behaves_like :stringio_each_byte_not_readable, :each_byte + it "raises an IOError" do + io = StringIO.new(+"xyz", "w") + -> { io.each_byte { |b| b } }.should.raise(IOError) + + io = StringIO.new("xyz") + io.close_read + -> { io.each_byte { |b| b } }.should.raise(IOError) + end end diff --git a/spec/ruby/library/stringio/each_char_spec.rb b/spec/ruby/library/stringio/each_char_spec.rb index 14b2f09a17..1db80c7d07 100644 --- a/spec/ruby/library/stringio/each_char_spec.rb +++ b/spec/ruby/library/stringio/each_char_spec.rb @@ -1,11 +1,38 @@ require_relative '../../spec_helper' require 'stringio' -require_relative 'shared/each_char' describe "StringIO#each_char" do - it_behaves_like :stringio_each_char, :each_char + before :each do + @io = StringIO.new("xyz äöü") + end + + it "yields each character code in turn" do + seen = [] + @io.each_char { |c| seen << c } + seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] + end + + it "returns self" do + @io.each_char {}.should.equal?(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.each_char + enum.instance_of?(Enumerator).should == true + + seen = [] + enum.each { |c| seen << c } + seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] + end end describe "StringIO#each_char when self is not readable" do - it_behaves_like :stringio_each_char_not_readable, :each_char + it "raises an IOError" do + io = StringIO.new(+"xyz", "w") + -> { io.each_char { |b| b } }.should.raise(IOError) + + io = StringIO.new("xyz") + io.close_read + -> { io.each_char { |b| b } }.should.raise(IOError) + end end diff --git a/spec/ruby/library/stringio/each_codepoint_spec.rb b/spec/ruby/library/stringio/each_codepoint_spec.rb index f18de22aad..d4f461db90 100644 --- a/spec/ruby/library/stringio/each_codepoint_spec.rb +++ b/spec/ruby/library/stringio/each_codepoint_spec.rb @@ -1,9 +1,47 @@ -# -*- encoding: utf-8 -*- require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/codepoints' +require 'stringio' # See redmine #1667 describe "StringIO#each_codepoint" do - it_behaves_like :stringio_codepoints, :each_codepoint + before :each do + @io = StringIO.new("∂φ/∂x = gaîté") + @enum = @io.each_codepoint + end + + it "returns an Enumerator" do + @enum.should.instance_of?(Enumerator) + end + + it "yields each codepoint code in turn" do + @enum.to_a.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] + end + + it "yields each codepoint starting from the current position" do + @io.pos = 15 + @enum.to_a.should == [238, 116, 233] + end + + it "raises an error if reading invalid sequence" do + @io.pos = 1 # inside of a multibyte sequence + -> { @enum.first }.should.raise(ArgumentError) + end + + it "raises an IOError if not readable" do + @io.close_read + -> { @enum.to_a }.should.raise(IOError) + + io = StringIO.new(+"xyz", "w") + -> { io.each_codepoint.to_a }.should.raise(IOError) + end + + + it "calls the given block" do + r = [] + @io.each_codepoint{|c| r << c } + r.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] + end + + it "returns self" do + @io.each_codepoint {|l| l }.should.equal?(@io) + end end diff --git a/spec/ruby/library/stringio/each_line_spec.rb b/spec/ruby/library/stringio/each_line_spec.rb index 4ac0db7c45..4abecbf026 100644 --- a/spec/ruby/library/stringio/each_line_spec.rb +++ b/spec/ruby/library/stringio/each_line_spec.rb @@ -1,27 +1,212 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/each' describe "StringIO#each_line when passed a separator" do - it_behaves_like :stringio_each_separator, :each_line + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "uses the passed argument as the line separator" do + seen = [] + @io.each_line(" ") {|s| seen << s} + seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] + end + + it "does not change $_" do + $_ = "test" + @io.each_line(" ") { |s| s} + $_.should == "test" + end + + it "returns self" do + @io.each_line {|l| l }.should.equal?(@io) + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(" ") + + seen = [] + @io.each_line(obj) { |l| seen << l } + seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] + end + + it "yields self's content starting from the current position when the passed separator is nil" do + seen = [] + io = StringIO.new("1 2 1 2 1 2") + io.pos = 2 + io.each_line(nil) {|s| seen << s} + seen.should == ["2 1 2 1 2"] + end + + it "yields each paragraph with all separation characters when passed an empty String as separator" do + seen = [] + io = StringIO.new("para1\n\npara2\n\n\npara3") + io.each_line("") {|s| seen << s} + seen.should == ["para1\n\n", "para2\n\n\n", "para3"] + end end describe "StringIO#each_line when passed no arguments" do - it_behaves_like :stringio_each_no_arguments, :each_line + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "yields each line to the passed block" do + seen = [] + @io.each_line {|s| seen << s } + seen.should == ["a b c d e\n", "1 2 3 4 5"] + end + + it "yields each line starting from the current position" do + seen = [] + @io.pos = 4 + @io.each_line {|s| seen << s } + seen.should == ["c d e\n", "1 2 3 4 5"] + end + + it "does not change $_" do + $_ = "test" + @io.each_line { |s| s} + $_.should == "test" + end + + it "uses $/ as the default line separator" do + seen = [] + begin + old_rs = $/ + suppress_warning {$/ = " "} + @io.each_line {|s| seen << s } + seen.should.eql?(["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"]) + ensure + suppress_warning {$/ = old_rs} + end + end + + it "returns self" do + @io.each_line {|l| l }.should.equal?(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.each_line + enum.instance_of?(Enumerator).should == true + + seen = [] + enum.each { |b| seen << b } + seen.should == ["a b c d e\n", "1 2 3 4 5"] + end end describe "StringIO#each_line when self is not readable" do - it_behaves_like :stringio_each_not_readable, :each_line + it "raises an IOError" do + io = StringIO.new(+"a b c d e", "w") + -> { io.each_line { |b| b } }.should.raise(IOError) + + io = StringIO.new("a b c d e") + io.close_read + -> { io.each_line { |b| b } }.should.raise(IOError) + end +end + +describe "StringIO#each_line when passed chomp" do + it "yields each line with removed newline characters to the passed block" do + seen = [] + io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end") + io.each_line(chomp: true) {|s| seen << s } + seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"] + end + + it "returns each line with removed newline characters when called without block" do + seen = [] + io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end") + enum = io.each_line(chomp: true) + enum.each {|s| seen << s } + seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"] + end end describe "StringIO#each_line when passed chomp" do - it_behaves_like :stringio_each_chomp, :each_line + it "yields each line with removed separator to the passed block" do + seen = [] + io = StringIO.new("a b \nc d e|1 2 3 4 5\n|the end") + io.each_line("|", chomp: true) {|s| seen << s } + seen.should == ["a b \nc d e", "1 2 3 4 5\n", "the end"] + end + + it "returns each line with removed separator when called without block" do + seen = [] + io = StringIO.new("a b \nc d e|1 2 3 4 5\n|the end") + enum = io.each_line("|", chomp: true) + enum.each {|s| seen << s } + seen.should == ["a b \nc d e", "1 2 3 4 5\n", "the end"] + end end describe "StringIO#each_line when passed limit" do - it_behaves_like :stringio_each_limit, :each_line + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "returns the data read until the limit is met" do + seen = [] + @io.each_line(4) { |s| seen << s } + seen.should == ["a b ", "c d ", "e\n", "1 2 ", "3 4 ", "5"] + end end describe "StringIO#each when passed separator and limit" do - it_behaves_like :stringio_each_separator_and_limit, :each_line + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns the data read until the limit is consumed or the separator is met" do + @io.each_line('>', 8) { |s| break s }.should == "this>" + @io.each_line('>', 2) { |s| break s }.should == "is" + @io.each_line('>', 10) { |s| break s }.should == ">" + @io.each_line('>', 6) { |s| break s }.should == "an>" + @io.each_line('>', 5) { |s| break s }.should == "examp" + end + + it "truncates the multi-character separator at the end to meet the limit" do + @io.each_line("is>an", 7) { |s| break s }.should == "this>is" + end + + it "does not change $_" do + $_ = "test" + @io.each_line('>', 8) { |s| s } + $_.should == "test" + end + + it "updates self's lineno by one" do + @io.each_line('>', 3) { |s| break s } + @io.lineno.should.eql?(1) + + @io.each_line('>', 3) { |s| break s } + @io.lineno.should.eql?(2) + + @io.each_line('>', 3) { |s| break s } + @io.lineno.should.eql?(3) + end + + it "tries to convert the passed separator to a String using #to_str" do # TODO + obj = mock('to_str') + obj.should_receive(:to_str).and_return('>') + + seen = [] + @io.each_line(obj, 5) { |s| seen << s } + seen.should == ["this>", "is>", "an>", "examp", "le"] + end + + it "does not raise TypeError if passed separator is nil" do + @io.each_line(nil, 5) { |s| break s }.should == "this>" + end + + it "tries to convert the passed limit to an Integer using #to_int" do # TODO + obj = mock('to_int') + obj.should_receive(:to_int).and_return(5) + + seen = [] + @io.each_line('>', obj) { |s| seen << s } + seen.should == ["this>", "is>", "an>", "examp", "le"] + end end diff --git a/spec/ruby/library/stringio/each_spec.rb b/spec/ruby/library/stringio/each_spec.rb index 7eb322f3ff..f3785bc18f 100644 --- a/spec/ruby/library/stringio/each_spec.rb +++ b/spec/ruby/library/stringio/each_spec.rb @@ -1,31 +1,8 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/each' +require 'stringio' -describe "StringIO#each when passed a separator" do - it_behaves_like :stringio_each_separator, :each -end - -describe "StringIO#each when passed no arguments" do - it_behaves_like :stringio_each_no_arguments, :each -end - -describe "StringIO#each when self is not readable" do - it_behaves_like :stringio_each_not_readable, :each -end - -describe "StringIO#each when passed chomp" do - it_behaves_like :stringio_each_chomp, :each -end - -describe "StringIO#each when passed chomp" do - it_behaves_like :stringio_each_separator_and_chomp, :each -end - -describe "StringIO#each when passed limit" do - it_behaves_like :stringio_each_limit, :each -end - -describe "StringIO#each when passed separator and limit" do - it_behaves_like :stringio_each_separator_and_limit, :each +describe "StringIO#each" do + it "is an alias of StringIO#each_line" do + StringIO.instance_method(:each).should == StringIO.instance_method(:each_line) + end end diff --git a/spec/ruby/library/stringio/eof_spec.rb b/spec/ruby/library/stringio/eof_spec.rb index af0170977c..acc49305f5 100644 --- a/spec/ruby/library/stringio/eof_spec.rb +++ b/spec/ruby/library/stringio/eof_spec.rb @@ -1,11 +1,33 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/eof' +require 'stringio' describe "StringIO#eof?" do - it_behaves_like :stringio_eof, :eof? + before :each do + @io = StringIO.new("eof") + end + + it "returns true when self's position is greater than or equal to self's size" do + @io.pos = 3 + @io.eof?.should == true + + @io.pos = 6 + @io.eof?.should == true + end + + it "returns false when self's position is less than self's size" do + @io.pos = 0 + @io.eof?.should == false + + @io.pos = 1 + @io.eof?.should == false + + @io.pos = 2 + @io.eof?.should == false + end end describe "StringIO#eof" do - it_behaves_like :stringio_eof, :eof + it "is an alias of StringIO#eof?" do + StringIO.instance_method(:eof).should == StringIO.instance_method(:eof?) + end end diff --git a/spec/ruby/library/stringio/isatty_spec.rb b/spec/ruby/library/stringio/isatty_spec.rb index 1ef33978b5..07743acc12 100644 --- a/spec/ruby/library/stringio/isatty_spec.rb +++ b/spec/ruby/library/stringio/isatty_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/isatty' +require 'stringio' describe "StringIO#isatty" do - it_behaves_like :stringio_isatty, :isatty + it "is an alias of StringIO#tty?" do + StringIO.instance_method(:isatty).should == StringIO.instance_method(:tty?) + end end diff --git a/spec/ruby/library/stringio/length_spec.rb b/spec/ruby/library/stringio/length_spec.rb index d3070f50a7..a83be6256a 100644 --- a/spec/ruby/library/stringio/length_spec.rb +++ b/spec/ruby/library/stringio/length_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/length' +require 'stringio' describe "StringIO#length" do - it_behaves_like :stringio_length, :length + it "returns the length of the wrapped string" do + StringIO.new("example").length.should == 7 + end end diff --git a/spec/ruby/library/stringio/pos_spec.rb b/spec/ruby/library/stringio/pos_spec.rb index ba640f8c18..16f068b049 100644 --- a/spec/ruby/library/stringio/pos_spec.rb +++ b/spec/ruby/library/stringio/pos_spec.rb @@ -1,9 +1,17 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/tell' describe "StringIO#pos" do - it_behaves_like :stringio_tell, :pos + before :each do + @io = StringIOSpecs.build + end + + it "returns the current byte offset" do + @io.getc + @io.pos.should == 1 + @io.read(7) + @io.pos.should == 8 + end end describe "StringIO#pos=" do diff --git a/spec/ruby/library/stringio/shared/codepoints.rb b/spec/ruby/library/stringio/shared/codepoints.rb deleted file mode 100644 index e35a02ccb4..0000000000 --- a/spec/ruby/library/stringio/shared/codepoints.rb +++ /dev/null @@ -1,45 +0,0 @@ -# -*- encoding: utf-8 -*- -describe :stringio_codepoints, shared: true do - before :each do - @io = StringIO.new("∂φ/∂x = gaîté") - @enum = @io.send(@method) - end - - it "returns an Enumerator" do - @enum.should.instance_of?(Enumerator) - end - - it "yields each codepoint code in turn" do - @enum.to_a.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] - end - - it "yields each codepoint starting from the current position" do - @io.pos = 15 - @enum.to_a.should == [238, 116, 233] - end - - it "raises an error if reading invalid sequence" do - @io.pos = 1 # inside of a multibyte sequence - -> { @enum.first }.should.raise(ArgumentError) - end - - it "raises an IOError if not readable" do - @io.close_read - -> { @enum.to_a }.should.raise(IOError) - - io = StringIO.new(+"xyz", "w") - -> { io.send(@method).to_a }.should.raise(IOError) - end - - - it "calls the given block" do - r = [] - @io.send(@method){|c| r << c } - r.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] - end - - it "returns self" do - @io.send(@method) {|l| l }.should.equal?(@io) - end - -end diff --git a/spec/ruby/library/stringio/shared/each.rb b/spec/ruby/library/stringio/shared/each.rb deleted file mode 100644 index 04e40b6b2a..0000000000 --- a/spec/ruby/library/stringio/shared/each.rb +++ /dev/null @@ -1,209 +0,0 @@ -describe :stringio_each_separator, shared: true do - before :each do - @io = StringIO.new("a b c d e\n1 2 3 4 5") - end - - it "uses the passed argument as the line separator" do - seen = [] - @io.send(@method, " ") {|s| seen << s} - seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] - end - - it "does not change $_" do - $_ = "test" - @io.send(@method, " ") { |s| s} - $_.should == "test" - end - - it "returns self" do - @io.send(@method) {|l| l }.should.equal?(@io) - end - - it "tries to convert the passed separator to a String using #to_str" do - obj = mock("to_str") - obj.stub!(:to_str).and_return(" ") - - seen = [] - @io.send(@method, obj) { |l| seen << l } - seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] - end - - it "yields self's content starting from the current position when the passed separator is nil" do - seen = [] - io = StringIO.new("1 2 1 2 1 2") - io.pos = 2 - io.send(@method, nil) {|s| seen << s} - seen.should == ["2 1 2 1 2"] - end - - it "yields each paragraph with all separation characters when passed an empty String as separator" do - seen = [] - io = StringIO.new("para1\n\npara2\n\n\npara3") - io.send(@method, "") {|s| seen << s} - seen.should == ["para1\n\n", "para2\n\n\n", "para3"] - end -end - -describe :stringio_each_no_arguments, shared: true do - before :each do - @io = StringIO.new("a b c d e\n1 2 3 4 5") - end - - it "yields each line to the passed block" do - seen = [] - @io.send(@method) {|s| seen << s } - seen.should == ["a b c d e\n", "1 2 3 4 5"] - end - - it "yields each line starting from the current position" do - seen = [] - @io.pos = 4 - @io.send(@method) {|s| seen << s } - seen.should == ["c d e\n", "1 2 3 4 5"] - end - - it "does not change $_" do - $_ = "test" - @io.send(@method) { |s| s} - $_.should == "test" - end - - it "uses $/ as the default line separator" do - seen = [] - begin - old_rs = $/ - suppress_warning {$/ = " "} - @io.send(@method) {|s| seen << s } - seen.should.eql?(["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"]) - ensure - suppress_warning {$/ = old_rs} - end - end - - it "returns self" do - @io.send(@method) {|l| l }.should.equal?(@io) - end - - it "returns an Enumerator when passed no block" do - enum = @io.send(@method) - enum.instance_of?(Enumerator).should == true - - seen = [] - enum.each { |b| seen << b } - seen.should == ["a b c d e\n", "1 2 3 4 5"] - end -end - -describe :stringio_each_not_readable, shared: true do - it "raises an IOError" do - io = StringIO.new(+"a b c d e", "w") - -> { io.send(@method) { |b| b } }.should.raise(IOError) - - io = StringIO.new("a b c d e") - io.close_read - -> { io.send(@method) { |b| b } }.should.raise(IOError) - end -end - -describe :stringio_each_chomp, shared: true do - it "yields each line with removed newline characters to the passed block" do - seen = [] - io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end") - io.send(@method, chomp: true) {|s| seen << s } - seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"] - end - - it "returns each line with removed newline characters when called without block" do - seen = [] - io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end") - enum = io.send(@method, chomp: true) - enum.each {|s| seen << s } - seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"] - end -end - -describe :stringio_each_separator_and_chomp, shared: true do - it "yields each line with removed separator to the passed block" do - seen = [] - io = StringIO.new("a b \nc d e|1 2 3 4 5\n|the end") - io.send(@method, "|", chomp: true) {|s| seen << s } - seen.should == ["a b \nc d e", "1 2 3 4 5\n", "the end"] - end - - it "returns each line with removed separator when called without block" do - seen = [] - io = StringIO.new("a b \nc d e|1 2 3 4 5\n|the end") - enum = io.send(@method, "|", chomp: true) - enum.each {|s| seen << s } - seen.should == ["a b \nc d e", "1 2 3 4 5\n", "the end"] - end -end - -describe :stringio_each_limit, shared: true do - before :each do - @io = StringIO.new("a b c d e\n1 2 3 4 5") - end - - it "returns the data read until the limit is met" do - seen = [] - @io.send(@method, 4) { |s| seen << s } - seen.should == ["a b ", "c d ", "e\n", "1 2 ", "3 4 ", "5"] - end -end - -describe :stringio_each_separator_and_limit, shared: true do - before :each do - @io = StringIO.new("this>is>an>example") - end - - it "returns the data read until the limit is consumed or the separator is met" do - @io.send(@method, '>', 8) { |s| break s }.should == "this>" - @io.send(@method, '>', 2) { |s| break s }.should == "is" - @io.send(@method, '>', 10) { |s| break s }.should == ">" - @io.send(@method, '>', 6) { |s| break s }.should == "an>" - @io.send(@method, '>', 5) { |s| break s }.should == "examp" - end - - it "truncates the multi-character separator at the end to meet the limit" do - @io.send(@method, "is>an", 7) { |s| break s }.should == "this>is" - end - - it "does not change $_" do - $_ = "test" - @io.send(@method, '>', 8) { |s| s } - $_.should == "test" - end - - it "updates self's lineno by one" do - @io.send(@method, '>', 3) { |s| break s } - @io.lineno.should.eql?(1) - - @io.send(@method, '>', 3) { |s| break s } - @io.lineno.should.eql?(2) - - @io.send(@method, '>', 3) { |s| break s } - @io.lineno.should.eql?(3) - end - - it "tries to convert the passed separator to a String using #to_str" do # TODO - obj = mock('to_str') - obj.should_receive(:to_str).and_return('>') - - seen = [] - @io.send(@method, obj, 5) { |s| seen << s } - seen.should == ["this>", "is>", "an>", "examp", "le"] - end - - it "does not raise TypeError if passed separator is nil" do - @io.send(@method, nil, 5) { |s| break s }.should == "this>" - end - - it "tries to convert the passed limit to an Integer using #to_int" do # TODO - obj = mock('to_int') - obj.should_receive(:to_int).and_return(5) - - seen = [] - @io.send(@method, '>', obj) { |s| seen << s } - seen.should == ["this>", "is>", "an>", "examp", "le"] - end -end diff --git a/spec/ruby/library/stringio/shared/each_byte.rb b/spec/ruby/library/stringio/shared/each_byte.rb deleted file mode 100644 index b3939c26de..0000000000 --- a/spec/ruby/library/stringio/shared/each_byte.rb +++ /dev/null @@ -1,48 +0,0 @@ -describe :stringio_each_byte, shared: true do - before :each do - @io = StringIO.new("xyz") - end - - it "yields each character code in turn" do - seen = [] - @io.send(@method) { |b| seen << b } - seen.should == [120, 121, 122] - end - - it "updates the position before each yield" do - seen = [] - @io.send(@method) { |b| seen << @io.pos } - seen.should == [1, 2, 3] - end - - it "does not yield if the current position is out of bounds" do - @io.pos = 1000 - seen = nil - @io.send(@method) { |b| seen = b } - seen.should == nil - end - - it "returns self" do - @io.send(@method) {}.should.equal?(@io) - end - - it "returns an Enumerator when passed no block" do - enum = @io.send(@method) - enum.instance_of?(Enumerator).should == true - - seen = [] - enum.each { |b| seen << b } - seen.should == [120, 121, 122] - end -end - -describe :stringio_each_byte_not_readable, shared: true do - it "raises an IOError" do - io = StringIO.new(+"xyz", "w") - -> { io.send(@method) { |b| b } }.should.raise(IOError) - - io = StringIO.new("xyz") - io.close_read - -> { io.send(@method) { |b| b } }.should.raise(IOError) - end -end diff --git a/spec/ruby/library/stringio/shared/each_char.rb b/spec/ruby/library/stringio/shared/each_char.rb deleted file mode 100644 index 4215a9952b..0000000000 --- a/spec/ruby/library/stringio/shared/each_char.rb +++ /dev/null @@ -1,36 +0,0 @@ -# -*- encoding: utf-8 -*- -describe :stringio_each_char, shared: true do - before :each do - @io = StringIO.new("xyz äöü") - end - - it "yields each character code in turn" do - seen = [] - @io.send(@method) { |c| seen << c } - seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] - end - - it "returns self" do - @io.send(@method) {}.should.equal?(@io) - end - - it "returns an Enumerator when passed no block" do - enum = @io.send(@method) - enum.instance_of?(Enumerator).should == true - - seen = [] - enum.each { |c| seen << c } - seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] - end -end - -describe :stringio_each_char_not_readable, shared: true do - it "raises an IOError" do - io = StringIO.new(+"xyz", "w") - -> { io.send(@method) { |b| b } }.should.raise(IOError) - - io = StringIO.new("xyz") - io.close_read - -> { io.send(@method) { |b| b } }.should.raise(IOError) - end -end diff --git a/spec/ruby/library/stringio/shared/eof.rb b/spec/ruby/library/stringio/shared/eof.rb deleted file mode 100644 index a9489581fc..0000000000 --- a/spec/ruby/library/stringio/shared/eof.rb +++ /dev/null @@ -1,24 +0,0 @@ -describe :stringio_eof, shared: true do - before :each do - @io = StringIO.new("eof") - end - - it "returns true when self's position is greater than or equal to self's size" do - @io.pos = 3 - @io.send(@method).should == true - - @io.pos = 6 - @io.send(@method).should == true - end - - it "returns false when self's position is less than self's size" do - @io.pos = 0 - @io.send(@method).should == false - - @io.pos = 1 - @io.send(@method).should == false - - @io.pos = 2 - @io.send(@method).should == false - end -end diff --git a/spec/ruby/library/stringio/shared/isatty.rb b/spec/ruby/library/stringio/shared/isatty.rb deleted file mode 100644 index 2b92e8d656..0000000000 --- a/spec/ruby/library/stringio/shared/isatty.rb +++ /dev/null @@ -1,5 +0,0 @@ -describe :stringio_isatty, shared: true do - it "returns false" do - StringIO.new("tty").send(@method).should == false - end -end diff --git a/spec/ruby/library/stringio/shared/length.rb b/spec/ruby/library/stringio/shared/length.rb deleted file mode 100644 index 60a4eb1bdd..0000000000 --- a/spec/ruby/library/stringio/shared/length.rb +++ /dev/null @@ -1,5 +0,0 @@ -describe :stringio_length, shared: true do - it "returns the length of the wrapped string" do - StringIO.new("example").send(@method).should == 7 - end -end diff --git a/spec/ruby/library/stringio/shared/tell.rb b/spec/ruby/library/stringio/shared/tell.rb deleted file mode 100644 index 852c51c192..0000000000 --- a/spec/ruby/library/stringio/shared/tell.rb +++ /dev/null @@ -1,12 +0,0 @@ -describe :stringio_tell, shared: true do - before :each do - @io = StringIOSpecs.build - end - - it "returns the current byte offset" do - @io.getc - @io.send(@method).should == 1 - @io.read(7) - @io.send(@method).should == 8 - end -end diff --git a/spec/ruby/library/stringio/size_spec.rb b/spec/ruby/library/stringio/size_spec.rb index f674d22db9..33e574ddae 100644 --- a/spec/ruby/library/stringio/size_spec.rb +++ b/spec/ruby/library/stringio/size_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/length' +require 'stringio' describe "StringIO#size" do - it_behaves_like :stringio_length, :size + it "is an alias of StringIO#length" do + StringIO.instance_method(:size).should == StringIO.instance_method(:length) + end end diff --git a/spec/ruby/library/stringio/tell_spec.rb b/spec/ruby/library/stringio/tell_spec.rb index 8350ee6f4d..80095999e9 100644 --- a/spec/ruby/library/stringio/tell_spec.rb +++ b/spec/ruby/library/stringio/tell_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/tell' +require 'stringio' describe "StringIO#tell" do - it_behaves_like :stringio_tell, :tell + it "is an alias of StringIO#pos" do + StringIO.instance_method(:tell).should == StringIO.instance_method(:pos) + end end diff --git a/spec/ruby/library/stringio/tty_spec.rb b/spec/ruby/library/stringio/tty_spec.rb index c6293dcbd7..87e22d49a5 100644 --- a/spec/ruby/library/stringio/tty_spec.rb +++ b/spec/ruby/library/stringio/tty_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/isatty' +require 'stringio' describe "StringIO#tty?" do - it_behaves_like :stringio_isatty, :tty? + it "returns false" do + StringIO.new("tty").tty?.should == false + end end diff --git a/spec/ruby/library/stringscanner/append_spec.rb b/spec/ruby/library/stringscanner/append_spec.rb index fef5dcf2bd..68747d52d7 100644 --- a/spec/ruby/library/stringscanner/append_spec.rb +++ b/spec/ruby/library/stringscanner/append_spec.rb @@ -1,11 +1,33 @@ require_relative '../../spec_helper' -require_relative 'shared/concat' require 'strscan' describe "StringScanner#<<" do - it_behaves_like :strscan_concat, :<< + it "concatenates the given argument to self and returns self" do + s = StringScanner.new(+"hello ") + (s. << 'world').should == s + s.string.should == "hello world" + s.eos?.should == false + end + + it "raises a TypeError if the given argument can't be converted to a String" do + -> { StringScanner.new('hello') << :world }.should.raise(TypeError) + -> { StringScanner.new('hello') << mock('x') }.should.raise(TypeError) + end end describe "StringScanner#<< when passed an Integer" do - it_behaves_like :strscan_concat_fixnum, :<< + it "raises a TypeError" do + a = StringScanner.new("hello world") + -> { a << 333 }.should.raise(TypeError) + b = StringScanner.new("") + -> { b << (256 * 3 + 64) }.should.raise(TypeError) + -> { b << -200 }.should.raise(TypeError) + end + + it "doesn't call to_int on the argument" do + x = mock('x') + x.should_not_receive(:to_int) + + -> { StringScanner.new("") << x }.should.raise(TypeError) + end end diff --git a/spec/ruby/library/stringscanner/beginning_of_line_spec.rb b/spec/ruby/library/stringscanner/beginning_of_line_spec.rb index 3f6f0da75f..ae97f52fe0 100644 --- a/spec/ruby/library/stringscanner/beginning_of_line_spec.rb +++ b/spec/ruby/library/stringscanner/beginning_of_line_spec.rb @@ -1,7 +1,28 @@ require_relative '../../spec_helper' -require_relative 'shared/bol' require 'strscan' describe "StringScanner#beginning_of_line?" do - it_behaves_like :strscan_bol, :beginning_of_line? + it "returns true if the scan pointer is at the beginning of the line, false otherwise" do + s = StringScanner.new("This is a test") + s.beginning_of_line?.should == true + s.scan(/This/) + s.beginning_of_line?.should == false + s.terminate + s.beginning_of_line?.should == false + + s = StringScanner.new("hello\nworld") + s.beginning_of_line?.should == true + s.scan(/\w+/) + s.beginning_of_line?.should == false + s.scan(/\n/) + s.beginning_of_line?.should == true + s.unscan + s.beginning_of_line?.should == false + end + + it "returns true if the scan pointer is at the end of the line of an empty string." do + s = StringScanner.new('') + s.terminate + s.beginning_of_line?.should == true + end end diff --git a/spec/ruby/library/stringscanner/bol_spec.rb b/spec/ruby/library/stringscanner/bol_spec.rb index d31766e0e2..1d10c8f7c0 100644 --- a/spec/ruby/library/stringscanner/bol_spec.rb +++ b/spec/ruby/library/stringscanner/bol_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/bol' require 'strscan' describe "StringScanner#bol?" do - it_behaves_like :strscan_bol, :bol? + it "is an alias of StringScanner#beginning_of_line?" do + StringScanner.instance_method(:bol?).should == StringScanner.instance_method(:beginning_of_line?) + end end diff --git a/spec/ruby/library/stringscanner/concat_spec.rb b/spec/ruby/library/stringscanner/concat_spec.rb index 4f790e2505..716268c956 100644 --- a/spec/ruby/library/stringscanner/concat_spec.rb +++ b/spec/ruby/library/stringscanner/concat_spec.rb @@ -1,11 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/concat' require 'strscan' describe "StringScanner#concat" do - it_behaves_like :strscan_concat, :concat -end - -describe "StringScanner#concat when passed an Integer" do - it_behaves_like :strscan_concat_fixnum, :concat + it "is an alias of StringScanner#<<" do + StringScanner.instance_method(:concat).should == StringScanner.instance_method(:<<) + end end diff --git a/spec/ruby/library/stringscanner/pointer_spec.rb b/spec/ruby/library/stringscanner/pointer_spec.rb index bc0c0c50b7..5fc6c8cdf3 100644 --- a/spec/ruby/library/stringscanner/pointer_spec.rb +++ b/spec/ruby/library/stringscanner/pointer_spec.rb @@ -1,11 +1,14 @@ require_relative '../../spec_helper' -require_relative 'shared/pos' require 'strscan' describe "StringScanner#pointer" do - it_behaves_like :strscan_pos, :pointer + it "is an alias of StringScanner#pos" do + StringScanner.instance_method(:pointer).should == StringScanner.instance_method(:pos) + end end describe "StringScanner#pointer=" do - it_behaves_like :strscan_pos_set, :pointer= + it "is an alias of StringScanner#pos=" do + StringScanner.instance_method(:pointer=).should == StringScanner.instance_method(:pos=) + end end diff --git a/spec/ruby/library/stringscanner/pos_spec.rb b/spec/ruby/library/stringscanner/pos_spec.rb index 275fecf0f3..bc3003ebdf 100644 --- a/spec/ruby/library/stringscanner/pos_spec.rb +++ b/spec/ruby/library/stringscanner/pos_spec.rb @@ -1,11 +1,62 @@ require_relative '../../spec_helper' -require_relative 'shared/pos' require 'strscan' describe "StringScanner#pos" do - it_behaves_like :strscan_pos, :pos + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the position of the scan pointer" do + @s.pos.should == 0 + @s.scan_until(/This is/) + @s.pos.should == 7 + @s.get_byte + @s.pos.should == 8 + @s.terminate + @s.pos.should == 14 + end + + it "returns 0 in the reset position" do + @s.reset + @s.pos.should == 0 + end + + it "returns the length of the string in the terminate position" do + @s.terminate + @s.pos.should == @s.string.length + end + + it "is not multi-byte character sensitive" do + s = StringScanner.new("abcädeföghi") + + s.scan_until(/ö/) + s.pos.should == 10 + end end describe "StringScanner#pos=" do - it_behaves_like :strscan_pos_set, :pos= + before :each do + @s = StringScanner.new("This is a test") + end + + it "modify the scan pointer" do + @s.pos = 5 + @s.rest.should == "is a test" + end + + it "positions from the end if the argument is negative" do + @s.pos = -2 + @s.rest.should == "st" + @s.pos.should == 12 + end + + it "raises a RangeError if position too far backward" do + -> { + @s.pos = -20 + }.should.raise(RangeError) + end + + it "raises a RangeError when the passed argument is out of range" do + -> { @s.pos = 20 }.should.raise(RangeError) + end end diff --git a/spec/ruby/library/stringscanner/shared/bol.rb b/spec/ruby/library/stringscanner/shared/bol.rb deleted file mode 100644 index ec5c2051b5..0000000000 --- a/spec/ruby/library/stringscanner/shared/bol.rb +++ /dev/null @@ -1,25 +0,0 @@ -describe :strscan_bol, shared: true do - it "returns true if the scan pointer is at the beginning of the line, false otherwise" do - s = StringScanner.new("This is a test") - s.send(@method).should == true - s.scan(/This/) - s.send(@method).should == false - s.terminate - s.send(@method).should == false - - s = StringScanner.new("hello\nworld") - s.bol?.should == true - s.scan(/\w+/) - s.bol?.should == false - s.scan(/\n/) - s.bol?.should == true - s.unscan - s.bol?.should == false - end - - it "returns true if the scan pointer is at the end of the line of an empty string." do - s = StringScanner.new('') - s.terminate - s.send(@method).should == true - end -end diff --git a/spec/ruby/library/stringscanner/shared/concat.rb b/spec/ruby/library/stringscanner/shared/concat.rb deleted file mode 100644 index 8138b0f8dc..0000000000 --- a/spec/ruby/library/stringscanner/shared/concat.rb +++ /dev/null @@ -1,30 +0,0 @@ -describe :strscan_concat, shared: true do - it "concatenates the given argument to self and returns self" do - s = StringScanner.new(+"hello ") - s.send(@method, 'world').should == s - s.string.should == "hello world" - s.eos?.should == false - end - - it "raises a TypeError if the given argument can't be converted to a String" do - -> { StringScanner.new('hello').send(@method, :world) }.should.raise(TypeError) - -> { StringScanner.new('hello').send(@method, mock('x')) }.should.raise(TypeError) - end -end - -describe :strscan_concat_fixnum, shared: true do - it "raises a TypeError" do - a = StringScanner.new("hello world") - -> { a.send(@method, 333) }.should.raise(TypeError) - b = StringScanner.new("") - -> { b.send(@method, (256 * 3 + 64)) }.should.raise(TypeError) - -> { b.send(@method, -200) }.should.raise(TypeError) - end - - it "doesn't call to_int on the argument" do - x = mock('x') - x.should_not_receive(:to_int) - - -> { StringScanner.new("").send(@method, x) }.should.raise(TypeError) - end -end diff --git a/spec/ruby/library/stringscanner/shared/pos.rb b/spec/ruby/library/stringscanner/shared/pos.rb deleted file mode 100644 index 91f80fdf08..0000000000 --- a/spec/ruby/library/stringscanner/shared/pos.rb +++ /dev/null @@ -1,59 +0,0 @@ -describe :strscan_pos, shared: true do - before :each do - @s = StringScanner.new("This is a test") - end - - it "returns the position of the scan pointer" do - @s.send(@method).should == 0 - @s.scan_until(/This is/) - @s.send(@method).should == 7 - @s.get_byte - @s.send(@method).should == 8 - @s.terminate - @s.send(@method).should == 14 - end - - it "returns 0 in the reset position" do - @s.reset - @s.send(@method).should == 0 - end - - it "returns the length of the string in the terminate position" do - @s.terminate - @s.send(@method).should == @s.string.length - end - - it "is not multi-byte character sensitive" do - s = StringScanner.new("abcädeföghi") - - s.scan_until(/ö/) - s.pos.should == 10 - end -end - -describe :strscan_pos_set, shared: true do - before :each do - @s = StringScanner.new("This is a test") - end - - it "modify the scan pointer" do - @s.send(@method, 5) - @s.rest.should == "is a test" - end - - it "positions from the end if the argument is negative" do - @s.send(@method, -2) - @s.rest.should == "st" - @s.pos.should == 12 - end - - it "raises a RangeError if position too far backward" do - -> { - @s.send(@method, -20) - }.should.raise(RangeError) - end - - it "raises a RangeError when the passed argument is out of range" do - -> { @s.send(@method, 20) }.should.raise(RangeError) - end -end diff --git a/spec/ruby/library/syslog/open_spec.rb b/spec/ruby/library/syslog/open_spec.rb index 73e3780d78..3aceea007d 100644 --- a/spec/ruby/library/syslog/open_spec.rb +++ b/spec/ruby/library/syslog/open_spec.rb @@ -1,7 +1,6 @@ require_relative '../../spec_helper' platform_is_not :windows do - require_relative 'shared/reopen' require 'syslog' describe "Syslog.open" do @@ -87,6 +86,41 @@ platform_is_not :windows do end describe "Syslog.open!" do - it_behaves_like :syslog_reopen, :open! + before :each do + Syslog.opened?.should == false + end + + after :each do + Syslog.opened?.should == false + end + + it "reopens the log" do + Syslog.open + -> { Syslog.open! }.should_not.raise + Syslog.opened?.should == true + Syslog.close + end + + it "fails with RuntimeError if the log is closed" do + -> { Syslog.open! }.should.raise(RuntimeError) + end + + it "receives the same parameters as Syslog.open" do + Syslog.open + Syslog.open!("rubyspec", 3, 8) do |s| + s.should == Syslog + s.ident.should == "rubyspec" + s.options.should == 3 + s.facility.should == Syslog::LOG_USER + s.opened?.should == true + end + Syslog.opened?.should == false + end + + it "returns the module" do + Syslog.open + Syslog.open!.should == Syslog + Syslog.close + end end end diff --git a/spec/ruby/library/syslog/reopen_spec.rb b/spec/ruby/library/syslog/reopen_spec.rb index a78529fa1f..ef32d13a87 100644 --- a/spec/ruby/library/syslog/reopen_spec.rb +++ b/spec/ruby/library/syslog/reopen_spec.rb @@ -1,10 +1,11 @@ require_relative '../../spec_helper' platform_is_not :windows do - require_relative 'shared/reopen' require 'syslog' describe "Syslog.reopen" do - it_behaves_like :syslog_reopen, :reopen + it "is an alias of Syslog.open!" do + Syslog.method(:reopen).should == Syslog.method(:open!) + end end end diff --git a/spec/ruby/library/syslog/shared/reopen.rb b/spec/ruby/library/syslog/shared/reopen.rb deleted file mode 100644 index f04408e807..0000000000 --- a/spec/ruby/library/syslog/shared/reopen.rb +++ /dev/null @@ -1,40 +0,0 @@ -describe :syslog_reopen, shared: true do - platform_is_not :windows do - before :each do - Syslog.opened?.should == false - end - - after :each do - Syslog.opened?.should == false - end - - it "reopens the log" do - Syslog.open - -> { Syslog.send(@method)}.should_not.raise - Syslog.opened?.should == true - Syslog.close - end - - it "fails with RuntimeError if the log is closed" do - -> { Syslog.send(@method)}.should.raise(RuntimeError) - end - - it "receives the same parameters as Syslog.open" do - Syslog.open - Syslog.send(@method, "rubyspec", 3, 8) do |s| - s.should == Syslog - s.ident.should == "rubyspec" - s.options.should == 3 - s.facility.should == Syslog::LOG_USER - s.opened?.should == true - end - Syslog.opened?.should == false - end - - it "returns the module" do - Syslog.open - Syslog.send(@method).should == Syslog - Syslog.close - end - end -end diff --git a/spec/ruby/library/tempfile/delete_spec.rb b/spec/ruby/library/tempfile/delete_spec.rb index 0332b44dde..b126ceae6a 100644 --- a/spec/ruby/library/tempfile/delete_spec.rb +++ b/spec/ruby/library/tempfile/delete_spec.rb @@ -1,7 +1,15 @@ require_relative '../../spec_helper' -require_relative 'shared/unlink' require 'tempfile' describe "Tempfile#delete" do - it_behaves_like :tempfile_unlink, :delete + before :each do + @tempfile = Tempfile.new("specs") + end + + it "unlinks self" do + @tempfile.close + path = @tempfile.path + @tempfile.delete + File.should_not.exist?(path) + end end diff --git a/spec/ruby/library/tempfile/length_spec.rb b/spec/ruby/library/tempfile/length_spec.rb index bc622b9a70..924c12942b 100644 --- a/spec/ruby/library/tempfile/length_spec.rb +++ b/spec/ruby/library/tempfile/length_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/length' require 'tempfile' describe "Tempfile#length" do - it_behaves_like :tempfile_length, :length + it "is an alias of Tempfile#size" do + Tempfile.instance_method(:length).should == Tempfile.instance_method(:size) + end end diff --git a/spec/ruby/library/tempfile/shared/length.rb b/spec/ruby/library/tempfile/shared/length.rb deleted file mode 100644 index 1a89ff7b4d..0000000000 --- a/spec/ruby/library/tempfile/shared/length.rb +++ /dev/null @@ -1,21 +0,0 @@ -describe :tempfile_length, shared: true do - before :each do - @tempfile = Tempfile.new("specs") - end - - after :each do - @tempfile.close! - end - - it "returns the size of self" do - @tempfile.send(@method).should.eql?(0) - @tempfile.print("Test!") - @tempfile.send(@method).should.eql?(5) - end - - it "returns the size of self even if self is closed" do - @tempfile.print("Test!") - @tempfile.close - @tempfile.send(@method).should.eql?(5) - end -end diff --git a/spec/ruby/library/tempfile/shared/unlink.rb b/spec/ruby/library/tempfile/shared/unlink.rb deleted file mode 100644 index e821228d70..0000000000 --- a/spec/ruby/library/tempfile/shared/unlink.rb +++ /dev/null @@ -1,12 +0,0 @@ -describe :tempfile_unlink, shared: true do - before :each do - @tempfile = Tempfile.new("specs") - end - - it "unlinks self" do - @tempfile.close - path = @tempfile.path - @tempfile.send(@method) - File.should_not.exist?(path) - end -end diff --git a/spec/ruby/library/tempfile/size_spec.rb b/spec/ruby/library/tempfile/size_spec.rb index f4824601c7..5a7edf8e4b 100644 --- a/spec/ruby/library/tempfile/size_spec.rb +++ b/spec/ruby/library/tempfile/size_spec.rb @@ -1,7 +1,24 @@ require_relative '../../spec_helper' -require_relative 'shared/length' require 'tempfile' describe "Tempfile#size" do - it_behaves_like :tempfile_length, :size + before :each do + @tempfile = Tempfile.new("specs") + end + + after :each do + @tempfile.close! + end + + it "returns the size of self" do + @tempfile.size.should.eql?(0) + @tempfile.print("Test!") + @tempfile.size.should.eql?(5) + end + + it "returns the size of self even if self is closed" do + @tempfile.print("Test!") + @tempfile.close + @tempfile.size.should.eql?(5) + end end diff --git a/spec/ruby/library/tempfile/unlink_spec.rb b/spec/ruby/library/tempfile/unlink_spec.rb index eac7df8472..c03fc34a54 100644 --- a/spec/ruby/library/tempfile/unlink_spec.rb +++ b/spec/ruby/library/tempfile/unlink_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/unlink' require 'tempfile' describe "Tempfile#unlink" do - it_behaves_like :tempfile_unlink, :unlink + it "is an alias of Tempfile#delete" do + Tempfile.instance_method(:unlink).should == Tempfile.instance_method(:delete) + end end diff --git a/spec/ruby/library/time/iso8601_spec.rb b/spec/ruby/library/time/iso8601_spec.rb index ab35ab25d6..d78de76792 100644 --- a/spec/ruby/library/time/iso8601_spec.rb +++ b/spec/ruby/library/time/iso8601_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/xmlschema' require 'time' describe "Time.iso8601" do - it_behaves_like :time_library_xmlschema, :iso8601 + it "is an alias of Time.xmlschema" do + Time.method(:iso8601).should == Time.method(:xmlschema) + end end diff --git a/spec/ruby/library/time/rfc2822_spec.rb b/spec/ruby/library/time/rfc2822_spec.rb index 7fc5e9a64b..14824e2396 100644 --- a/spec/ruby/library/time/rfc2822_spec.rb +++ b/spec/ruby/library/time/rfc2822_spec.rb @@ -1,7 +1,68 @@ require_relative '../../spec_helper' -require_relative 'shared/rfc2822' require 'time' describe "Time.rfc2822" do - it_behaves_like :time_rfc2822, :rfc2822 + it "parses RFC-822 strings" do + t1 = (Time.utc(1976, 8, 26, 14, 30) + 4 * 3600) + t2 = Time.rfc2822("26 Aug 76 14:30 EDT") + t1.should == t2 + + t3 = Time.utc(1976, 8, 27, 9, 32) + 7 * 3600 + t4 = Time.rfc2822("27 Aug 76 09:32 PDT") + t3.should == t4 + end + + it "parses RFC-2822 strings" do + t1 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 + t2 = Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600") + t1.should == t2 + + t3 = Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600 + t4 = Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200") + t3.should == t4 + + t5 = Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600 + t6 = Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600") + t5.should == t6 + + t7 = Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600 + t8 = Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600") + t7.should == t8 + + t9 = Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600 + t10 = Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800") + t9.should == t10 + + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + t11 = Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60 + t12 = Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330") + t11.should == t12 + + t13 = Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60 + t14 = Time.rfc2822(" Thu, + 13 + Feb + 1969 + 23:32 + -0330 (Newfoundland Time)") + t13.should == t14 + end + + t15 = Time.utc(1997, 11, 21, 9, 55, 6) + t16 = Time.rfc2822("21 Nov 97 09:55:06 GMT") + t15.should == t16 + + t17 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 + t18 = Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600") + t17.should == t18 + + -> { + # inner comment is not supported. + Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600") + }.should.raise(ArgumentError) + end end diff --git a/spec/ruby/library/time/rfc822_spec.rb b/spec/ruby/library/time/rfc822_spec.rb index da77e6ee77..e32e9becae 100644 --- a/spec/ruby/library/time/rfc822_spec.rb +++ b/spec/ruby/library/time/rfc822_spec.rb @@ -1,7 +1,8 @@ require_relative '../../spec_helper' -require_relative 'shared/rfc2822' require 'time' describe "Time.rfc822" do - it_behaves_like :time_rfc2822, :rfc822 + it "is an alias of Time.rfc2822" do + Time.method(:rfc822).should == Time.method(:rfc2822) + end end diff --git a/spec/ruby/library/time/shared/rfc2822.rb b/spec/ruby/library/time/shared/rfc2822.rb deleted file mode 100644 index 49ef76db47..0000000000 --- a/spec/ruby/library/time/shared/rfc2822.rb +++ /dev/null @@ -1,65 +0,0 @@ -describe :time_rfc2822, shared: true do - it "parses RFC-822 strings" do - t1 = (Time.utc(1976, 8, 26, 14, 30) + 4 * 3600) - t2 = Time.send(@method, "26 Aug 76 14:30 EDT") - t1.should == t2 - - t3 = Time.utc(1976, 8, 27, 9, 32) + 7 * 3600 - t4 = Time.send(@method, "27 Aug 76 09:32 PDT") - t3.should == t4 - end - - it "parses RFC-2822 strings" do - t1 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 - t2 = Time.send(@method, "Fri, 21 Nov 1997 09:55:06 -0600") - t1.should == t2 - - t3 = Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600 - t4 = Time.send(@method, "Tue, 1 Jul 2003 10:52:37 +0200") - t3.should == t4 - - t5 = Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600 - t6 = Time.send(@method, "Fri, 21 Nov 1997 10:01:10 -0600") - t5.should == t6 - - t7 = Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600 - t8 = Time.send(@method, "Fri, 21 Nov 1997 11:00:00 -0600") - t7.should == t8 - - t9 = Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600 - t10 = Time.send(@method, "Mon, 24 Nov 1997 14:22:01 -0800") - t9.should == t10 - - begin - Time.at(-1) - rescue ArgumentError - # ignore - else - t11 = Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60 - t12 = Time.send(@method, "Thu, 13 Feb 1969 23:32:54 -0330") - t11.should == t12 - - t13 = Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60 - t14 = Time.send(@method, " Thu, - 13 - Feb - 1969 - 23:32 - -0330 (Newfoundland Time)") - t13.should == t14 - end - - t15 = Time.utc(1997, 11, 21, 9, 55, 6) - t16 = Time.send(@method, "21 Nov 97 09:55:06 GMT") - t15.should == t16 - - t17 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 - t18 = Time.send(@method, "Fri, 21 Nov 1997 09 : 55 : 06 -0600") - t17.should == t18 - - -> { - # inner comment is not supported. - Time.send(@method, "Fri, 21 Nov 1997 09(comment): 55 : 06 -0600") - }.should.raise(ArgumentError) - end -end diff --git a/spec/ruby/library/time/shared/xmlschema.rb b/spec/ruby/library/time/shared/xmlschema.rb deleted file mode 100644 index 0002886ca5..0000000000 --- a/spec/ruby/library/time/shared/xmlschema.rb +++ /dev/null @@ -1,53 +0,0 @@ -describe :time_library_xmlschema, shared: true do - it "parses ISO-8601 strings" do - t = Time.utc(1985, 4, 12, 23, 20, 50, 520000) - s = "1985-04-12T23:20:50.52Z" - t.should == Time.send(@method, s) - #s.should == t.send(@method, 2) - - t = Time.utc(1996, 12, 20, 0, 39, 57) - s = "1996-12-19T16:39:57-08:00" - t.should == Time.send(@method, s) - # There is no way to generate time string with arbitrary timezone. - s = "1996-12-20T00:39:57Z" - t.should == Time.send(@method, s) - #assert_equal(s, t.send(@method)) - - t = Time.utc(1990, 12, 31, 23, 59, 60) - s = "1990-12-31T23:59:60Z" - t.should == Time.send(@method, s) - # leap second is representable only if timezone file has it. - s = "1990-12-31T15:59:60-08:00" - t.should == Time.send(@method, s) - - begin - Time.at(-1) - rescue ArgumentError - # ignore - else - t = Time.utc(1937, 1, 1, 11, 40, 27, 870000) - s = "1937-01-01T12:00:27.87+00:20" - t.should == Time.send(@method, s) - end - - # more - - # (Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600).should == Time.send(@method, "1999-05-31T13:20:00-05:00") - # (Time.local(2000, 1, 20, 12, 0, 0)).should == Time.send(@method, "2000-01-20T12:00:00") - # (Time.utc(2000, 1, 20, 12, 0, 0)).should == Time.send(@method, "2000-01-20T12:00:00Z") - # (Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600).should == Time.send(@method, "2000-01-20T12:00:00+12:00") - # (Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600).should == Time.send(@method, "2000-01-20T12:00:00-13:00") - # (Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600).should == Time.send(@method, "2000-03-04T23:00:00+03:00") - # (Time.utc(2000, 3, 4, 20, 0, 0)).should == Time.send(@method, "2000-03-04T20:00:00Z") - # (Time.local(2000, 1, 15, 0, 0, 0)).should == Time.send(@method, "2000-01-15T00:00:00") - # (Time.local(2000, 2, 15, 0, 0, 0)).should == Time.send(@method, "2000-02-15T00:00:00") - # (Time.local(2000, 1, 15, 12, 0, 0)).should == Time.send(@method, "2000-01-15T12:00:00") - # (Time.utc(2000, 1, 16, 12, 0, 0)).should == Time.send(@method, "2000-01-16T12:00:00Z") - # (Time.local(2000, 1, 1, 12, 0, 0)).should == Time.send(@method, "2000-01-01T12:00:00") - # (Time.utc(1999, 12, 31, 23, 0, 0)).should == Time.send(@method, "1999-12-31T23:00:00Z") - # (Time.local(2000, 1, 16, 12, 0, 0)).should == Time.send(@method, "2000-01-16T12:00:00") - # (Time.local(2000, 1, 16, 0, 0, 0)).should == Time.send(@method, "2000-01-16T00:00:00") - # (Time.utc(2000, 1, 12, 12, 13, 14)).should == Time.send(@method, "2000-01-12T12:13:14Z") - # (Time.utc(2001, 4, 17, 19, 23, 17, 300000)).should == Time.send(@method, "2001-04-17T19:23:17.3Z") - end -end diff --git a/spec/ruby/library/time/xmlschema_spec.rb b/spec/ruby/library/time/xmlschema_spec.rb index ff3c864a02..1f7d63979a 100644 --- a/spec/ruby/library/time/xmlschema_spec.rb +++ b/spec/ruby/library/time/xmlschema_spec.rb @@ -1,7 +1,56 @@ require_relative '../../spec_helper' -require_relative 'shared/xmlschema' require 'time' describe "Time.xmlschema" do - it_behaves_like :time_library_xmlschema, :xmlschema + it "parses ISO-8601 strings" do + t = Time.utc(1985, 4, 12, 23, 20, 50, 520000) + s = "1985-04-12T23:20:50.52Z" + t.should == Time.xmlschema(s) + #s.should == t.xmlschema(2) + + t = Time.utc(1996, 12, 20, 0, 39, 57) + s = "1996-12-19T16:39:57-08:00" + t.should == Time.xmlschema(s) + # There is no way to generate time string with arbitrary timezone. + s = "1996-12-20T00:39:57Z" + t.should == Time.xmlschema(s) + #assert_equal(s, t.xmlschema) + + t = Time.utc(1990, 12, 31, 23, 59, 60) + s = "1990-12-31T23:59:60Z" + t.should == Time.xmlschema(s) + # leap second is representable only if timezone file has it. + s = "1990-12-31T15:59:60-08:00" + t.should == Time.xmlschema(s) + + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + t = Time.utc(1937, 1, 1, 11, 40, 27, 870000) + s = "1937-01-01T12:00:27.87+00:20" + t.should == Time.xmlschema(s) + end + + # more + + # (Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600).should == Time.xmlschema("1999-05-31T13:20:00-05:00") + # (Time.local(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00") + # (Time.utc(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00Z") + # (Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600).should == Time.xmlschema("2000-01-20T12:00:00+12:00") + # (Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600).should == Time.xmlschema("2000-01-20T12:00:00-13:00") + # (Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600).should == Time.xmlschema("2000-03-04T23:00:00+03:00") + # (Time.utc(2000, 3, 4, 20, 0, 0)).should == Time.xmlschema("2000-03-04T20:00:00Z") + # (Time.local(2000, 1, 15, 0, 0, 0)).should == Time.xmlschema("2000-01-15T00:00:00") + # (Time.local(2000, 2, 15, 0, 0, 0)).should == Time.xmlschema("2000-02-15T00:00:00") + # (Time.local(2000, 1, 15, 12, 0, 0)).should == Time.xmlschema("2000-01-15T12:00:00") + # (Time.utc(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00Z") + # (Time.local(2000, 1, 1, 12, 0, 0)).should == Time.xmlschema("2000-01-01T12:00:00") + # (Time.utc(1999, 12, 31, 23, 0, 0)).should == Time.xmlschema("1999-12-31T23:00:00Z") + # (Time.local(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00") + # (Time.local(2000, 1, 16, 0, 0, 0)).should == Time.xmlschema("2000-01-16T00:00:00") + # (Time.utc(2000, 1, 12, 12, 13, 14)).should == Time.xmlschema("2000-01-12T12:13:14Z") + # (Time.utc(2001, 4, 17, 19, 23, 17, 300000)).should == Time.xmlschema("2001-04-17T19:23:17.3Z") + end end diff --git a/spec/ruby/library/uri/parser/extract_spec.rb b/spec/ruby/library/uri/parser/extract_spec.rb index 20d4565b08..f5ecd6ec8e 100644 --- a/spec/ruby/library/uri/parser/extract_spec.rb +++ b/spec/ruby/library/uri/parser/extract_spec.rb @@ -1,7 +1,90 @@ require_relative '../../../spec_helper' -require_relative '../shared/extract' require 'uri' describe "URI::Parser#extract" do - it_behaves_like :uri_extract, :extract, URI::Parser.new + before :all do + @parser = URI::Parser.new + end + + it "behaves according to its documentation" do + @parser.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.").should == ["http://foo.example.org/bla", "mailto:test@example.com"] + end + + it "treats contiguous URIs as a single URI" do + @parser.extract('http://example.jphttp://example.jp').should == ['http://example.jphttp://example.jp'] + end + + it "treats pretty much anything with a colon as a URI" do + @parser.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').should == ['From:', 'mailto:xxx@xxx.xxx.xxx]'] + end + + it "wraps a URI string in an array" do + @parser.extract("http://github.com/brixen/rubyspec/tree/master").should == ["http://github.com/brixen/rubyspec/tree/master"] + end + + it "pulls a variety of protocol URIs from a string" do + @parser.extract("this is a string, it has http://rubini.us/ in it").should == ["http://rubini.us/"] + @parser.extract("mailto:spambait@example.com").should == ["mailto:spambait@example.com"] + @parser.extract("ftp://ruby-lang.org/").should == ["ftp://ruby-lang.org/"] + @parser.extract("https://mail.google.com").should == ["https://mail.google.com"] + @parser.extract("anything://example.com/").should == ["anything://example.com/"] + end + + it "pulls all URIs within a string in order into an array when a block is not given" do + @parser.extract("1.3. Example URI + + The following examples illustrate URI that are in common use. + + ftp://ftp.is.co.za/rfc/rfc1808.txt + -- ftp scheme for File Transfer Protocol services + + gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles + -- gopher scheme for Gopher and Gopher+ Protocol services + + http://www.math.uio.no/faq/compression-faq/part1.html + -- http scheme for Hypertext Transfer Protocol services + + mailto:mduerst@ifi.unizh.ch + -- mailto scheme for electronic mail addresses + + news:comp.infosystems.www.servers.unix + -- news scheme for USENET news groups and articles + + telnet://melvyl.ucop.edu/ + -- telnet scheme for interactive services via the TELNET Protocol + ").should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch","news:comp.infosystems.www.servers.unix","telnet://melvyl.ucop.edu/"] + end + + it "yields each URI in the given string in order to a block, if given, and returns nil" do + results = ["http://foo.example.org/bla", "mailto:test@example.com"] + @parser.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") {|uri| + uri.should == results.shift + }.should == nil + results.should == [] + end + + it "allows the user to specify a list of acceptable protocols of URIs to scan for" do + @parser.extract("1.3. Example URI + + The following examples illustrate URI that are in common use. + + ftp://ftp.is.co.za/rfc/rfc1808.txt + -- ftp scheme for File Transfer Protocol services + + gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles + -- gopher scheme for Gopher and Gopher+ Protocol services + + http://www.math.uio.no/faq/compression-faq/part1.html + -- http scheme for Hypertext Transfer Protocol services + + mailto:mduerst@ifi.unizh.ch + -- mailto scheme for electronic mail addresses + + news:comp.infosystems.www.servers.unix + -- news scheme for USENET news groups and articles + + telnet://melvyl.ucop.edu/ + -- telnet scheme for interactive services via the TELNET Protocol + ", ["http","ftp","mailto"]).should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch"] + end end diff --git a/spec/ruby/library/uri/parser/join_spec.rb b/spec/ruby/library/uri/parser/join_spec.rb index 0c9230be76..0fb29cf00a 100644 --- a/spec/ruby/library/uri/parser/join_spec.rb +++ b/spec/ruby/library/uri/parser/join_spec.rb @@ -1,7 +1,62 @@ require_relative '../../../spec_helper' -require_relative '../shared/join' require 'uri' describe "URI::Parser#join" do - it_behaves_like :uri_join, :join, URI::Parser.new + before :all do + @parser = URI::Parser.new + end + + it "returns a URI object of the concatenation of a protocol and domain, and a path" do + @parser.join("http://localhost/","main.rbx").should == URI.parse("http://localhost/main.rbx") + end + + it "accepts URI objects" do + @parser.join(URI("http://localhost/"),"main.rbx").should == URI.parse("http://localhost/main.rbx") + @parser.join("http://localhost/",URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + @parser.join(URI("http://localhost/"),URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + end + + it "accepts string-like arguments with to_str" do + str = mock('string-like') + str.should_receive(:to_str).and_return("http://ruby-lang.org") + str2 = mock('string-like also') + str2.should_receive(:to_str).and_return("foo/bar") + @parser.join(str, str2).should == URI.parse("http://ruby-lang.org/foo/bar") + end + + it "raises an error if given no argument" do + -> { + @parser.join + }.should.raise(ArgumentError) + end + + it "doesn't create redundant '/'s" do + @parser.join("http://localhost/", "/main.rbx").should == URI.parse("http://localhost/main.rbx") + end + + it "discards arguments given before an absolute uri" do + @parser.join("http://localhost/a/b/c/d", "http://ruby-lang.com/foo", "bar").should == URI.parse("http://ruby-lang.com/bar") + end + + it "resolves .. in paths" do + @parser.join("http://localhost/a/b/c/d", "../../e/f", "g/h/../i").to_s.should == "http://localhost/a/e/g/i" + end end + +# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar')) +# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar')) +# assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/')) +# +# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz')) +# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz')) +# assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/')) +# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge')) +# +# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz')) +# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) +# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) +# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) +# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) diff --git a/spec/ruby/library/uri/parser/parse_spec.rb b/spec/ruby/library/uri/parser/parse_spec.rb index df126eab6d..0e6a06ebe5 100644 --- a/spec/ruby/library/uri/parser/parse_spec.rb +++ b/spec/ruby/library/uri/parser/parse_spec.rb @@ -1,7 +1,213 @@ require_relative '../../../spec_helper' require_relative '../fixtures/classes' -require_relative '../shared/parse' describe "URI::Parser#parse" do - it_behaves_like :uri_parse, :parse, URI::Parser.new + before :all do + @parser = URI::Parser.new + end + + it "returns a URI::HTTP object when parsing an HTTP URI" do + @parser.parse("http://www.example.com/").should.is_a?(URI::HTTP) + end + + it "populates the components of a parsed URI::HTTP, setting the port to 80 by default" do + # general case + URISpec.components(@parser.parse("http://user:pass@example.com/path/?query=val&q2=val2#fragment")).should == { + scheme: "http", + userinfo: "user:pass", + host: "example.com", + port: 80, + path: "/path/", + query: "query=val&q2=val2", + fragment: "fragment" + } + + # multiple paths + URISpec.components(@parser.parse("http://a/b/c/d;p?q")).should == { + scheme: "http", + userinfo: nil, + host: "a", + port: 80, + path: "/b/c/d;p", + query: "q", + fragment: nil + } + + # multi-level domain + URISpec.components(@parser.parse('http://www.math.uio.no/faq/compression-faq/part1.html')).should == { + scheme: "http", + userinfo: nil, + host: "www.math.uio.no", + port: 80, + path: "/faq/compression-faq/part1.html", + query: nil, + fragment: nil + } + end + + it "parses out the port number of a URI, when given" do + @parser.parse("http://example.com:8080/").port.should == 8080 + end + + it "returns a URI::HTTPS object when parsing an HTTPS URI" do + @parser.parse("https://important-intern-net.net").should.is_a?(URI::HTTPS) + end + + it "sets the port of a parsed https URI to 443 by default" do + @parser.parse("https://example.com/").port.should == 443 + end + + it "populates the components of a parsed URI::FTP object" do + # generic, empty password. + url = @parser.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i") + url.should.is_a?(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: "anonymous", + host: "ruby-lang.org", + port: 21, + path: "pub/ruby/1.8/ruby-1.8.6.tar.bz2", + typecode: "i" + } + + # multidomain, no user or password + url = @parser.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt') + url.should.is_a?(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: nil, + host: "ftp.is.co.za", + port: 21, + path: "rfc/rfc1808.txt", + typecode: nil + } + + # empty user + url = @parser.parse('ftp://:pass@localhost/') + url.should.is_a?(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: ":pass", + host: "localhost", + port: 21, + path: "", + typecode: nil + } + url.password.should == "pass" + end + + it "returns a URI::LDAP object when parsing an LDAP URI" do + #taken from http://www.faqs.org/rfcs/rfc2255.html 'cause I don't really know what an LDAP url looks like + ldap_uris = %w{ ldap:///o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) ldap://ldap.itd.umich.edu/c=GB?objectClass?one ldap://ldap.question.com/o=Question%3f,c=US?mail ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04) ldap:///??sub??bindname=cn=Manager%2co=Foo ldap:///??sub??!bindname=cn=Manager%2co=Foo } + ldap_uris.each do |ldap_uri| + @parser.parse(ldap_uri).should.is_a?(URI::LDAP) + end + end + + it "populates the components of a parsed URI::LDAP object" do + URISpec.components(@parser.parse("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress?scope?filter?extensions")).should == { + scheme: "ldap", + host: "ldap.itd.umich.edu", + port: 389, + dn: "o=University%20of%20Michigan,c=US", + attributes: "postalAddress", + scope: "scope", + filter: "filter", + extensions: "extensions" + } + end + + it "returns a URI::MailTo object when passed a mailto URI" do + @parser.parse("mailto:spam@mailinator.com").should.is_a?(URI::MailTo) + end + + it "populates the components of a parsed URI::MailTo object" do + URISpec.components(@parser.parse("mailto:spam@mailinator.com?subject=Discounts%20On%20Imported%20methods!!!&body=Exciting%20offer")).should == { + scheme: "mailto", + to: "spam@mailinator.com", + headers: [["subject","Discounts%20On%20Imported%20methods!!!"], + ["body", "Exciting%20offer"]] + } + end + + # TODO + # Test registry + it "does its best to extract components from URI::Generic objects" do + # generic + URISpec.components(URI("scheme://userinfo@host/path?query#fragment")).should == { + scheme: "scheme", + userinfo: "userinfo", + host: "host", + port: nil, + path: "/path", + query: "query", + fragment: "fragment", + registry: nil, + opaque: nil + } + + # gopher + gopher = @parser.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles') + gopher.should.is_a?(URI::Generic) + + URISpec.components(gopher).should == { + scheme: "gopher", + userinfo: nil, + host: "spinaltap.micro.umn.edu", + port: nil, + path: "/00/Weather/California/Los%20Angeles", + query: nil, + fragment: nil, + registry: nil, + opaque: nil + } + + # news + news = @parser.parse('news:comp.infosystems.www.servers.unix') + news.should.is_a?(URI::Generic) + URISpec.components(news).should == { + scheme: "news", + userinfo: nil, + host: nil, + port: nil, + path: nil, + query: nil, + fragment: nil, + registry: nil, + opaque: "comp.infosystems.www.servers.unix" + } + + # telnet + telnet = @parser.parse('telnet://melvyl.ucop.edu/') + telnet.should.is_a?(URI::Generic) + URISpec.components(telnet).should == { + scheme: "telnet", + userinfo: nil, + host: "melvyl.ucop.edu", + port: nil, + path: "/", + query: nil, + fragment: nil, + registry: nil, + opaque: nil + } + + # files + file_l = @parser.parse('file:///foo/bar.txt') + file_l.should.is_a?(URI::Generic) + file = @parser.parse('file:/foo/bar.txt') + file.should.is_a?(URI::Generic) + end + + if URI::DEFAULT_PARSER == URI::RFC2396_Parser + it "raises errors on malformed URIs" do + -> { @parser.parse('http://a_b:80/') }.should.raise(URI::InvalidURIError) + -> { @parser.parse('http://a_b/') }.should.raise(URI::InvalidURIError) + end + elsif URI::DEFAULT_PARSER == URI::RFC3986_Parser + it "does not raise errors on URIs contained underscore" do + -> { @parser.parse('http://a_b:80/') }.should_not.raise(URI::InvalidURIError) + -> { @parser.parse('http://a_b/') }.should_not.raise(URI::InvalidURIError) + end + end end diff --git a/spec/ruby/library/uri/shared/extract.rb b/spec/ruby/library/uri/shared/extract.rb deleted file mode 100644 index efe60ae4b9..0000000000 --- a/spec/ruby/library/uri/shared/extract.rb +++ /dev/null @@ -1,83 +0,0 @@ -describe :uri_extract, shared: true do - it "behaves according to its documentation" do - @object.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.").should == ["http://foo.example.org/bla", "mailto:test@example.com"] - end - - it "treats contiguous URIs as a single URI" do - @object.extract('http://example.jphttp://example.jp').should == ['http://example.jphttp://example.jp'] - end - - it "treats pretty much anything with a colon as a URI" do - @object.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').should == ['From:', 'mailto:xxx@xxx.xxx.xxx]'] - end - - it "wraps a URI string in an array" do - @object.extract("http://github.com/brixen/rubyspec/tree/master").should == ["http://github.com/brixen/rubyspec/tree/master"] - end - - it "pulls a variety of protocol URIs from a string" do - @object.extract("this is a string, it has http://rubini.us/ in it").should == ["http://rubini.us/"] - @object.extract("mailto:spambait@example.com").should == ["mailto:spambait@example.com"] - @object.extract("ftp://ruby-lang.org/").should == ["ftp://ruby-lang.org/"] - @object.extract("https://mail.google.com").should == ["https://mail.google.com"] - @object.extract("anything://example.com/").should == ["anything://example.com/"] - end - - it "pulls all URIs within a string in order into an array when a block is not given" do - @object.extract("1.3. Example URI - - The following examples illustrate URI that are in common use. - - ftp://ftp.is.co.za/rfc/rfc1808.txt - -- ftp scheme for File Transfer Protocol services - - gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles - -- gopher scheme for Gopher and Gopher+ Protocol services - - http://www.math.uio.no/faq/compression-faq/part1.html - -- http scheme for Hypertext Transfer Protocol services - - mailto:mduerst@ifi.unizh.ch - -- mailto scheme for electronic mail addresses - - news:comp.infosystems.www.servers.unix - -- news scheme for USENET news groups and articles - - telnet://melvyl.ucop.edu/ - -- telnet scheme for interactive services via the TELNET Protocol - ").should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch","news:comp.infosystems.www.servers.unix","telnet://melvyl.ucop.edu/"] - end - - it "yields each URI in the given string in order to a block, if given, and returns nil" do - results = ["http://foo.example.org/bla", "mailto:test@example.com"] - @object.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") {|uri| - uri.should == results.shift - }.should == nil - results.should == [] - end - - it "allows the user to specify a list of acceptable protocols of URIs to scan for" do - @object.extract("1.3. Example URI - - The following examples illustrate URI that are in common use. - - ftp://ftp.is.co.za/rfc/rfc1808.txt - -- ftp scheme for File Transfer Protocol services - - gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles - -- gopher scheme for Gopher and Gopher+ Protocol services - - http://www.math.uio.no/faq/compression-faq/part1.html - -- http scheme for Hypertext Transfer Protocol services - - mailto:mduerst@ifi.unizh.ch - -- mailto scheme for electronic mail addresses - - news:comp.infosystems.www.servers.unix - -- news scheme for USENET news groups and articles - - telnet://melvyl.ucop.edu/ - -- telnet scheme for interactive services via the TELNET Protocol - ", ["http","ftp","mailto"]).should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch"] - end -end diff --git a/spec/ruby/library/uri/shared/join.rb b/spec/ruby/library/uri/shared/join.rb deleted file mode 100644 index b1f5f1c72b..0000000000 --- a/spec/ruby/library/uri/shared/join.rb +++ /dev/null @@ -1,56 +0,0 @@ -describe :uri_join, shared: true do - it "returns a URI object of the concatenation of a protocol and domain, and a path" do - @object.join("http://localhost/","main.rbx").should == URI.parse("http://localhost/main.rbx") - end - - it "accepts URI objects" do - @object.join(URI("http://localhost/"),"main.rbx").should == URI.parse("http://localhost/main.rbx") - @object.join("http://localhost/",URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") - @object.join(URI("http://localhost/"),URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") - end - - it "accepts string-like arguments with to_str" do - str = mock('string-like') - str.should_receive(:to_str).and_return("http://ruby-lang.org") - str2 = mock('string-like also') - str2.should_receive(:to_str).and_return("foo/bar") - @object.join(str, str2).should == URI.parse("http://ruby-lang.org/foo/bar") - end - - it "raises an error if given no argument" do - -> { - @object.join - }.should.raise(ArgumentError) - end - - it "doesn't create redundant '/'s" do - @object.join("http://localhost/", "/main.rbx").should == URI.parse("http://localhost/main.rbx") - end - - it "discards arguments given before an absolute uri" do - @object.join("http://localhost/a/b/c/d", "http://ruby-lang.com/foo", "bar").should == URI.parse("http://ruby-lang.com/bar") - end - - it "resolves .. in paths" do - @object.join("http://localhost/a/b/c/d", "../../e/f", "g/h/../i").to_s.should == "http://localhost/a/e/g/i" - end -end - - -# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar')) -# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar')) -# assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/')) -# -# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz')) -# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz')) -# assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/')) -# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz')) -# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge')) -# -# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz')) -# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) -# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) -# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) -# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) -# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) -# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) diff --git a/spec/ruby/library/uri/shared/parse.rb b/spec/ruby/library/uri/shared/parse.rb deleted file mode 100644 index 7ec7179526..0000000000 --- a/spec/ruby/library/uri/shared/parse.rb +++ /dev/null @@ -1,206 +0,0 @@ -describe :uri_parse, shared: true do - it "returns a URI::HTTP object when parsing an HTTP URI" do - @object.parse("http://www.example.com/").should.is_a?(URI::HTTP) - end - - it "populates the components of a parsed URI::HTTP, setting the port to 80 by default" do - # general case - URISpec.components(@object.parse("http://user:pass@example.com/path/?query=val&q2=val2#fragment")).should == { - scheme: "http", - userinfo: "user:pass", - host: "example.com", - port: 80, - path: "/path/", - query: "query=val&q2=val2", - fragment: "fragment" - } - - # multiple paths - URISpec.components(@object.parse("http://a/b/c/d;p?q")).should == { - scheme: "http", - userinfo: nil, - host: "a", - port: 80, - path: "/b/c/d;p", - query: "q", - fragment: nil - } - - # multi-level domain - URISpec.components(@object.parse('http://www.math.uio.no/faq/compression-faq/part1.html')).should == { - scheme: "http", - userinfo: nil, - host: "www.math.uio.no", - port: 80, - path: "/faq/compression-faq/part1.html", - query: nil, - fragment: nil - } - end - - it "parses out the port number of a URI, when given" do - @object.parse("http://example.com:8080/").port.should == 8080 - end - - it "returns a URI::HTTPS object when parsing an HTTPS URI" do - @object.parse("https://important-intern-net.net").should.is_a?(URI::HTTPS) - end - - it "sets the port of a parsed https URI to 443 by default" do - @object.parse("https://example.com/").port.should == 443 - end - - it "populates the components of a parsed URI::FTP object" do - # generic, empty password. - url = @object.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i") - url.should.is_a?(URI::FTP) - URISpec.components(url).should == { - scheme: "ftp", - userinfo: "anonymous", - host: "ruby-lang.org", - port: 21, - path: "pub/ruby/1.8/ruby-1.8.6.tar.bz2", - typecode: "i" - } - - # multidomain, no user or password - url = @object.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt') - url.should.is_a?(URI::FTP) - URISpec.components(url).should == { - scheme: "ftp", - userinfo: nil, - host: "ftp.is.co.za", - port: 21, - path: "rfc/rfc1808.txt", - typecode: nil - } - - # empty user - url = @object.parse('ftp://:pass@localhost/') - url.should.is_a?(URI::FTP) - URISpec.components(url).should == { - scheme: "ftp", - userinfo: ":pass", - host: "localhost", - port: 21, - path: "", - typecode: nil - } - url.password.should == "pass" - end - - it "returns a URI::LDAP object when parsing an LDAP URI" do - #taken from http://www.faqs.org/rfcs/rfc2255.html 'cause I don't really know what an LDAP url looks like - ldap_uris = %w{ ldap:///o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) ldap://ldap.itd.umich.edu/c=GB?objectClass?one ldap://ldap.question.com/o=Question%3f,c=US?mail ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04) ldap:///??sub??bindname=cn=Manager%2co=Foo ldap:///??sub??!bindname=cn=Manager%2co=Foo } - ldap_uris.each do |ldap_uri| - @object.parse(ldap_uri).should.is_a?(URI::LDAP) - end - end - - it "populates the components of a parsed URI::LDAP object" do - URISpec.components(@object.parse("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress?scope?filter?extensions")).should == { - scheme: "ldap", - host: "ldap.itd.umich.edu", - port: 389, - dn: "o=University%20of%20Michigan,c=US", - attributes: "postalAddress", - scope: "scope", - filter: "filter", - extensions: "extensions" - } - end - - it "returns a URI::MailTo object when passed a mailto URI" do - @object.parse("mailto:spam@mailinator.com").should.is_a?(URI::MailTo) - end - - it "populates the components of a parsed URI::MailTo object" do - URISpec.components(@object.parse("mailto:spam@mailinator.com?subject=Discounts%20On%20Imported%20methods!!!&body=Exciting%20offer")).should == { - scheme: "mailto", - to: "spam@mailinator.com", - headers: [["subject","Discounts%20On%20Imported%20methods!!!"], - ["body", "Exciting%20offer"]] - } - end - - # TODO - # Test registry - it "does its best to extract components from URI::Generic objects" do - # generic - URISpec.components(URI("scheme://userinfo@host/path?query#fragment")).should == { - scheme: "scheme", - userinfo: "userinfo", - host: "host", - port: nil, - path: "/path", - query: "query", - fragment: "fragment", - registry: nil, - opaque: nil - } - - # gopher - gopher = @object.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles') - gopher.should.is_a?(URI::Generic) - - URISpec.components(gopher).should == { - scheme: "gopher", - userinfo: nil, - host: "spinaltap.micro.umn.edu", - port: nil, - path: "/00/Weather/California/Los%20Angeles", - query: nil, - fragment: nil, - registry: nil, - opaque: nil - } - - # news - news = @object.parse('news:comp.infosystems.www.servers.unix') - news.should.is_a?(URI::Generic) - URISpec.components(news).should == { - scheme: "news", - userinfo: nil, - host: nil, - port: nil, - path: nil, - query: nil, - fragment: nil, - registry: nil, - opaque: "comp.infosystems.www.servers.unix" - } - - # telnet - telnet = @object.parse('telnet://melvyl.ucop.edu/') - telnet.should.is_a?(URI::Generic) - URISpec.components(telnet).should == { - scheme: "telnet", - userinfo: nil, - host: "melvyl.ucop.edu", - port: nil, - path: "/", - query: nil, - fragment: nil, - registry: nil, - opaque: nil - } - - # files - file_l = @object.parse('file:///foo/bar.txt') - file_l.should.is_a?(URI::Generic) - file = @object.parse('file:/foo/bar.txt') - file.should.is_a?(URI::Generic) - end - - if URI::DEFAULT_PARSER == URI::RFC2396_Parser - it "raises errors on malformed URIs" do - -> { @object.parse('http://a_b:80/') }.should.raise(URI::InvalidURIError) - -> { @object.parse('http://a_b/') }.should.raise(URI::InvalidURIError) - end - elsif URI::DEFAULT_PARSER == URI::RFC3986_Parser - it "does not raise errors on URIs contained underscore" do - -> { @object.parse('http://a_b:80/') }.should_not.raise(URI::InvalidURIError) - -> { @object.parse('http://a_b/') }.should_not.raise(URI::InvalidURIError) - end - end -end diff --git a/spec/ruby/library/yaml/load_stream_spec.rb b/spec/ruby/library/yaml/load_stream_spec.rb index 31bc862f5e..5f5d4c7337 100644 --- a/spec/ruby/library/yaml/load_stream_spec.rb +++ b/spec/ruby/library/yaml/load_stream_spec.rb @@ -1,9 +1,23 @@ require_relative '../../spec_helper' require_relative 'fixtures/strings' -require_relative 'shared/each_document' - require 'yaml' describe "YAML.load_stream" do - it_behaves_like :yaml_each_document, :load_stream + it "calls the block on each successive document" do + documents = [] + YAML.load_stream(YAMLSpecs::MULTIDOCUMENT) do |doc| + documents << doc + end + documents.should == [["Mark McGwire", "Sammy Sosa", "Ken Griffey"], + ["Chicago Cubs", "St Louis Cardinals"]] + end + + it "works on files" do + test_parse_file = fixture __FILE__, "test_yaml.yml" + File.open(test_parse_file, "r") do |file| + YAML.load_stream(file) do |doc| + doc.should == {"project"=>{"name"=>"RubySpec"}} + end + end + end end diff --git a/spec/ruby/library/yaml/shared/each_document.rb b/spec/ruby/library/yaml/shared/each_document.rb deleted file mode 100644 index 6f00aee297..0000000000 --- a/spec/ruby/library/yaml/shared/each_document.rb +++ /dev/null @@ -1,19 +0,0 @@ -describe :yaml_each_document, shared: true do - it "calls the block on each successive document" do - documents = [] - YAML.send(@method, YAMLSpecs::MULTIDOCUMENT) do |doc| - documents << doc - end - documents.should == [["Mark McGwire", "Sammy Sosa", "Ken Griffey"], - ["Chicago Cubs", "St Louis Cardinals"]] - end - - it "works on files" do - test_parse_file = fixture __FILE__, "test_yaml.yml" - File.open(test_parse_file, "r") do |file| - YAML.send(@method, file) do |doc| - doc.should == {"project"=>{"name"=>"RubySpec"}} - end - end - end -end diff --git a/spec/ruby/library/zlib/gzipreader/each_line_spec.rb b/spec/ruby/library/zlib/gzipreader/each_line_spec.rb index 6f17365879..97f24d410f 100644 --- a/spec/ruby/library/zlib/gzipreader/each_line_spec.rb +++ b/spec/ruby/library/zlib/gzipreader/each_line_spec.rb @@ -1,6 +1,9 @@ require_relative "../../../spec_helper" -require_relative 'shared/each' +require 'zlib' describe "Zlib::GzipReader#each_line" do - it_behaves_like :gzipreader_each, :each_line + it "is an alias of Zlib::GzipReader#each" do + Zlib::GzipReader.instance_method(:each_line).should == + Zlib::GzipReader.instance_method(:each) + end end diff --git a/spec/ruby/library/zlib/gzipreader/each_spec.rb b/spec/ruby/library/zlib/gzipreader/each_spec.rb index 3b98391a87..75fd7e6bae 100644 --- a/spec/ruby/library/zlib/gzipreader/each_spec.rb +++ b/spec/ruby/library/zlib/gzipreader/each_spec.rb @@ -1,6 +1,49 @@ require_relative "../../../spec_helper" -require_relative 'shared/each' +require 'stringio' +require 'zlib' describe "Zlib::GzipReader#each" do - it_behaves_like :gzipreader_each, :each + before :each do + @data = "firstline\nsecondline\n\nforthline" + @zip = [31, 139, 8, 0, 244, 125, 128, 88, 2, 255, 75, 203, 44, 42, 46, 201, + 201, 204, 75, 229, 42, 78, 77, 206, 207, 75, 1, 51, 185, 210,242, + 139, 74, 50, 64, 76, 0, 180, 54, 61, 111, 31, 0, 0, 0].pack('C*') + + @io = StringIO.new @zip + @gzreader = Zlib::GzipReader.new @io + end + + after :each do + ScratchPad.clear + end + + it "calls the given block for each line in the stream, passing the line as an argument" do + ScratchPad.record [] + @gzreader.each { |b| ScratchPad << b } + + ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"] + end + + it "returns an enumerator, which yields each byte in the stream, when no block is passed" do + enum = @gzreader.each + + ScratchPad.record [] + while true + begin + ScratchPad << enum.next + rescue StopIteration + break + end + end + + ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"] + end + + it "increments position before calling the block" do + i = 0 + @gzreader.each do |line| + i += line.length + @gzreader.pos.should == i + end + end end diff --git a/spec/ruby/library/zlib/gzipreader/eof_spec.rb b/spec/ruby/library/zlib/gzipreader/eof_spec.rb index a38e144c72..434e716b64 100644 --- a/spec/ruby/library/zlib/gzipreader/eof_spec.rb +++ b/spec/ruby/library/zlib/gzipreader/eof_spec.rb @@ -52,3 +52,10 @@ describe "Zlib::GzipReader#eof?" do gz.eof?.should == true end end + +describe "Zlib::GzipReader#eof" do + it "is an alias of Zlib::GzipReader#eof?" do + Zlib::GzipReader.instance_method(:eof).should == + Zlib::GzipReader.instance_method(:eof?) + end +end diff --git a/spec/ruby/library/zlib/gzipreader/shared/each.rb b/spec/ruby/library/zlib/gzipreader/shared/each.rb deleted file mode 100644 index 71608e04ab..0000000000 --- a/spec/ruby/library/zlib/gzipreader/shared/each.rb +++ /dev/null @@ -1,49 +0,0 @@ -require_relative '../../../../spec_helper' -require 'stringio' -require 'zlib' - -describe :gzipreader_each, shared: true do - before :each do - @data = "firstline\nsecondline\n\nforthline" - @zip = [31, 139, 8, 0, 244, 125, 128, 88, 2, 255, 75, 203, 44, 42, 46, 201, - 201, 204, 75, 229, 42, 78, 77, 206, 207, 75, 1, 51, 185, 210,242, - 139, 74, 50, 64, 76, 0, 180, 54, 61, 111, 31, 0, 0, 0].pack('C*') - - @io = StringIO.new @zip - @gzreader = Zlib::GzipReader.new @io - end - - after :each do - ScratchPad.clear - end - - it "calls the given block for each line in the stream, passing the line as an argument" do - ScratchPad.record [] - @gzreader.send(@method) { |b| ScratchPad << b } - - ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"] - end - - it "returns an enumerator, which yields each byte in the stream, when no block is passed" do - enum = @gzreader.send(@method) - - ScratchPad.record [] - while true - begin - ScratchPad << enum.next - rescue StopIteration - break - end - end - - ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"] - end - - it "increments position before calling the block" do - i = 0 - @gzreader.send(@method) do |line| - i += line.length - @gzreader.pos.should == i - end - end -end diff --git a/spec/ruby/library/zlib/gzipreader/tell_spec.rb b/spec/ruby/library/zlib/gzipreader/tell_spec.rb new file mode 100644 index 0000000000..cc103e57b4 --- /dev/null +++ b/spec/ruby/library/zlib/gzipreader/tell_spec.rb @@ -0,0 +1,9 @@ +require_relative "../../../spec_helper" +require 'zlib' + +describe "Zlib::GzipReader#tell" do + it "is an alias of Zlib::GzipReader#pos" do + Zlib::GzipReader.instance_method(:tell).should == + Zlib::GzipReader.instance_method(:pos) + end +end diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb index 35bd856e00..459a32d954 100644 --- a/spec/ruby/optional/capi/io_spec.rb +++ b/spec/ruby/optional/capi/io_spec.rb @@ -142,7 +142,7 @@ describe "C-API IO function" do describe "rb_io_check_closed" do it "does not raise an exception if the IO is not closed" do - # The MRI function is void, so we use should_not raise_error + # The MRI function is void, so we use should_not.raise -> { @o.rb_io_check_closed(@io) }.should_not.raise end @@ -221,7 +221,7 @@ describe "C-API IO function" do describe "rb_io_check_readable" do it "does not raise an exception if the IO is opened for reading" do - # The MRI function is void, so we use should_not raise_error + # The MRI function is void, so we use should_not.raise -> { @o.rb_io_check_readable(@r_io) }.should_not.raise end @@ -237,7 +237,7 @@ describe "C-API IO function" do describe "rb_io_check_writable" do it "does not raise an exception if the IO is opened for writing" do - # The MRI function is void, so we use should_not raise_error + # The MRI function is void, so we use should_not.raise -> { @o.rb_io_check_writable(@w_io) }.should_not.raise end diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb index 8459166906..04eaa94e6a 100644 --- a/spec/ruby/shared/kernel/raise.rb +++ b/spec/ruby/shared/kernel/raise.rb @@ -92,6 +92,21 @@ describe :kernel_raise, shared: true do -> { @object.raise("message", {cause: RuntimeError.new()}) }.should.raise(TypeError, "exception class/object expected") end + it "raises result from #exception when passed a non-Exception object" do + e = Object.new + def e.exception = StandardError.new + + -> { @object.raise e }.should.raise(StandardError) + end + + it "raises result from #exception with given arguments when passed a non-Exception object" do + e = Object.new + def e.exception(msg) = StandardError.new(msg) + + -> { @object.raise e, "foo" }.should.raise(StandardError, "foo") + -> { @object.raise e }.should.raise(ArgumentError, "wrong number of arguments (given 0, expected 1)") + end + it "raises TypeError when passed a non-Exception object but it responds to #exception method that doesn't return an instance of Exception class" do e = Object.new def e.exception diff --git a/spec/ruby/shared/process/fork.rb b/spec/ruby/shared/process/fork.rb index dd595cd93e..6c7ea75980 100644 --- a/spec/ruby/shared/process/fork.rb +++ b/spec/ruby/shared/process/fork.rb @@ -1,5 +1,5 @@ describe :process_fork, shared: true do - platform_is :windows do + guard_not -> { Process.respond_to?(:fork) } do it "returns false from #respond_to?" do # Workaround for Kernel::Method being public and losing the "non-respond_to? magic" mod = @object.class.name == "KernelSpecs::Method" ? Object.new : @object @@ -12,7 +12,7 @@ describe :process_fork, shared: true do end end - platform_is_not :windows do + guard -> { Process.respond_to?(:fork) } do before :each do @file = tmp('i_exist') rm_r @file @@ -2486,7 +2486,8 @@ rb_str_plus(VALUE str1, VALUE str2) { VALUE str3; rb_encoding *enc; - char *ptr1, *ptr2, *ptr3; + const char *ptr1, *ptr2; + char *ptr3; long len1, len2; int termlen; @@ -2912,12 +2913,14 @@ str_null_check(VALUE str, int *w) return s; } +static char *str_to_cstr(VALUE str); + const char * rb_str_null_check(VALUE str) { RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); - char *s; + const char *s; long len; RSTRING_GETMEM(str, s, len); @@ -2927,14 +2930,7 @@ rb_str_null_check(VALUE str) } } else { - int w; - const char *s = str_null_check(str, &w); - if (!s) { - if (w) { - rb_raise(rb_eArgError, "string contains null char"); - } - rb_raise(rb_eArgError, "string contains null byte"); - } + str_to_cstr(str); } return s; @@ -2951,6 +2947,12 @@ char * rb_string_value_cstr(volatile VALUE *ptr) { VALUE str = rb_string_value(ptr); + return str_to_cstr(str); +} + +static char * +str_to_cstr(VALUE str) +{ int w; char *s = str_null_check(str, &w); if (!s) { @@ -3127,7 +3129,7 @@ rb_str_sublen(VALUE str, long pos) if (single_byte_optimizable(str) || pos < 0) return pos; else { - char *p = RSTRING_PTR(str); + const char *p = RSTRING_PTR(str); return enc_strlen(p, p + pos, STR_ENC_GET(str), ENC_CODERANGE(str)); } } @@ -3196,7 +3198,7 @@ rb_str_subpos(VALUE str, long beg, long *lenp) long slen = -1L; const long blen = RSTRING_LEN(str); rb_encoding *enc = STR_ENC_GET(str); - char *p, *s = RSTRING_PTR(str), *e = s + blen; + const char *p, *s = RSTRING_PTR(str), *e = s + blen; if (len < 0) return 0; if (beg < 0 && -beg < 0) return 0; @@ -3273,7 +3275,7 @@ rb_str_subpos(VALUE str, long beg, long *lenp) end: *lenp = len; RB_GC_GUARD(str); - return p; + return (char *)p; } static VALUE str_substr(VALUE str, long beg, long len, int empty); @@ -3293,7 +3295,7 @@ rb_str_substr_two_fixnums(VALUE str, VALUE beg, VALUE len, int empty) static VALUE str_substr(VALUE str, long beg, long len, int empty) { - char *p = rb_str_subpos(str, beg, &len); + const char *p = rb_str_subpos(str, beg, &len); if (!p) return Qnil; if (!len && !empty) return Qnil; @@ -4767,10 +4769,9 @@ memrchr(const char *search_str, int chr, long search_len) static long str_rindex(VALUE str, VALUE sub, const char *s, rb_encoding *enc) { - char *hit, *adjusted; + const char *hit, *adjusted, *sbeg, *e, *t; int c; long slen, searchlen; - char *sbeg, *e, *t; sbeg = RSTRING_PTR(str); slen = RSTRING_LEN(sub); @@ -4805,7 +4806,7 @@ static long rb_str_rindex(VALUE str, VALUE sub, long pos) { long len, slen; - char *sbeg, *s; + const char *sbeg, *s; rb_encoding *enc; int singlebyte; @@ -4889,7 +4890,7 @@ static long rb_str_byterindex(VALUE str, VALUE sub, long pos) { long len, slen; - char *sbeg, *s; + const char *sbeg, *s; rb_encoding *enc; enc = rb_enc_check(str, sub); @@ -7251,6 +7252,21 @@ rb_str_escape(VALUE str) return result; } +/* Lookup table for the inspect fast path. 1 marks bytes that need + * no escaping. 0 marks bytes that need escape inspection: 0x00-0x1F + * (control), 0x22 ("), 0x23 (#), 0x5C (\), 0x7F (DEL), 0x80-0xFF + * (non-ASCII). */ +static const bool inspect_no_escape[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1F */ + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20-0x2F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x30-0x3F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40-0x4F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 0x50-0x5F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60-0x6F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 0x70-0x7F */ +}; + /* * call-seq: * inspect -> string @@ -7266,10 +7282,11 @@ rb_str_inspect(VALUE str) rb_encoding *enc = rb_enc_from_index(encidx); const char *p, *pend, *prev; char buf[CHAR_ESC_LEN + 1]; - VALUE result = rb_str_buf_new(0); + VALUE result = rb_str_buf_new(RSTRING_LEN(str) + 2); /* string content + surrounding quotes */ rb_encoding *resenc = rb_default_internal_encoding(); int unicode_p = rb_enc_unicode_p(enc); int asciicompat = rb_enc_asciicompat(enc); + int cr = rb_enc_str_coderange(str); if (resenc == NULL) resenc = rb_default_external_encoding(); if (!rb_enc_asciicompat(resenc)) resenc = rb_usascii_encoding(); @@ -7282,6 +7299,15 @@ rb_str_inspect(VALUE str) unsigned int c, cc; int n; + /* Fast path: bulk-skip runs of safe ASCII bytes via a lookup table. + * Only well-formed strings (CR=7BIT for any encoding, or UTF-8 VALID) + * are eligible. */ + if (cr == ENC_CODERANGE_7BIT || + (encidx == ENCINDEX_UTF_8 && cr == ENC_CODERANGE_VALID)) { + while (p < pend && inspect_no_escape[(unsigned char)*p]) p++; + if (p >= pend) break; + } + n = rb_enc_precise_mbclen(p, pend, enc); if (!MBCLEN_CHARFOUND_P(n)) { if (p > prev) str_buf_cat(result, prev, p - prev); @@ -8257,7 +8283,7 @@ typedef unsigned char *USTR; struct tr { int gen; unsigned int now, max; - char *p, *pend; + const char *p, *pend; }; static unsigned int @@ -8987,7 +9013,7 @@ rb_str_count(int argc, VALUE *argv, VALUE str) char table[TR_TABLE_SIZE]; rb_encoding *enc = 0; VALUE del = 0, nodel = 0, tstr; - char *s, *send; + const char *s, *send; int i; int ascompat; size_t n = 0; @@ -9218,12 +9244,12 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str) str_mod_check(str, str_start, str_len)) beg = 0; - char *ptr = RSTRING_PTR(str); - char *const str_start = ptr; + const char *ptr = RSTRING_PTR(str); + const char *const str_start = ptr; const long str_len = RSTRING_LEN(str); - char *const eptr = str_start + str_len; + const char *const eptr = str_start + str_len; if (split_type == SPLIT_TYPE_AWK) { - char *bptr = ptr; + const char *bptr = ptr; int skip = 1; unsigned int c; @@ -9282,8 +9308,8 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str) } } else if (split_type == SPLIT_TYPE_STRING) { - char *substr_start = ptr; - char *sptr = RSTRING_PTR(spat); + const char *substr_start = ptr; + const char *sptr = RSTRING_PTR(spat); long slen = RSTRING_LEN(spat); if (result) result = rb_ary_new(); @@ -9292,7 +9318,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str) while (ptr < eptr && (end = rb_memsearch(sptr, slen, ptr, eptr - ptr, enc)) >= 0) { /* Check we are at the start of a char */ - char *t = rb_enc_right_char_head(ptr, ptr + end, eptr, enc); + const char *t = rb_enc_right_char_head(ptr, ptr + end, eptr, enc); if (t != ptr + end) { ptr = t; continue; @@ -9431,8 +9457,8 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, VALUE ary) { rb_encoding *enc; VALUE line, rs, orig = str, opts = Qnil, chomp = Qfalse; - const char *ptr, *pend, *subptr, *subend, *rsptr, *hit, *adjusted; - long pos, len, rslen; + const char *pend, *subptr, *subend, *rsptr, *hit, *adjusted; + long pos, rslen; int rsnewline = 0; if (rb_scan_args(argc, argv, "01:", &rs, &opts) == 0) @@ -9457,9 +9483,9 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, VALUE ary) if (!RSTRING_LEN(str)) goto end; str = rb_str_new_frozen(str); - ptr = subptr = RSTRING_PTR(str); + const char *const ptr = subptr = RSTRING_PTR(str); + const long len = RSTRING_LEN(str); pend = RSTRING_END(str); - len = RSTRING_LEN(str); StringValue(rs); rslen = RSTRING_LEN(rs); @@ -10096,9 +10122,9 @@ chompped_length(VALUE str, VALUE rs) { rb_encoding *enc; int newline; - char *pp, *e, *rsptr; + const char *pp, *e, *rsptr; long rslen; - char *const p = RSTRING_PTR(str); + const char *const p = RSTRING_PTR(str); long len = RSTRING_LEN(str); if (len == 0) return 0; @@ -10311,7 +10337,7 @@ static VALUE rb_str_lstrip_bang(int argc, VALUE *argv, VALUE str) { rb_encoding *enc; - char *start, *s; + char *start; long olen, loffset; str_modify_keep_cr(str); @@ -10330,8 +10356,7 @@ rb_str_lstrip_bang(int argc, VALUE *argv, VALUE str) if (loffset > 0) { long len = olen-loffset; - s = start + loffset; - memmove(start, s, len); + memmove(start, start + loffset, len); STR_SET_LEN(str, len); TERM_FILL(start+len, rb_enc_mbminlen(enc)); return str; @@ -10370,7 +10395,7 @@ rb_str_lstrip_bang(int argc, VALUE *argv, VALUE str) static VALUE rb_str_lstrip(int argc, VALUE *argv, VALUE str) { - char *start; + const char *start; long len, loffset; RSTRING_GETMEM(str, start, len); @@ -10406,7 +10431,7 @@ rstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc) while (s < t && ((c = *(t-1)) == '\0' || ascii_isspace(c))) t--; } else { - char *tp; + const char *tp; while ((tp = rb_enc_prev_char(s, t, e, enc)) != NULL) { unsigned int c = rb_enc_codepoint(tp, e, enc); @@ -10421,8 +10446,7 @@ static long rstrip_offset_table(VALUE str, const char *s, const char *e, rb_encoding *enc, char table[TR_TABLE_SIZE], VALUE del, VALUE nodel) { - const char *t; - char *tp; + const char *t, *tp; rb_str_check_dummy_enc(enc); if (rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN) { @@ -10514,7 +10538,7 @@ static VALUE rb_str_rstrip(int argc, VALUE *argv, VALUE str) { rb_encoding *enc; - char *start; + const char *start; long olen, roffset; enc = STR_ENC_GET(str); @@ -10614,7 +10638,7 @@ rb_str_strip_bang(int argc, VALUE *argv, VALUE str) static VALUE rb_str_strip(int argc, VALUE *argv, VALUE str) { - char *start; + const char *start; long olen, loffset, roffset; rb_encoding *enc = STR_ENC_GET(str); @@ -10707,7 +10731,8 @@ rb_str_scan(VALUE str, VALUE pat) VALUE result; long start = 0; long last = -1, prev = 0; - char *p = RSTRING_PTR(str); long len = RSTRING_LEN(str); + const char *p = RSTRING_PTR(str); + long len = RSTRING_LEN(str); pat = get_pat_quoted(pat, 1); mustnot_broken(str); @@ -10955,8 +10980,7 @@ rb_str_crypt(VALUE str, VALUE salt) # define CRYPT_END() rb_nativethread_lock_unlock(&crypt_mutex.lock) #endif VALUE result; - const char *s, *saltp; - char *res; + const char *s, *saltp, *res; #ifdef BROKEN_CRYPT char salt_8bit_clean[3]; #endif @@ -11001,12 +11025,11 @@ rb_str_crypt(VALUE str, VALUE salt) // before allocating a new object (the string to be returned). If we allocate while // holding the lock, we could run GC which fires the VM barrier and causes a deadlock // if other ractors are waiting on this lock. - size_t res_size = strlen(res)+1; + size_t res_size = strlen(res); tmp_buf = ALLOCA_N(char, res_size); // should be small enough to alloca memcpy(tmp_buf, res, res_size); - res = tmp_buf; CRYPT_END(); - result = rb_str_new_cstr(res); + result = rb_str_new(tmp_buf, res_size); #endif return result; } diff --git a/test/json/json_ext_parser_test.rb b/test/json/json_ext_parser_test.rb index e610f642f1..d585b8d0dc 100644 --- a/test/json/json_ext_parser_test.rb +++ b/test/json/json_ext_parser_test.rb @@ -26,7 +26,7 @@ class JSONExtParserTest < Test::Unit::TestCase ex = assert_raise(ParserError) { parse('-Infinity something') } unless RUBY_PLATFORM =~ /java/ - assert_equal "unexpected token '-Infinity' at line 1 column 1", ex.message + assert_equal "invalid number: '-Infinity' at line 1 column 1", ex.message end ex = assert_raise(ParserError) { parse('NaN something') } diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index faa22f1424..a9b902ed45 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -350,6 +350,21 @@ class TestObjSpace < Test::Unit::TestCase RUBY end + def test_trace_object_allocations_does_not_reuse_freed_allocation_info + assert_separately(%w(-robjspace), <<~RUBY) + ObjectSpace.trace_object_allocations do + 1_000_000.times.map { Object.new } + end + + GC.start + + objs = 1_000_000.times.map { Object.new } + + leaked = objs.count { |obj| ObjectSpace.allocation_sourcefile(obj) } + assert_equal 0, leaked + RUBY + end + def test_dump_flags # Ensure that the fstring is promoted to old generation 4.times { GC.start } diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index 611b3b7715..e7eb0cd4b3 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -321,6 +321,17 @@ class TestRactor < Test::Unit::TestCase RUBY end + def test_mn_threads + # Ideally, we would assert that vm->ractor.sched.max_cpu equals sysconf(_SC_NPROCESSORS_ONLN) + # when RUBY_MAX_CPU is not set. + assert_ractor(<<~'RUBY', args: [{ "RUBY_MN_THREADS" => "1" }]) + require "etc" + n = Etc.respond_to?(:nprocessors) ? Etc.nprocessors : 8 + rs = n.times.map { Ractor.new { :ok } } + assert_equal [:ok] * n, rs.map(&:value) + RUBY + end + def test_symbol_proc_is_shareable pr = :symbol.to_proc assert_make_shareable(pr) diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index bf7a4a8dfc..8947694f53 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -60,6 +60,21 @@ class TestGemInstaller < Gem::InstallerTestCase end end + def test_app_script_text_escapes_executable_name + installer = setup_base_installer + + malicious = "evil');system('id');#" + @spec.bindir = "bin" + write_file @spec.bin_file(malicious) do |io| + io.puts "#!/usr/bin/ruby" + end + + wrapper = installer.app_script_text malicious + + assert_includes wrapper, %q{Gem.activate_and_load_bin_path('a', 'evil\');system(\'id\');#', version)} + assert_includes wrapper, %q{load Gem.activate_bin_path('a', 'evil\');system(\'id\');#', version)} + end + def test_check_executable_overwrite installer = setup_base_installer @@ -1481,6 +1496,39 @@ class TestGemInstaller < Gem::InstallerTestCase refute_match(/I am a shiny gem!/, @ui.output) end + def test_install_sanitizes_post_install_message + # Use for_spec so the in-memory message reaches the installer verbatim; + # building a gem would escape the control characters during serialization. + @spec = setup_base_spec + @spec.post_install_message = "shiny \e]2;pwn\a gem" + + installer = Gem::Installer.for_spec @spec, post_install_message: true + installer.gem_home = @gemhome + + use_ui @ui do + installer.install + end + + assert_match(/shiny \.\]2;pwn\. gem/, @ui.output) + refute_match(/\e\]2;pwn/, @ui.output) + end + + def test_install_handles_non_string_post_install_message + # post_install_message may be a non-String (the gemspec schema allows an + # array), so sanitizing must not assume it responds to gsub. + @spec = setup_base_spec + @spec.post_install_message = %w[one two] + + installer = Gem::Installer.for_spec @spec, post_install_message: true + installer.gem_home = @gemhome + + use_ui @ui do + installer.install + end + + assert_match(/one/, @ui.output) + end + def test_install_extension_dir gemhome2 = "#{@gemhome}2" @@ -1921,6 +1969,82 @@ class TestGemInstaller < Gem::InstallerTestCase end end + def test_pre_install_checks_malicious_executables_before_eval + spec = util_spec "malicious", "1" + def spec.full_name # so the spec is buildable + "malicious-1" + end + + def spec.validate(*args); end + spec.executables = ["../../../tmp/malicious"] + + util_build_gem spec + + gem = File.join(@gemhome, "cache", spec.file_name) + + use_ui @ui do + installer = Gem::Installer.at gem + e = assert_raise Gem::InstallError do + installer.pre_install_checks + end + assert_equal "#<Gem::Specification name=malicious version=1> has an invalid executable", e.message + end + end + + def test_pre_install_checks_malicious_bindir_before_eval + spec = util_spec "malicious", "1" + def spec.full_name # so the spec is buildable + "malicious-1" + end + + def spec.validate(*args); end + spec.bindir = "../../../tmp/malicious" + + util_build_gem spec + + gem = File.join(@gemhome, "cache", spec.file_name) + + use_ui @ui do + installer = Gem::Installer.at gem + e = assert_raise Gem::InstallError do + installer.pre_install_checks + end + assert_equal "#<Gem::Specification name=malicious version=1> has an invalid bindir", e.message + end + end + + def test_pre_install_checks_non_string_executable + spec = util_spec "malicious", "1" + def spec.validate(*args); end + spec.executables = [nil] + + installer = Gem::Installer.for_spec spec + installer.gem_home = @gemhome + + use_ui @ui do + e = assert_raise Gem::InstallError do + installer.pre_install_checks + end + assert_equal "#<Gem::Specification name=malicious version=1> has an invalid executable", e.message + end + end + + def test_pre_install_checks_non_string_bindir + spec = util_spec "malicious", "1" + def spec.validate(*args); end + spec.bindir = true + + installer = Gem::Installer.for_spec spec + installer.gem_home = @gemhome + + use_ui @ui do + e = assert_raise Gem::InstallError do + installer.pre_install_checks + end + assert_equal "#<Gem::Specification name=malicious version=1> has an invalid bindir", e.message + end + end + def test_pre_install_checks_malicious_platform_before_eval gem_with_ill_formatted_platform = File.expand_path("packages/ill-formatted-platform-1.0.0.10.gem", __dir__) diff --git a/test/rubygems/test_gem_text.rb b/test/rubygems/test_gem_text.rb index 8e99610946..60739e6131 100644 --- a/test/rubygems/test_gem_text.rb +++ b/test/rubygems/test_gem_text.rb @@ -100,4 +100,21 @@ Without the wrapping, the text might not look good in the RSS feed. def test_clean_text assert_equal ".]2;nyan.", clean_text("\e]2;nyan\a") end + + def test_clean_text_strips_c1_control_characters + text = [0x41, 0x9b, 0x42].pack("U*") # "A", CSI (U+009B), "B" + assert_equal "A.B", clean_text(text) + end + + def test_clean_text_preserves_multibyte_characters + # U+0400 encodes to bytes D0 80, whose 0x80 continuation byte must not be + # mistaken for a C1 control byte. NEL (U+0085) is stripped. + text = [0x400, 0x85].pack("U*") + assert_equal [0x400, 0x2e].pack("U*"), clean_text(text) + end + + def test_clean_text_passes_through_non_unicode_encodings + text = "x\x9by".dup.force_encoding("ISO-8859-1") + assert_equal text, clean_text(text) + end end diff --git a/test/socket/test_ancdata.rb b/test/socket/test_ancdata.rb index b2f86a0bb1..387be6080f 100644 --- a/test/socket/test_ancdata.rb +++ b/test/socket/test_ancdata.rb @@ -65,4 +65,22 @@ class TestSocketAncData < Test::Unit::TestCase } end end + + if /freebsd/ =~ RUBY_PLATFORM + def test_cmsgcred_inspect + cred = [0, 0, 0, 0, 9999, *Array.new(16, 0)].pack('L4I!L*') + s = Socket::AncillaryData.new(:UNIX, :SOCKET, :SCM_CREDS, cred).inspect + assert_include(s, 'groups[9999]') + assert_include(s, '(cmsgcred)') + end + end + + if /netbsd|freebsd/ =~ RUBY_PLATFORM + def test_sockcred_inspect + cred = [0, 0, 0, 0, 9999, 0].pack('L4S!L') + s = Socket::AncillaryData.new(:UNIX, :SOCKET, :SCM_CREDS, cred).inspect + assert_include(s, 'groups[9999]') + assert_include(s, '(sockcred)') + end + end end if defined? Socket::AncillaryData diff --git a/thread_pthread.c b/thread_pthread.c index 6e3ce8ef87..5b5329fd21 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -1799,7 +1799,12 @@ ruby_mn_threads_params(void) main_ractor->threads.sched.enable_mn_threads = enable_mn_threads; const char *max_cpu_cstr = getenv("RUBY_MAX_CPU"); - const int default_max_cpu = 8; // TODO: CPU num? +#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) + long nprocessors = sysconf(_SC_NPROCESSORS_ONLN); + const int default_max_cpu = (nprocessors > 0) ? (int)nprocessors : 8; +#else + const int default_max_cpu = 8; +#endif int max_cpu = default_max_cpu; if (USE_MN_THREADS && max_cpu_cstr) { diff --git a/vcpkg.json b/vcpkg.json index 64d73c4048..c2caad14cd 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,5 +7,5 @@ "openssl", "zlib" ], - "builtin-baseline": "56bb2411609227288b70117ead2c47585ba07713" + "builtin-baseline": "f3e10653cc27d62a37a3763cd84b38bca07c6075" }
\ No newline at end of file diff --git a/vm_method.c b/vm_method.c index fb34426bf2..59906f9b16 100644 --- a/vm_method.c +++ b/vm_method.c @@ -522,7 +522,7 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) // not see these origins via RCLASS_ORIGIN(owner), so we find them by // iterating all of owner's classexts and checking their origin_ fields. { - VALUE origins = rb_ary_new(); + VALUE origins = rb_ary_hidden_new(1); struct collect_per_box_origins_arg origins_arg = { .owner = owner, .klass_housing_cme = klass_housing_cme, @@ -532,6 +532,7 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) for (long i = 0; i < RARRAY_LEN(origins); i++) { invalidate_callable_method_entry_in_every_m_table(RARRAY_AREF(origins, i), mid, cme); } + RB_GC_GUARD(origins); } } diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index a417df300a..91a1a3ffcf 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -4,12 +4,12 @@ use std::mem::take; use std::rc::Rc; use crate::bitset::BitSet; use crate::codegen::{local_size_and_idx_to_ep_offset, perf_symbol_range_start, perf_symbol_range_end}; -use crate::cruby::{IseqPtr, RUBY_OFFSET_CFP_ISEQ, RUBY_OFFSET_CFP_JIT_RETURN, RUBY_OFFSET_CFP_PC, RUBY_OFFSET_CFP_SP, SIZEOF_VALUE_I32, vm_stack_canary}; +use crate::cruby::{IseqPtr, RUBY_OFFSET_CFP_ISEQ, RUBY_OFFSET_CFP_JIT_RETURN, RUBY_OFFSET_CFP_PC, RUBY_OFFSET_CFP_SP, SIZEOF_VALUE_I32, vm_stack_canary, YarvInsnIdx }; use crate::hir::{Invariant, SideExitReason}; use crate::hir; use crate::options::{TraceExits, PerfMap, get_option}; use crate::cruby::VALUE; -use crate::payload::IseqVersionRef; +use crate::payload::{IseqVersionRef, get_or_create_iseq_payload}; use crate::stats::{exit_counter_ptr, exit_counter_ptr_for_opcode, side_exit_counter, CompileError}; use crate::virtualmem::CodePtr; use crate::asm::{CodeBlock, Label}; @@ -2402,7 +2402,8 @@ impl Assembler fn compile_exit_recompile(asm: &mut Assembler, exit: &SideExit) { if let Some(recompile) = &exit.recompile { - + let payload = get_or_create_iseq_payload(exit.iseq); + payload.reset_profiles_remaining(recompile.insn_idx as YarvInsnIdx); use crate::codegen::exit_recompile; asm_comment!(asm, "profile and maybe recompile"); asm_ccall!(asm, exit_recompile, diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 2d2ade8d7e..ee80ac0db5 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -699,9 +699,9 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio let val_type = function.type_of(*val); gen_has_type(jit, asm, opnd!(val), val_type, *expected) } - Insn::GuardType { val, guard_type, state } => { - let val_type = function.type_of(*val); - gen_guard_type(jit, asm, opnd!(val), val_type, *guard_type, &function.frame_state(*state)) + &Insn::GuardType { val, guard_type, state, recompile } => { + let val_type = function.type_of(val); + gen_guard_type(jit, asm, opnd!(val), val_type, guard_type, recompile, &function.frame_state(state)) } &Insn::GuardBitEquals { val, expected, reason, state, recompile } => gen_guard_bit_equals(jit, asm, opnd!(val), expected, reason, recompile, &function.frame_state(state)), &Insn::GuardAnyBitSet { val, mask, reason, state, .. } => gen_guard_any_bit_set(jit, asm, opnd!(val), mask, reason, &function.frame_state(state)), @@ -711,9 +711,11 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))), Insn::CCall { cfunc, recv, args, name, owner: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnd!(recv), opnds!(args)), // Give up CCallWithFrame for 7+ args since asm.ccall() supports at most 6 args (recv + args). - // There's no test case for this because no core cfuncs have this many parameters. But C extensions could have such methods. - Insn::CCallWithFrame { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => - gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::CCallWithFrameTooManyArgs), + // We're currently emitting a CCallWithFrame for `super` in to a cfunction. + // We can't lower to `gen_send_without_block` because the + // source opcode isn't necessarily `opt_send_without_block` + // and so the interpreter stack layout may be incompatible. + Insn::CCallWithFrame { cd, state, args, block, .. } if args.len() + 1 > C_ARG_OPNDS.len() => return Err(*state), Insn::CCallWithFrame { cfunc, recv, name, args, cme, state, block, .. } => gen_ccall_with_frame(jit, asm, *cfunc, *name, opnd!(recv), opnds!(args), *cme, *block, &function.frame_state(*state)), Insn::CCallVariadic { cfunc, recv, name, args, cme, state, block, return_type: _, elidable: _ } => { @@ -2523,33 +2525,33 @@ fn gen_has_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, val_typ } /// Compile a type check with a side exit -fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, val_type: Type, guard_type: Type, state: &FrameState) -> lir::Opnd { +fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, val_type: Type, guard_type: Type, recompile: Option<Recompile>, state: &FrameState) -> lir::Opnd { let is_known_heap_basic_object = val_type.is_subtype(types::HeapBasicObject); gen_incr_counter(asm, Counter::guard_type_count); if guard_type.is_subtype(types::Fixnum) { asm.test(val, Opnd::UImm(RUBY_FIXNUM_FLAG as u64)); - asm.jz(jit, side_exit(jit, state, GuardType(guard_type))); + asm.jz(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile)); } else if guard_type.is_subtype(types::Flonum) { // Flonum: (val & RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG let masked = asm.and(val, Opnd::UImm(RUBY_FLONUM_MASK as u64)); asm.cmp(masked, Opnd::UImm(RUBY_FLONUM_FLAG as u64)); - asm.jne(jit, side_exit(jit, state, GuardType(guard_type))); + asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile)); } else if guard_type.is_subtype(types::StaticSymbol) { // Static symbols have (val & 0xff) == RUBY_SYMBOL_FLAG // Use 8-bit comparison like YJIT does. // If `val` is a constant (rare but possible), put it in a register to allow masking. let val = asm.load_imm(val); asm.cmp(val.with_num_bits(8), Opnd::UImm(RUBY_SYMBOL_FLAG as u64)); - asm.jne(jit, side_exit(jit, state, GuardType(guard_type))); + asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile)); } else if guard_type.is_subtype(types::NilClass) { asm.cmp(val, Qnil.into()); - asm.jne(jit, side_exit(jit, state, GuardType(guard_type))); + asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile)); } else if guard_type.is_subtype(types::TrueClass) { asm.cmp(val, Qtrue.into()); - asm.jne(jit, side_exit(jit, state, GuardType(guard_type))); + asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile)); } else if guard_type.is_subtype(types::FalseClass) { asm.cmp(val, Qfalse.into()); - asm.jne(jit, side_exit(jit, state, GuardType(guard_type))); + asm.jne(jit, side_exit_with_recompile(jit, state, GuardType(guard_type), recompile)); } else if guard_type.is_immediate() { // All immediate types' guard should have been handled above panic!("unexpected immediate guard type: {guard_type}"); @@ -2560,7 +2562,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, val_t // TODO: Max thinks codegen should not care about the shapes of the operands except to create them. (Shopify/ruby#685) let val = asm.load_mem(val); - let side_exit = side_exit(jit, state, GuardType(guard_type)); + let side_exit = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile); if !is_known_heap_basic_object { // Check if it's a special constant asm.test(val, (RUBY_IMMEDIATE_MASK as u64).into()); @@ -2577,7 +2579,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, val_t asm.cmp(klass, Opnd::Value(expected_class)); asm.jne(jit, side_exit); } else if let Some(builtin_type) = guard_type.builtin_type_equivalent() { - let side = side_exit(jit, state, GuardType(guard_type)); + let side = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile); if !is_known_heap_basic_object { // Check special constant @@ -2596,7 +2598,7 @@ fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, val: lir::Opnd, val_t asm.cmp(tag, Opnd::UImm(builtin_type as u64)); asm.jne(jit, side); } else if guard_type.bit_equal(types::HeapBasicObject) { - let side_exit = side_exit(jit, state, GuardType(guard_type)); + let side_exit = side_exit_with_recompile(jit, state, GuardType(guard_type), recompile); asm.cmp(val, Opnd::Value(Qfalse)); asm.je(jit, side_exit.clone()); asm.test(val, (RUBY_IMMEDIATE_MASK as u64).into()); diff --git a/zjit/src/codegen_tests.rs b/zjit/src/codegen_tests.rs index 1ed4a289df..9b76690d5b 100644 --- a/zjit/src/codegen_tests.rs +++ b/zjit/src/codegen_tests.rs @@ -1209,6 +1209,52 @@ fn test_invokesuper_to_cfunc_varargs() { } #[test] +fn test_invokesuper_to_cfunc_with_too_many_args_exits() { + unsafe extern "C" fn test_six_args( + _self: VALUE, + a: VALUE, + b: VALUE, + c: VALUE, + d: VALUE, + e: VALUE, + f: VALUE, + ) -> VALUE { + unsafe { rb_ary_new_from_args(6, a, b, c, d, e, f) } + } + + with_rubyvm(|| { + let superclass = define_class("ZJITSixArgs", unsafe { rb_cObject }); + unsafe { + rb_define_method( + superclass, + c"six".as_ptr(), + Some(std::mem::transmute::< + unsafe extern "C" fn(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE) -> VALUE, + unsafe extern "C" fn(VALUE) -> VALUE, + >(test_six_args)), + 6, + ); + } + }); + + assert_snapshot!(assert_compiles_allowing_exits(r#" + class ZJITSixArgsSubclass < ZJITSixArgs + def six(a, b, c, d, e, f) + super + end + end + + def test + ZJITSixArgsSubclass.new.six(1, 2, 3, 4, 5, 6) + end + + test + test + test + "#), @"[1, 2, 3, 4, 5, 6]"); +} + +#[test] fn test_string_new_preserves_string_arg() { assert_snapshot!(inspect(r#" def test diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 4e4a246b8a..61de3709cb 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -91,7 +91,7 @@ use std::convert::From; use std::ffi::{c_void, CString, CStr}; use std::fmt::{Debug, Display, Formatter}; -use std::os::raw::{c_char, c_int, c_uint}; +use std::os::raw::{c_char, c_int, c_long, c_uint}; use std::panic::{catch_unwind, UnwindSafe}; use crate::cast::IntoUsize as _; @@ -132,6 +132,7 @@ unsafe extern "C" { pub fn rb_float_new(d: f64) -> VALUE; pub fn rb_hash_empty_p(hash: VALUE) -> VALUE; + pub fn rb_ary_new_from_args(n: c_long, ...) -> VALUE; pub fn rb_str_setbyte(str: VALUE, index: VALUE, value: VALUE) -> VALUE; pub fn rb_str_getbyte(str: VALUE, index: VALUE) -> VALUE; pub fn rb_vm_splat_array(flag: VALUE, ary: VALUE) -> VALUE; @@ -165,6 +166,12 @@ unsafe extern "C" { pub fn rb_vm_stack_canary() -> VALUE; pub fn rb_vm_push_cfunc_frame(cme: *const rb_callable_method_entry_t, recv_idx: c_int); pub fn rb_obj_class(klass: VALUE) -> VALUE; + pub fn rb_define_method( + klass: VALUE, + mid: *const c_char, + func: Option<unsafe extern "C" fn(args: VALUE) -> VALUE>, + arity: c_int, + ); pub fn rb_vm_objtostring(reg_cfp: CfpPtr, recv: VALUE, cd: *const rb_call_data) -> VALUE; } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 82e50cdd36..fa035292e4 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -1168,7 +1168,7 @@ pub enum Insn { HasType { val: InsnId, expected: Type }, /// Side-exit if val doesn't have the expected type. - GuardType { val: InsnId, guard_type: Type, state: InsnId }, + GuardType { val: InsnId, guard_type: Type, state: InsnId, recompile: Option<Recompile> }, /// Side-exit if val is not the expected Const. GuardBitEquals { val: InsnId, expected: Const, reason: SideExitReason, state: InsnId, recompile: Option<Recompile> }, /// Side-exit if (val & mask) == 0 @@ -2125,7 +2125,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::IntOr { left, right } => { write!(f, "IntOr {left}, {right}") }, Insn::FixnumLShift { left, right, .. } => { write!(f, "FixnumLShift {left}, {right}") }, Insn::FixnumRShift { left, right, .. } => { write!(f, "FixnumRShift {left}, {right}") }, - Insn::GuardType { val, guard_type, .. } => { write!(f, "GuardType {val}, {}", guard_type.print(self.ptr_map)) }, + Insn::GuardType { val, guard_type, recompile, .. } => { + write!(f, "GuardType {val}, {}", guard_type.print(self.ptr_map))?; + if recompile.is_some() { + write!(f, " recompile")?; + } + return Ok(()) + }, Insn::RefineType { val, new_type, .. } => { write!(f, "RefineType {val}, {}", new_type.print(self.ptr_map)) }, Insn::HasType { val, expected, .. } => { write!(f, "HasType {val}, {}", expected.print(self.ptr_map)) }, Insn::GuardBitEquals { val, expected, recompile, .. } => { @@ -3468,7 +3474,7 @@ impl Function { pub fn coerce_to(&mut self, block: BlockId, val: InsnId, guard_type: Type, state: InsnId) -> InsnId { if self.is_a(val, guard_type) { return val; } - self.push_insn(block, Insn::GuardType { val, guard_type, state }) + self.push_insn(block, Insn::GuardType { val, guard_type, state, recompile: None }) } fn count_complex_call_features(&mut self, block: BlockId, ci_flags: c_uint) { @@ -3711,7 +3717,8 @@ impl Function { // Add GuardType for profiled receiver if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(ci) } as i32; + recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend { argc }) }); } let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, block: send_block }); @@ -3754,7 +3761,8 @@ impl Function { self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(ci) } as i32; + recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend{ argc }) }); } let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, block: None }); @@ -3774,7 +3782,8 @@ impl Function { self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(ci) } as i32; + recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend{ argc }) }); } let id = unsafe { get_cme_def_body_attr_id(cme) }; @@ -3790,7 +3799,8 @@ impl Function { self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(ci) } as i32; + recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend{ argc }) }); } let id = unsafe { get_cme_def_body_attr_id(cme) }; @@ -3812,7 +3822,8 @@ impl Function { } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(ci) } as i32; + recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend{ argc }) }); } let kw_splat = flags & VM_CALL_KW_SPLAT != 0; let invoke_proc = self.push_insn(block, Insn::InvokeProc { recv, args: args.clone(), state, kw_splat }); @@ -3850,7 +3861,8 @@ impl Function { } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(ci) } as i32; + recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend{ argc }) }); } // All structs from the same Struct class should have the same // length. So if our recv is embedded all runtime @@ -3921,12 +3933,12 @@ impl Function { if recv_type.is_string() { self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_type.class() }, state }); - let guard = self.push_insn(block, Insn::GuardType { val, guard_type: types::String, state }); + let guard = self.push_insn(block, Insn::GuardType { val, guard_type: types::String, state, recompile: None }); // Infer type so AnyToString can fold off this self.insn_types[guard.0] = self.infer_type(guard); self.make_equal_to(insn_id, guard); } else { - let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state}); + let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state, recompile: None }); let send_to_s = self.push_insn(block, Insn::Send { recv, cd, block: None, args: vec![], state, reason: ObjToStringNotString }); self.make_equal_to(insn_id, send_to_s); } @@ -4373,16 +4385,16 @@ impl Function { fn load_ivar_guard_type(&mut self, block: BlockId, recv: InsnId, recv_type: ProfiledType, state: InsnId) -> InsnId { if recv_type.flags().is_t_class() { // Check class first since `Class < Module` - self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Class, state }) + self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Class, state, recompile: None }) } else if recv_type.flags().is_t_module() { - self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Module, state }) + self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::Module, state, recompile: None }) } else if recv_type.flags().is_t_data() { - self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TData, state }) + self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::TData, state, recompile: None }) } else { // HeapBasicObject is wider than T_OBJECT, but shapes for T_OBJECTs are in a pool of // its own and are guaranteed to be different from shapes of any other T_* types. So // the shape check that follows already covers checking for T_OBJECT. - self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::HeapBasicObject, state }) + self.push_insn(block, Insn::GuardType { val: recv, guard_type: types::HeapBasicObject, state, recompile: None }) } } @@ -4769,7 +4781,8 @@ impl Function { if let Some(profiled_type) = profiled_type { // Guard receiver class - recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(call_info) } as i32; + recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend { argc }) }); fun.insn_types[recv.0] = fun.infer_type(recv); } @@ -4835,7 +4848,8 @@ impl Function { if let Some(profiled_type) = profiled_type { // Guard receiver class - recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); + let argc = unsafe { vm_ci_argc(call_info) } as i32; + recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state, recompile: Some(Recompile::ProfileSend { argc }) }); fun.insn_types[recv.0] = fun.infer_type(recv); } @@ -4987,14 +5001,28 @@ impl Function { compile_time_heap.insert(key, val); insn_id }, - Insn::LoadField { recv, offset, .. } => { + Insn::LoadField { recv, offset, return_type, .. } => { let key = (self.chase_insn(recv), offset); match compile_time_heap.entry(key) { std::collections::hash_map::Entry::Occupied(entry) => { - // If the value is stored already, we should short circuit. - // However, we need to replace insn_id with its representative in the SSA union. - self.make_equal_to(insn_id, *entry.get()); - continue + let cached_insn = *entry.get(); + + // TODO (nirvdrum 2026-06-04): Remove the return type guard and supporting code when the type checker becomes more accurate. + // If there's an an embedded<=>heap shape storage transition, it's possible for this `LoadField` to have a different return + // type than the cached entry (`CPtr` vs `BasicObject`). While the loaded value would be the same in either case, the + // difference in associated type causes type checking to fail. Consequently, we conservatively retain the duplicate `LoadField`. + // The `optimize_load_store_does_not_alias_loads_with_incompatible_return_types` test checks the problematic case. + let can_forward_cached_insn = match self.find(cached_insn) { + Insn::LoadField { return_type : cached_return_type,.. } => cached_return_type.is_subtype(return_type), + _ => true + }; + + if can_forward_cached_insn { + // If the value is stored already, we should short circuit. + // However, we need to replace insn_id with its representative in the SSA union. + self.make_equal_to(insn_id, cached_insn); + continue + } } std::collections::hash_map::Entry::Vacant(_) => { // If the value has not been accessed, cache a copy to optimize future loads or stores. @@ -7079,9 +7107,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { } let ty = Type::from_profiled_type(summary.bucket(0)); let obj = if ty.is_subtype(types::NilClass) { - fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::NilClass, state: exit_id }) + fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::NilClass, state: exit_id, recompile: None }) } else if ty.is_subtype(types::HashExact) { - fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::HashExact, state: exit_id }) + fun.push_insn(block, Insn::GuardType { val: hash, guard_type: types::HashExact, state: exit_id, recompile: None }) } else { fun.push_insn(block, Insn::SideExit { state: exit_id, reason: SideExitReason::SplatKwNotNilOrHash, recompile: None }); break; // End the block @@ -7147,7 +7175,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let id = ID(get_arg(pc, 0).as_u64()); let pushval = get_arg(pc, 2); if let Some(summary) = fun.polymorphic_summary(&profiles, self_param, exit_state.insn_idx) { - self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id }); + self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id, recompile: None }); let rbasic_flags = fun.load_rbasic_flags(block, self_param); let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx)); let join_param = fun.push_insn(join_block, Insn::Param); @@ -8318,7 +8346,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { break; // End the block } if let Some(summary) = fun.polymorphic_summary(&profiles, self_param, exit_state.insn_idx) { - self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id }); + self_param = fun.push_insn(block, Insn::GuardType { val: self_param, guard_type: types::HeapBasicObject, state: exit_id, recompile: None }); let rbasic_flags = fun.load_rbasic_flags(block, self_param); let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx)); let join_param = fun.push_insn(join_block, Insn::Param); @@ -8526,7 +8554,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { break; // End the block } let val = state.stack_pop()?; - let array = fun.push_insn(block, Insn::GuardType { val, guard_type: types::ArrayExact, state: exit_id, }); + let array = fun.push_insn(block, Insn::GuardType { val, guard_type: types::ArrayExact, state: exit_id, recompile: None }); let length = fun.push_insn(block, Insn::ArrayLength { array }); let expected = fun.push_insn(block, Insn::Const { val: Const::CInt64(num as i64) }); fun.push_insn(block, Insn::GuardGreaterEq { left: length, right: expected, reason: SideExitReason::ExpandArray, state: exit_id }); @@ -9307,6 +9335,81 @@ mod validation_tests { function.seal_entries(); assert_matches_err(function.validate(), ValidationError::DuplicateInstruction(exit, val)); } + + // The heap-fields pointer (`as_heap`, a CPtr) and the first embedded + // instance variable both live at ROBJECT_OFFSET_AS_HEAP_FIELDS == + // ROBJECT_OFFSET_AS_ARY == 0x10 on a Ruby object. They are distinct fields + // with incompatible value types that happen to share a base and an offset. + // Since we could end up with two `LoadField` on different shape types + // (e.g., as the result of inlining), `optimize_load_store` must not satisfy + // one load from another cached load with a different return type. The fault + // surfaces here as the forwarded value flowing into a `Return` with the + // wrong type (`CPtr` rather than `BasicObject`). + #[test] + fn optimize_load_store_does_not_alias_loads_with_incompatible_return_types() { + assert_eq!(ROBJECT_OFFSET_AS_HEAP_FIELDS, ROBJECT_OFFSET_AS_ARY, + "Conflicting field offsets changed, rendering the rest of this test incorrect"); + + let mut function = Function::new(std::ptr::null()); + let entry = function.entry_block; + let recv = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) }); + function.push_insn(entry, Insn::LoadField { + recv, + id: FieldName::as_heap, + offset: ROBJECT_OFFSET_AS_HEAP_FIELDS as i32, + return_type: types::CPtr, + }); + let ivar = function.push_insn(entry, Insn::LoadField { + recv, + id: FieldName::Id(ID(1)), + offset: ROBJECT_OFFSET_AS_ARY as i32, + return_type: types::BasicObject, + }); + function.push_insn(entry, Insn::Return { val: ivar }); + function.seal_entries(); + + function.infer_types(); + function.optimize_load_store(); + + assert!( + function.validate().is_ok(), + "optimize_load_store aliased two loads with different return types: {:?}", + function.validate(), + ); + } + + #[test] + fn optimize_load_store_does_not_alias_loads_with_compatible_return_types() { + assert_eq!(ROBJECT_OFFSET_AS_HEAP_FIELDS, ROBJECT_OFFSET_AS_ARY, + "Conflicting field offsets changed, rendering the rest of this test incorrect"); + + let mut function = Function::new(std::ptr::null()); + let entry = function.entry_block; + let recv = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) }); + function.push_insn(entry, Insn::LoadField { + recv, + id: FieldName::as_heap, + offset: ROBJECT_OFFSET_AS_HEAP_FIELDS as i32, + return_type: types::BasicObject, + }); + let ivar = function.push_insn(entry, Insn::LoadField { + recv, + id: FieldName::Id(ID(1)), + offset: ROBJECT_OFFSET_AS_ARY as i32, + return_type: types::Array, + }); + function.push_insn(entry, Insn::Return { val: ivar }); + function.seal_entries(); + + function.infer_types(); + function.optimize_load_store(); + + assert!( + function.validate().is_ok(), + "optimize_load_store failed to alias two loads with different, but compatible, return types: {:?}", + function.validate(), + ); + } } #[cfg(test)] diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 74b951d2e5..9a2ff9f03b 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -225,7 +225,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[0] = Const Value(0) PatchPoint MethodRedefined(Integer@0x1008, -@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile CheckInterrupts Return v26 "); @@ -455,7 +455,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, /@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile CheckInterrupts Return v26 "); @@ -1132,7 +1132,7 @@ mod hir_opt_tests { v15:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v27:ArrayExact = GuardType v10, ArrayExact + v27:ArrayExact = GuardType v10, ArrayExact recompile v35:CInt64[0] = Const CInt64(0) v29:CInt64 = ArrayLength v27 v30:CInt64[0] = GuardLess v35, v29 @@ -1165,7 +1165,7 @@ mod hir_opt_tests { v15:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v27:ArrayExact = GuardType v10, ArrayExact + v27:ArrayExact = GuardType v10, ArrayExact recompile v35:CInt64[0] = Const CInt64(0) v29:CInt64 = ArrayLength v27 v30:CInt64[0] = GuardLess v35, v29 @@ -1248,7 +1248,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(CustomEq@0x1008) PatchPoint MethodRedefined(CustomEq@0x1008, !=@0x1010, cme:0x1018) - v30:ObjectSubclass[class_exact:CustomEq] = GuardType v10, ObjectSubclass[class_exact:CustomEq] + v30:ObjectSubclass[class_exact:CustomEq] = GuardType v10, ObjectSubclass[class_exact:CustomEq] recompile v31:BoolExact = CCallWithFrame v30, :BasicObject#!=@0x1040, v30 v21:NilClass = Const Value(nil) CheckInterrupts @@ -1280,7 +1280,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile v27:Fixnum = FixnumAdd v26, v15 CheckInterrupts Return v27 @@ -1406,7 +1406,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v19:BasicObject = SendDirect v18, 0x1038, :foo (0x1048) CheckInterrupts Return v19 @@ -1434,7 +1434,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:Fixnum[1] = Const Value(1) CheckInterrupts Return v20 @@ -1461,7 +1461,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile CheckInterrupts Return v19 "); @@ -1498,7 +1498,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:BasicObject = SendDirect v20, 0x1038, :foo (0x1048), v11 CheckInterrupts Return v21 @@ -1530,7 +1530,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, fun_new_map@0x1010, cme:0x1018) - v27:ArraySubclass[class_exact:C] = GuardType v10, ArraySubclass[class_exact:C] + v27:ArraySubclass[class_exact:C] = GuardType v10, ArraySubclass[class_exact:C] recompile v28:BasicObject = SendDirect v27, 0x1040, :fun_new_map (0x1050) PatchPoint NoEPEscape(test) v18:CPtr = LoadSP @@ -1567,7 +1567,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, bar@0x1010, cme:0x1018) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v29:BasicObject = CCallWithFrame v28, :Enumerable#bar@0x1040, block=0x1048 PatchPoint NoEPEscape(test) v18:CPtr = LoadSP @@ -1604,7 +1604,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, length@0x1010, cme:0x1018) - v24:ArrayExact = GuardType v10, ArrayExact + v24:ArrayExact = GuardType v10, ArrayExact recompile v25:CInt64 = ArrayLength v24 v26:Fixnum = BoxFixnum v25 CheckInterrupts @@ -1662,7 +1662,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v19:BasicObject = SendDirect v18, 0x1038, :foo (0x1048) CheckInterrupts Return v19 @@ -1690,7 +1690,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:BasicObject = SendDirect v20, 0x1038, :Integer (0x1048), v11 CheckInterrupts Return v21 @@ -1720,7 +1720,7 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048), v11, v13 CheckInterrupts Return v23 @@ -1750,7 +1750,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v23:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v23:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048) PatchPoint MethodRedefined(Object@0x1000, bar@0x1050, cme:0x1058) v27:BasicObject = SendDirect v23, 0x1038, :bar (0x1048) @@ -1778,7 +1778,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v19:BasicObject = SendDirect v18, 0x1038, :foo (0x1048) CheckInterrupts Return v19 @@ -1805,7 +1805,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:BasicObject = SendDirect v20, 0x1038, :foo (0x1048), v11 CheckInterrupts Return v21 @@ -1833,7 +1833,7 @@ mod hir_opt_tests { v11:Fixnum[3] = Const Value(3) v13:Fixnum[4] = Const Value(4) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048), v11, v13 CheckInterrupts Return v23 @@ -1860,7 +1860,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010) - v44:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v44:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v45:BasicObject = SendDirect v44, 0x1038, :target (0x1048) v14:Fixnum[10] = Const Value(10) v16:Fixnum[20] = Const Value(20) @@ -1901,7 +1901,7 @@ mod hir_opt_tests { v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v11 PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1008)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1008)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1008)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1008)] recompile v23:BasicObject = CCallVariadic v22, :Kernel#puts@0x1040, v12 CheckInterrupts Return v23 @@ -1936,7 +1936,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v27:Fixnum = GuardType v12, Fixnum + v27:Fixnum = GuardType v12, Fixnum recompile v29:Fixnum[100] = Const Value(100) CheckInterrupts Return v29 @@ -1966,7 +1966,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v28:Fixnum = GuardType v12, Fixnum + v28:Fixnum = GuardType v12, Fixnum recompile v29:Fixnum = GuardType v13, Fixnum v30:Fixnum = FixnumAdd v28, v29 CheckInterrupts @@ -1996,7 +1996,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile v27:Fixnum = FixnumAdd v26, v15 CheckInterrupts Return v27 @@ -2055,7 +2055,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, []@0x1010, cme:0x1018) - v28:Fixnum = GuardType v12, Fixnum + v28:Fixnum = GuardType v12, Fixnum recompile v29:Fixnum = GuardType v13, Fixnum v30:Fixnum = FixnumAref v28, v29 CheckInterrupts @@ -2166,7 +2166,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, <@0x1010, cme:0x1018) - v28:Fixnum = GuardType v12, Fixnum + v28:Fixnum = GuardType v12, Fixnum recompile v29:Fixnum = GuardType v13, Fixnum v30:BoolExact = FixnumLt v28, v29 CheckInterrupts @@ -2196,7 +2196,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, <@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile v27:BoolExact = FixnumLt v26, v15 CheckInterrupts Return v27 @@ -2484,7 +2484,7 @@ mod hir_opt_tests { v15:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v27:ArrayExact = GuardType v10, ArrayExact + v27:ArrayExact = GuardType v10, ArrayExact recompile v35:CInt64[0] = Const CInt64(0) v29:CInt64 = ArrayLength v27 v30:CInt64[0] = GuardLess v35, v29 @@ -2519,7 +2519,7 @@ mod hir_opt_tests { v15:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) - v27:HashExact = GuardType v10, HashExact + v27:HashExact = GuardType v10, HashExact recompile v28:BasicObject = HashAref v27, v15 CheckInterrupts Return v28 @@ -2830,7 +2830,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2864,7 +2864,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, -@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2898,7 +2898,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, *@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2932,7 +2932,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, /@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v34:Integer = FixnumDiv v32, v33 v24:Fixnum[5] = Const Value(5) @@ -2967,7 +2967,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, %@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v34:Fixnum = FixnumMod v32, v33 v24:Fixnum[5] = Const Value(5) @@ -3002,7 +3002,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, <@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -3036,7 +3036,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, <=@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -3070,7 +3070,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, >@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -3104,7 +3104,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, >=@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -3138,7 +3138,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, ==@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile v33:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) CheckInterrupts @@ -3172,7 +3172,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, !=@0x1010, cme:0x1018) - v32:Fixnum = GuardType v12, Fixnum + v32:Fixnum = GuardType v12, Fixnum recompile PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) v34:Fixnum = GuardType v13, Fixnum v24:Fixnum[5] = Const Value(5) @@ -3259,7 +3259,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, itself@0x1010, cme:0x1018) - v23:Fixnum = GuardType v10, Fixnum + v23:Fixnum = GuardType v10, Fixnum recompile CheckInterrupts Return v23 "); @@ -3570,7 +3570,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:CPtr = GetEP 0 v21:BoolExact = IsBlockGiven v20 CheckInterrupts @@ -3596,7 +3596,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:FalseClass = Const Value(false) CheckInterrupts Return v20 @@ -3624,7 +3624,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) - v23:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v23:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v15:Fixnum[5] = Const Value(5) CheckInterrupts Return v15 @@ -3751,7 +3751,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v24:BasicObject = SendDirect v23, 0x1040, :foo (0x1050) CheckInterrupts Return v24 @@ -3781,7 +3781,7 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048), v11, v13 CheckInterrupts Return v23 @@ -3815,7 +3815,7 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:NilClass): v13:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v34:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v8, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v34:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v8, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v36:Fixnum[1] = Const Value(1) PatchPoint NoEPEscape(test) v21:CPtr = LoadSP @@ -3854,7 +3854,7 @@ mod hir_opt_tests { v13:Fixnum[1] = Const Value(1) SetLocal :a, l0, EP@3, v13 PatchPoint MethodRedefined(Object@0x1000, lambda@0x1008, cme:0x1010) - v45:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v8, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v45:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v8, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v46:BasicObject = CCallWithFrame v45, :Kernel#lambda@0x1038, block=0x1040 v20:CPtr = GetEP 0 v21:BasicObject = LoadField v20, :a@0x1048 @@ -3920,7 +3920,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[10] = Const Value(10) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:BasicObject = SendDirect v20, 0x1038, :foo (0x1048), v11 CheckInterrupts Return v21 @@ -3950,7 +3950,7 @@ mod hir_opt_tests { v11:Fixnum[10] = Const Value(10) v13:Fixnum[20] = Const Value(20) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048), v11, v13 CheckInterrupts Return v23 @@ -3979,7 +3979,7 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048), v11, v13 CheckInterrupts Return v23 @@ -4009,7 +4009,7 @@ mod hir_opt_tests { v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v25:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v25:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v26:BasicObject = SendDirect v25, 0x1038, :foo (0x1048), v13, v15, v11 CheckInterrupts Return v26 @@ -4039,7 +4039,7 @@ mod hir_opt_tests { v13:Fixnum[2] = Const Value(2) v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v25:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v25:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v26:BasicObject = SendDirect v25, 0x1038, :foo (0x1048), v11, v15, v13 CheckInterrupts Return v26 @@ -4068,7 +4068,7 @@ mod hir_opt_tests { v11:Fixnum[0] = Const Value(0) v13:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048), v11, v13 CheckInterrupts Return v23 @@ -4098,7 +4098,7 @@ mod hir_opt_tests { v13:Fixnum[3] = Const Value(3) v15:Fixnum[4] = Const Value(4) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v37:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v37:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v38:BasicObject = SendDirect v37, 0x1038, :foo (0x1048), v11, v13, v15 v20:Fixnum[1] = Const Value(1) v22:Fixnum[2] = Const Value(2) @@ -4135,7 +4135,7 @@ mod hir_opt_tests { v13:Fixnum[3] = Const Value(3) v34:Fixnum[4] = Const Value(4) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v37:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v37:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v38:BasicObject = SendDirect v37, 0x1038, :foo (0x1048), v11, v13, v34 v18:Fixnum[1] = Const Value(1) v20:Fixnum[2] = Const Value(2) @@ -4170,7 +4170,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[6] = Const Value(6) PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010) - v48:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v48:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v49:BasicObject = SendDirect v48, 0x1038, :target (0x1048), v11 v16:Fixnum[10] = Const Value(10) v18:Fixnum[20] = Const Value(20) @@ -4212,7 +4212,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:BasicObject = SendDirect v20, 0x1038, :foo (0x1048), v11 CheckInterrupts Return v21 @@ -4327,7 +4327,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v17:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:BasicObject = SendDirect v20, 0x1038, :foo (0x1048), v17 CheckInterrupts Return v21 @@ -4786,7 +4786,7 @@ mod hir_opt_tests { v17:HeapBasicObject = ObjectAlloc v43 PatchPoint NoSingletonClass(Set@0x1008) PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) - v49:SetExact = GuardType v17, SetExact + v49:SetExact = GuardType v17, SetExact recompile v50:BasicObject = CCallVariadic v49, :Set#initialize@0x1068 CheckInterrupts Return v49 @@ -5467,7 +5467,7 @@ mod hir_opt_tests { v15:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Proc@0x1008) PatchPoint MethodRedefined(Proc@0x1008, call@0x1010, cme:0x1018) - v25:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] + v25:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] recompile v26:BasicObject = InvokeProc v25, v15 CheckInterrupts Return v26 @@ -5500,7 +5500,7 @@ mod hir_opt_tests { v15:Fixnum[2] = Const Value(2) PatchPoint NoSingletonClass(Proc@0x1008) PatchPoint MethodRedefined(Proc@0x1008, []@0x1010, cme:0x1018) - v26:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] + v26:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] recompile v27:BasicObject = InvokeProc v26, v15 CheckInterrupts Return v27 @@ -5533,7 +5533,7 @@ mod hir_opt_tests { v15:Fixnum[3] = Const Value(3) PatchPoint NoSingletonClass(Proc@0x1008) PatchPoint MethodRedefined(Proc@0x1008, yield@0x1010, cme:0x1018) - v25:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] + v25:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] recompile v26:BasicObject = InvokeProc v25, v15 CheckInterrupts Return v26 @@ -5566,7 +5566,7 @@ mod hir_opt_tests { v15:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Proc@0x1008) PatchPoint MethodRedefined(Proc@0x1008, ===@0x1010, cme:0x1018) - v25:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] + v25:ObjectSubclass[class_exact:Proc] = GuardType v10, ObjectSubclass[class_exact:Proc] recompile v26:BasicObject = InvokeProc v25, v15 CheckInterrupts Return v26 @@ -7115,7 +7115,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v28:StaticSymbol[:b] = Const Value(VALUE(0x1038)) PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) CheckInterrupts @@ -7328,7 +7328,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(NilClass@0x1008, nil?@0x1010, cme:0x1018) - v24:NilClass = GuardType v10, NilClass + v24:NilClass = GuardType v10, NilClass recompile v25:TrueClass = Const Value(true) CheckInterrupts Return v25 @@ -7357,7 +7357,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(FalseClass@0x1008, nil?@0x1010, cme:0x1018) - v24:FalseClass = GuardType v10, FalseClass + v24:FalseClass = GuardType v10, FalseClass recompile v25:FalseClass = Const Value(false) CheckInterrupts Return v25 @@ -7386,7 +7386,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(TrueClass@0x1008, nil?@0x1010, cme:0x1018) - v24:TrueClass = GuardType v10, TrueClass + v24:TrueClass = GuardType v10, TrueClass recompile v25:FalseClass = Const Value(false) CheckInterrupts Return v25 @@ -7415,7 +7415,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Symbol@0x1008, nil?@0x1010, cme:0x1018) - v24:StaticSymbol = GuardType v10, StaticSymbol + v24:StaticSymbol = GuardType v10, StaticSymbol recompile v25:FalseClass = Const Value(false) CheckInterrupts Return v25 @@ -7444,7 +7444,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, nil?@0x1010, cme:0x1018) - v24:Fixnum = GuardType v10, Fixnum + v24:Fixnum = GuardType v10, Fixnum recompile v25:FalseClass = Const Value(false) CheckInterrupts Return v25 @@ -7473,7 +7473,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, nil?@0x1010, cme:0x1018) - v24:Flonum = GuardType v10, Flonum + v24:Flonum = GuardType v10, Flonum recompile v25:FalseClass = Const Value(false) CheckInterrupts Return v25 @@ -7503,7 +7503,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, nil?@0x1010, cme:0x1018) - v25:StringExact = GuardType v10, StringExact + v25:StringExact = GuardType v10, StringExact recompile v26:FalseClass = Const Value(false) CheckInterrupts Return v26 @@ -7533,7 +7533,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, !@0x1010, cme:0x1018) - v25:ArrayExact = GuardType v10, ArrayExact + v25:ArrayExact = GuardType v10, ArrayExact recompile v26:FalseClass = Const Value(false) CheckInterrupts Return v26 @@ -7562,7 +7562,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(FalseClass@0x1008, !@0x1010, cme:0x1018) - v24:FalseClass = GuardType v10, FalseClass + v24:FalseClass = GuardType v10, FalseClass recompile v25:TrueClass = Const Value(true) CheckInterrupts Return v25 @@ -7591,7 +7591,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(NilClass@0x1008, !@0x1010, cme:0x1018) - v24:NilClass = GuardType v10, NilClass + v24:NilClass = GuardType v10, NilClass recompile v25:TrueClass = Const Value(true) CheckInterrupts Return v25 @@ -7635,11 +7635,25 @@ mod hir_opt_tests { v29:NilClass = Const Value(nil) Jump bb5(v25, v26, v29) bb5(v31:BasicObject, v32:BasicObject, v33:Falsy): - PatchPoint MethodRedefined(NilClass@0x1008, !@0x1010, cme:0x1018) - v45:NilClass = GuardType v33, NilClass - v46:TrueClass = Const Value(true) + v37:CBool = HasType v33, FalseClass + CondBranch v37, bb8(v31, v32, v33), bb9() + bb8(v38:BasicObject, v39:BasicObject, v40:Falsy): + PatchPoint MethodRedefined(FalseClass@0x1008, !@0x1010, cme:0x1018) + v68:TrueClass = Const Value(true) + Jump bb7(v38, v39, v68) + bb9(): + v46:CBool = HasType v33, NilClass + CondBranch v46, bb10(v31, v32, v33), bb11() + bb10(v47:BasicObject, v48:BasicObject, v49:Falsy): + PatchPoint MethodRedefined(NilClass@0x1040, !@0x1010, cme:0x1018) + v71:TrueClass = Const Value(true) + Jump bb7(v47, v48, v71) + bb11(): + v55:BasicObject = Send v33, :! # SendFallbackReason: SendWithoutBlock: polymorphic fallback + Jump bb7(v31, v32, v55) + bb7(v57:BasicObject, v58:BasicObject, v59:BasicObject): CheckInterrupts - Return v46 + Return v59 "); } @@ -7666,7 +7680,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, empty?@0x1010, cme:0x1018) - v25:ArrayExact = GuardType v10, ArrayExact + v25:ArrayExact = GuardType v10, ArrayExact recompile v26:CInt64 = ArrayLength v25 v27:CInt64[0] = Const CInt64(0) v28:CBool = IsBitEqual v26, v27 @@ -7699,7 +7713,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, empty?@0x1010, cme:0x1018) - v25:HashExact = GuardType v10, HashExact + v25:HashExact = GuardType v10, HashExact recompile v26:BoolExact = CCall v25, :Hash#empty?@0x1040 CheckInterrupts Return v26 @@ -7732,7 +7746,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, ==@0x1010, cme:0x1018) - v29:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] + v29:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] recompile v30:CBool = IsBitEqual v29, v13 v31:BoolExact = BoxBool v30 CheckInterrupts @@ -7764,7 +7778,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, &@0x1010, cme:0x1018) - v28:Fixnum = GuardType v12, Fixnum + v28:Fixnum = GuardType v12, Fixnum recompile v29:Fixnum = GuardType v13, Fixnum v30:Fixnum = FixnumAnd v28, v29 CheckInterrupts @@ -7796,7 +7810,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, |@0x1010, cme:0x1018) - v28:Fixnum = GuardType v12, Fixnum + v28:Fixnum = GuardType v12, Fixnum recompile v29:Fixnum = GuardType v13, Fixnum v30:Fixnum = FixnumOr v28, v29 CheckInterrupts @@ -7825,7 +7839,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:NilClass = Const Value(nil) CheckInterrupts Return v20 @@ -7863,7 +7877,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v26:CShape = LoadField v23, :shape_id@0x1040 v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:BasicObject = LoadField v23, :@foo@0x1042 @@ -7906,7 +7920,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v24:BasicObject = GetIvar v23, :@foo CheckInterrupts Return v24 @@ -8379,7 +8393,7 @@ mod hir_opt_tests { bb3(v9:HeapBasicObject, v10:BasicObject): v17:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(C@0x1008, foo=@0x1010, cme:0x1018) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v31:CShape = LoadField v28, :shape_id@0x1040 v32:CShape[0x1041] = GuardBitEquals v31, CShape(0x1041) StoreField v28, :@foo@0x1042, v17 @@ -8746,7 +8760,7 @@ mod hir_opt_tests { bb4(v13:BasicObject): v34:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v45:Fixnum = GuardType v13, Fixnum + v45:Fixnum = GuardType v13, Fixnum recompile v46:Fixnum = FixnumAdd v45, v34 CheckInterrupts Return v46 @@ -8828,7 +8842,7 @@ mod hir_opt_tests { bb4(v13:BasicObject): v34:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v50:Fixnum = GuardType v13, Fixnum + v50:Fixnum = GuardType v13, Fixnum recompile v51:Fixnum = FixnumAdd v50, v34 CheckInterrupts Return v51 @@ -8898,7 +8912,7 @@ mod hir_opt_tests { bb4(v13:BasicObject): v33:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v44:Fixnum = GuardType v13, Fixnum + v44:Fixnum = GuardType v13, Fixnum recompile v45:Fixnum = FixnumAdd v44, v33 CheckInterrupts Return v45 @@ -9061,7 +9075,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v24:BasicObject = GetIvar v23, :@foo CheckInterrupts Return v24 @@ -9283,7 +9297,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v19:BasicObject = SendDirect v18, 0x1038, :foo (0x1048) CheckInterrupts Return v19 @@ -9313,7 +9327,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v19:BasicObject = SendDirect v18, 0x1038, :foo (0x1048) CheckInterrupts Return v19 @@ -9344,7 +9358,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v19:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:BasicObject = SendDirect v19, 0x1038, :foo (0x1048) CheckInterrupts Return v20 @@ -9450,7 +9464,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v26:CShape = LoadField v23, :shape_id@0x1040 v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:NilClass = Const Value(nil) @@ -9486,7 +9500,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v26:CShape = LoadField v23, :shape_id@0x1040 v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:NilClass = Const Value(nil) @@ -9522,7 +9536,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v17:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(C@0x1008, foo=@0x1010, cme:0x1018) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v31:CShape = LoadField v28, :shape_id@0x1040 v32:CShape[0x1041] = GuardBitEquals v31, CShape(0x1041) StoreField v28, :@foo@0x1042, v17 @@ -9561,7 +9575,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v17:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(C@0x1008, foo=@0x1010, cme:0x1018) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v31:CShape = LoadField v28, :shape_id@0x1040 v32:CShape[0x1041] = GuardBitEquals v31, CShape(0x1041) StoreField v28, :@foo@0x1042, v17 @@ -9597,7 +9611,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v24:BasicObject = LoadField v23, :foo@0x1040 CheckInterrupts Return v24 @@ -9628,7 +9642,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v24:CPtr = LoadField v23, :as_heap@0x1040 v25:BasicObject = LoadField v24, :foo@0x1041 CheckInterrupts @@ -9663,7 +9677,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) - v27:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v27:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v19:Fixnum[5] = Const Value(5) CheckInterrupts Return v19 @@ -9697,7 +9711,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo=@0x1010, cme:0x1018) - v31:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] + v31:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] recompile v32:CUInt64 = LoadField v31, :RBASIC_FLAGS@0x1040 v33:CUInt64 = GuardNoBitsSet v32, RUBY_FL_FREEZE=CUInt64(2048) StoreField v31, :foo=@0x1041, v13 @@ -9734,7 +9748,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo=@0x1010, cme:0x1018) - v31:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] + v31:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] recompile v32:CUInt64 = LoadField v31, :RBASIC_FLAGS@0x1040 v33:CUInt64 = GuardNoBitsSet v32, RUBY_FL_FREEZE=CUInt64(2048) v34:CPtr = LoadField v31, :as_heap@0x1041 @@ -9897,7 +9911,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) - v24:StringExact = GuardType v10, StringExact + v24:StringExact = GuardType v10, StringExact recompile CheckInterrupts Return v24 "); @@ -9924,7 +9938,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, to_s@0x1010, cme:0x1018) - v23:Fixnum = GuardType v10, Fixnum + v23:Fixnum = GuardType v10, Fixnum recompile v24:StringExact = CCallVariadic v23, :Integer#to_s@0x1040 CheckInterrupts Return v24 @@ -9952,7 +9966,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, to_s@0x1010, cme:0x1018) - v23:Bignum = GuardType v10, Bignum + v23:Bignum = GuardType v10, Bignum recompile v24:StringExact = CCallVariadic v23, :Integer#to_s@0x1040 CheckInterrupts Return v24 @@ -10050,7 +10064,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v29:ArrayExact = GuardType v12, ArrayExact + v29:ArrayExact = GuardType v12, ArrayExact recompile v30:Fixnum = GuardType v13, Fixnum v31:CInt64 = UnboxFixnum v30 v32:CInt64 = ArrayLength v29 @@ -10091,7 +10105,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, []@0x1010, cme:0x1018) - v29:ArraySubclass[class_exact:C] = GuardType v12, ArraySubclass[class_exact:C] + v29:ArraySubclass[class_exact:C] = GuardType v12, ArraySubclass[class_exact:C] recompile v30:Fixnum = GuardType v13, Fixnum v31:CInt64 = UnboxFixnum v30 v32:CInt64 = ArrayLength v29 @@ -10163,7 +10177,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) - v29:HashExact = GuardType v12, HashExact + v29:HashExact = GuardType v12, HashExact recompile v30:BasicObject = HashAref v29, v13 CheckInterrupts Return v30 @@ -10197,7 +10211,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, []@0x1010, cme:0x1018) - v29:HashSubclass[class_exact:C] = GuardType v12, HashSubclass[class_exact:C] + v29:HashSubclass[class_exact:C] = GuardType v12, HashSubclass[class_exact:C] recompile v30:BasicObject = CCallWithFrame v29, :Hash#[]@0x1040, v13 CheckInterrupts Return v30 @@ -10295,7 +10309,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, []=@0x1010, cme:0x1018) - v37:HashExact = GuardType v14, HashExact + v37:HashExact = GuardType v14, HashExact recompile HashAset v37, v15, v16 CheckInterrupts Return v16 @@ -10331,7 +10345,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, []=@0x1010, cme:0x1018) - v37:HashSubclass[class_exact:C] = GuardType v14, HashSubclass[class_exact:C] + v37:HashSubclass[class_exact:C] = GuardType v14, HashSubclass[class_exact:C] recompile v38:BasicObject = CCallWithFrame v37, :Hash#[]=@0x1040, v15, v16 CheckInterrupts Return v16 @@ -10393,7 +10407,7 @@ mod hir_opt_tests { v19:Fixnum[10] = Const Value(10) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []=@0x1010, cme:0x1018) - v33:ArrayExact = GuardType v10, ArrayExact + v33:ArrayExact = GuardType v10, ArrayExact recompile v34:CUInt64 = LoadField v33, :RBASIC_FLAGS@0x1040 v35:CUInt64 = GuardNoBitsSet v34, RUBY_FL_FREEZE=CUInt64(2048) v37:CUInt64 = GuardNoBitsSet v35, RUBY_ELTS_SHARED=CUInt64(4096) @@ -10435,7 +10449,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []=@0x1010, cme:0x1018) - v37:ArrayExact = GuardType v14, ArrayExact + v37:ArrayExact = GuardType v14, ArrayExact recompile v38:Fixnum = GuardType v15, Fixnum v39:CUInt64 = LoadField v37, :RBASIC_FLAGS@0x1040 v40:CUInt64 = GuardNoBitsSet v39, RUBY_FL_FREEZE=CUInt64(2048) @@ -10483,7 +10497,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(MyArray@0x1008) PatchPoint MethodRedefined(MyArray@0x1008, []=@0x1010, cme:0x1018) - v37:ArraySubclass[class_exact:MyArray] = GuardType v14, ArraySubclass[class_exact:MyArray] + v37:ArraySubclass[class_exact:MyArray] = GuardType v14, ArraySubclass[class_exact:MyArray] recompile v38:BasicObject = CCallVariadic v37, :Array#[]=@0x1040, v15, v16 CheckInterrupts Return v16 @@ -10515,7 +10529,7 @@ mod hir_opt_tests { v15:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, <<@0x1010, cme:0x1018) - v27:ArrayExact = GuardType v10, ArrayExact + v27:ArrayExact = GuardType v10, ArrayExact recompile v28:CUInt64 = LoadField v27, :RBASIC_FLAGS@0x1040 v29:CUInt64 = GuardNoBitsSet v28, RUBY_FL_FREEZE=CUInt64(2048) ArrayPush v27, v15 @@ -10549,7 +10563,7 @@ mod hir_opt_tests { v15:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, push@0x1010, cme:0x1018) - v26:ArrayExact = GuardType v10, ArrayExact + v26:ArrayExact = GuardType v10, ArrayExact recompile v27:CUInt64 = LoadField v26, :RBASIC_FLAGS@0x1040 v28:CUInt64 = GuardNoBitsSet v27, RUBY_FL_FREEZE=CUInt64(2048) ArrayPush v26, v15 @@ -10585,7 +10599,7 @@ mod hir_opt_tests { v19:Fixnum[3] = Const Value(3) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, push@0x1010, cme:0x1018) - v30:ArrayExact = GuardType v10, ArrayExact + v30:ArrayExact = GuardType v10, ArrayExact recompile v31:BasicObject = CCallVariadic v30, :Array#push@0x1040, v15, v17, v19 CheckInterrupts Return v31 @@ -10767,7 +10781,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, length@0x1010, cme:0x1018) - v25:ArrayExact = GuardType v10, ArrayExact + v25:ArrayExact = GuardType v10, ArrayExact recompile v26:CInt64 = ArrayLength v25 v27:Fixnum = BoxFixnum v26 CheckInterrupts @@ -10798,7 +10812,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, size@0x1010, cme:0x1018) - v25:ArrayExact = GuardType v10, ArrayExact + v25:ArrayExact = GuardType v10, ArrayExact recompile v26:CInt64 = ArrayLength v25 v27:Fixnum = BoxFixnum v26 CheckInterrupts @@ -10830,7 +10844,7 @@ mod hir_opt_tests { v15:RegexpExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, =~@0x1018, cme:0x1020) - v27:StringExact = GuardType v10, StringExact + v27:StringExact = GuardType v10, StringExact recompile v28:BasicObject = CCallWithFrame v27, :String#=~@0x1048, v15 CheckInterrupts Return v28 @@ -10861,7 +10875,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, getbyte@0x1010, cme:0x1018) - v28:StringExact = GuardType v12, StringExact + v28:StringExact = GuardType v12, StringExact recompile v29:Fixnum = GuardType v13, Fixnum v30:CInt64 = UnboxFixnum v29 v31:CInt64 = LoadField v28, :len@0x1040 @@ -10902,7 +10916,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, getbyte@0x1010, cme:0x1018) - v32:StringExact = GuardType v12, StringExact + v32:StringExact = GuardType v12, StringExact recompile v33:Fixnum = GuardType v13, Fixnum v34:CInt64 = UnboxFixnum v33 v35:CInt64 = LoadField v32, :len@0x1040 @@ -10944,7 +10958,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, setbyte@0x1010, cme:0x1018) - v32:StringExact = GuardType v14, StringExact + v32:StringExact = GuardType v14, StringExact recompile v33:Fixnum = GuardType v15, Fixnum v34:Fixnum = GuardType v16, Fixnum v35:CInt64 = UnboxFixnum v33 @@ -10991,7 +11005,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(MyString@0x1008) PatchPoint MethodRedefined(MyString@0x1008, setbyte@0x1010, cme:0x1018) - v32:StringSubclass[class_exact:MyString] = GuardType v14, StringSubclass[class_exact:MyString] + v32:StringSubclass[class_exact:MyString] = GuardType v14, StringSubclass[class_exact:MyString] recompile v33:Fixnum = GuardType v15, Fixnum v34:Fixnum = GuardType v16, Fixnum v35:CInt64 = UnboxFixnum v33 @@ -11036,7 +11050,7 @@ mod hir_opt_tests { bb3(v13:BasicObject, v14:BasicObject, v15:BasicObject, v16:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, setbyte@0x1010, cme:0x1018) - v32:StringExact = GuardType v14, StringExact + v32:StringExact = GuardType v14, StringExact recompile v33:BasicObject = CCallWithFrame v32, :String#setbyte@0x1040, v15, v16 CheckInterrupts Return v33 @@ -11067,7 +11081,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, empty?@0x1010, cme:0x1018) - v25:StringExact = GuardType v10, StringExact + v25:StringExact = GuardType v10, StringExact recompile v26:CInt64 = LoadField v25, :len@0x1040 v27:CInt64[0] = Const CInt64(0) v28:CBool = IsBitEqual v26, v27 @@ -11102,7 +11116,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, empty?@0x1010, cme:0x1018) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v20:Fixnum[4] = Const Value(4) CheckInterrupts Return v20 @@ -11131,7 +11145,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, succ@0x1010, cme:0x1018) - v24:Fixnum = GuardType v10, Fixnum + v24:Fixnum = GuardType v10, Fixnum recompile v25:Fixnum[1] = Const Value(1) v26:Fixnum = FixnumAdd v24, v25 CheckInterrupts @@ -11161,7 +11175,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, succ@0x1010, cme:0x1018) - v24:Bignum = GuardType v10, Bignum + v24:Bignum = GuardType v10, Bignum recompile v25:BasicObject = CCallWithFrame v24, :Integer#succ@0x1040 CheckInterrupts Return v25 @@ -11191,7 +11205,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(Integer@0x1008, <<@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile v27:Fixnum = FixnumLShift v26, v15 CheckInterrupts Return v27 @@ -11221,7 +11235,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[-5] = Const Value(-5) PatchPoint MethodRedefined(Integer@0x1008, <<@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile v27:BasicObject = CCallWithFrame v26, :Integer#<<@0x1040, v15 CheckInterrupts Return v27 @@ -11251,7 +11265,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[64] = Const Value(64) PatchPoint MethodRedefined(Integer@0x1008, <<@0x1010, cme:0x1018) - v26:Fixnum = GuardType v10, Fixnum + v26:Fixnum = GuardType v10, Fixnum recompile v27:BasicObject = CCallWithFrame v26, :Integer#<<@0x1040, v15 CheckInterrupts Return v27 @@ -11282,7 +11296,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, <<@0x1010, cme:0x1018) - v28:Fixnum = GuardType v12, Fixnum + v28:Fixnum = GuardType v12, Fixnum recompile v29:BasicObject = CCallWithFrame v28, :Integer#<<@0x1040, v13 CheckInterrupts Return v29 @@ -11311,7 +11325,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[5] = Const Value(5) PatchPoint MethodRedefined(Integer@0x1008, >>@0x1010, cme:0x1018) - v25:Fixnum = GuardType v10, Fixnum + v25:Fixnum = GuardType v10, Fixnum recompile v26:Fixnum = FixnumRShift v25, v15 CheckInterrupts Return v26 @@ -11340,7 +11354,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[-5] = Const Value(-5) PatchPoint MethodRedefined(Integer@0x1008, >>@0x1010, cme:0x1018) - v25:Fixnum = GuardType v10, Fixnum + v25:Fixnum = GuardType v10, Fixnum recompile v26:BasicObject = CCallWithFrame v25, :Integer#>>@0x1040, v15 CheckInterrupts Return v26 @@ -11369,7 +11383,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[64] = Const Value(64) PatchPoint MethodRedefined(Integer@0x1008, >>@0x1010, cme:0x1018) - v25:Fixnum = GuardType v10, Fixnum + v25:Fixnum = GuardType v10, Fixnum recompile v26:BasicObject = CCallWithFrame v25, :Integer#>>@0x1040, v15 CheckInterrupts Return v26 @@ -11399,7 +11413,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, >>@0x1010, cme:0x1018) - v27:Fixnum = GuardType v12, Fixnum + v27:Fixnum = GuardType v12, Fixnum recompile v28:BasicObject = CCallWithFrame v27, :Integer#>>@0x1040, v13 CheckInterrupts Return v28 @@ -11430,7 +11444,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, <<@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile v30:String = GuardType v13, String v31:StringExact = StringAppend v29, v30 CheckInterrupts @@ -11462,7 +11476,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, <<@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile v30:Fixnum = GuardType v13, Fixnum v31:StringExact = StringAppendCodepoint v29, v30 CheckInterrupts @@ -11496,7 +11510,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, <<@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile v30:String = GuardType v13, String v31:StringExact = StringAppend v29, v30 CheckInterrupts @@ -11530,7 +11544,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(MyString@0x1008) PatchPoint MethodRedefined(MyString@0x1008, <<@0x1010, cme:0x1018) - v29:StringSubclass[class_exact:MyString] = GuardType v12, StringSubclass[class_exact:MyString] + v29:StringSubclass[class_exact:MyString] = GuardType v12, StringSubclass[class_exact:MyString] recompile v30:BasicObject = CCallWithFrame v29, :String#<<@0x1040, v13 CheckInterrupts Return v30 @@ -11613,7 +11627,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ascii_only?@0x1010, cme:0x1018) - v24:StringExact = GuardType v10, StringExact + v24:StringExact = GuardType v10, StringExact recompile v25:BoolExact = CCall v24, :String#ascii_only?@0x1040 CheckInterrupts Return v25 @@ -11691,7 +11705,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, ^@0x1010, cme:0x1018) - v27:Fixnum = GuardType v12, Fixnum + v27:Fixnum = GuardType v12, Fixnum recompile v28:Fixnum = GuardType v13, Fixnum v29:Fixnum = FixnumXor v27, v28 CheckInterrupts @@ -11725,7 +11739,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, ^@0x1010, cme:0x1018) - v31:Fixnum = GuardType v12, Fixnum + v31:Fixnum = GuardType v12, Fixnum recompile v32:Fixnum = GuardType v13, Fixnum v23:Fixnum[42] = Const Value(42) CheckInterrupts @@ -11756,7 +11770,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, ^@0x1010, cme:0x1018) - v27:Bignum = GuardType v12, Bignum + v27:Bignum = GuardType v12, Bignum recompile v28:BasicObject = CCallWithFrame v27, :Integer#^@0x1040, v13 CheckInterrupts Return v28 @@ -11786,7 +11800,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, ^@0x1010, cme:0x1018) - v27:Fixnum = GuardType v12, Fixnum + v27:Fixnum = GuardType v12, Fixnum recompile v28:BasicObject = CCallWithFrame v27, :Integer#^@0x1040, v13 CheckInterrupts Return v28 @@ -11816,7 +11830,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(TrueClass@0x1008, ^@0x1010, cme:0x1018) - v27:TrueClass = GuardType v12, TrueClass + v27:TrueClass = GuardType v12, TrueClass recompile v28:BasicObject = CCallWithFrame v27, :TrueClass#^@0x1040, v13 CheckInterrupts Return v28 @@ -11870,7 +11884,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, size@0x1010, cme:0x1018) - v25:HashExact = GuardType v10, HashExact + v25:HashExact = GuardType v10, HashExact recompile v26:Fixnum = CCall v25, :Hash#size@0x1040 CheckInterrupts Return v26 @@ -11902,7 +11916,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, size@0x1010, cme:0x1018) - v29:HashExact = GuardType v10, HashExact + v29:HashExact = GuardType v10, HashExact recompile v20:Fixnum[5] = Const Value(5) CheckInterrupts Return v20 @@ -11935,7 +11949,7 @@ mod hir_opt_tests { v15:StaticSymbol[:foo] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v30:TrueClass = Const Value(true) CheckInterrupts @@ -11968,7 +11982,7 @@ mod hir_opt_tests { v15:StaticSymbol[:foo] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, respond_to_missing?@0x1048, cme:0x1050) PatchPoint MethodRedefined(C@0x1010, foo@0x1078, cme:0x1080) v32:FalseClass = Const Value(false) @@ -12004,7 +12018,7 @@ mod hir_opt_tests { v15:StaticSymbol[:foo] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v30:FalseClass = Const Value(false) CheckInterrupts @@ -12040,7 +12054,7 @@ mod hir_opt_tests { v17:FalseClass = Const Value(false) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v32:FalseClass = Const Value(false) CheckInterrupts @@ -12076,7 +12090,7 @@ mod hir_opt_tests { v17:NilClass = Const Value(nil) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v32:FalseClass = Const Value(false) CheckInterrupts @@ -12112,7 +12126,7 @@ mod hir_opt_tests { v17:TrueClass = Const Value(true) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v32:TrueClass = Const Value(true) CheckInterrupts @@ -12147,7 +12161,7 @@ mod hir_opt_tests { v17:Fixnum[4] = Const Value(4) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v32:TrueClass = Const Value(true) CheckInterrupts @@ -12182,7 +12196,7 @@ mod hir_opt_tests { v17:NilClass = Const Value(nil) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v28:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, foo@0x1048, cme:0x1050) v32:TrueClass = Const Value(true) CheckInterrupts @@ -12215,7 +12229,7 @@ mod hir_opt_tests { v15:StaticSymbol[:foo] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile PatchPoint MethodRedefined(C@0x1010, respond_to_missing?@0x1048, cme:0x1050) PatchPoint MethodRedefined(C@0x1010, foo@0x1078, cme:0x1080) v32:FalseClass = Const Value(false) @@ -12252,7 +12266,7 @@ mod hir_opt_tests { v15:StaticSymbol[:foo] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, respond_to?@0x1018, cme:0x1020) - v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v26:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v27:BasicObject = CCallVariadic v26, :Kernel#respond_to?@0x1048, v15 CheckInterrupts Return v27 @@ -12278,7 +12292,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile CheckInterrupts Return v18 "); @@ -12304,7 +12318,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:StringExact[VALUE(0x1038)] = Const Value(VALUE(0x1038)) CheckInterrupts Return v20 @@ -12330,7 +12344,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:NilClass = Const Value(nil) CheckInterrupts Return v20 @@ -12356,7 +12370,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:TrueClass = Const Value(true) CheckInterrupts Return v20 @@ -12382,7 +12396,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:FalseClass = Const Value(false) CheckInterrupts Return v20 @@ -12408,7 +12422,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:Fixnum[0] = Const Value(0) CheckInterrupts Return v20 @@ -12434,7 +12448,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v20:Fixnum[1] = Const Value(1) CheckInterrupts Return v20 @@ -12461,7 +12475,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v20:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile CheckInterrupts Return v11 "); @@ -12489,7 +12503,7 @@ mod hir_opt_tests { v13:Fixnum[2] = Const Value(2) v15:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) - v24:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v24:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile CheckInterrupts Return v15 "); @@ -12559,7 +12573,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Symbol@0x1008, to_sym@0x1010, cme:0x1018) - v22:StaticSymbol = GuardType v10, StaticSymbol + v22:StaticSymbol = GuardType v10, StaticSymbol recompile CheckInterrupts Return v22 "); @@ -12586,7 +12600,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, to_i@0x1010, cme:0x1018) - v22:Fixnum = GuardType v10, Fixnum + v22:Fixnum = GuardType v10, Fixnum recompile CheckInterrupts Return v22 "); @@ -12776,7 +12790,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile v30:String = GuardType v13, String v31:BoolExact = StringEqual v29, v30 CheckInterrupts @@ -12810,7 +12824,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, ==@0x1010, cme:0x1018) - v29:StringSubclass[class_exact:C] = GuardType v12, StringSubclass[class_exact:C] + v29:StringSubclass[class_exact:C] = GuardType v12, StringSubclass[class_exact:C] recompile v30:String = GuardType v13, String v31:BoolExact = StringEqual v29, v30 CheckInterrupts @@ -12844,7 +12858,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile v30:String = GuardType v13, String v31:BoolExact = StringEqual v29, v30 CheckInterrupts @@ -12876,7 +12890,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ===@0x1010, cme:0x1018) - v28:StringExact = GuardType v12, StringExact + v28:StringExact = GuardType v12, StringExact recompile v29:String = GuardType v13, String v30:BoolExact = StringEqual v28, v29 CheckInterrupts @@ -12910,7 +12924,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, ===@0x1010, cme:0x1018) - v28:StringSubclass[class_exact:C] = GuardType v12, StringSubclass[class_exact:C] + v28:StringSubclass[class_exact:C] = GuardType v12, StringSubclass[class_exact:C] recompile v29:String = GuardType v13, String v30:BoolExact = StringEqual v28, v29 CheckInterrupts @@ -12944,7 +12958,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ===@0x1010, cme:0x1018) - v28:StringExact = GuardType v12, StringExact + v28:StringExact = GuardType v12, StringExact recompile v29:String = GuardType v13, String v30:BoolExact = StringEqual v28, v29 CheckInterrupts @@ -12974,7 +12988,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v26:StringExact = GuardType v10, StringExact + v26:StringExact = GuardType v10, StringExact recompile v29:TrueClass = Const Value(true) CheckInterrupts Return v29 @@ -13003,7 +13017,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ===@0x1010, cme:0x1018) - v25:StringExact = GuardType v10, StringExact + v25:StringExact = GuardType v10, StringExact recompile v28:TrueClass = Const Value(true) CheckInterrupts Return v28 @@ -13277,7 +13291,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, ==@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile v30:String = GuardType v13, String v31:BoolExact = StringEqual v29, v30 CheckInterrupts @@ -13344,7 +13358,7 @@ mod hir_opt_tests { v15:NilClass = Const Value(nil) PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, !=@0x1010, cme:0x1018) - v27:StringExact = GuardType v10, StringExact + v27:StringExact = GuardType v10, StringExact recompile v28:BoolExact = CCallWithFrame v27, :BasicObject#!=@0x1040, v15 CheckInterrupts Return v28 @@ -13377,7 +13391,7 @@ mod hir_opt_tests { bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, !=@0x1010, cme:0x1018) - v29:StringExact = GuardType v12, StringExact + v29:StringExact = GuardType v12, StringExact recompile PatchPoint MethodRedefined(String@0x1008, ==@0x1040, cme:0x1048) v33:String = GuardType v13, String v34:BoolExact = StringEqual v29, v33 @@ -13413,7 +13427,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, !=@0x1010, cme:0x1018) - v26:StringExact = GuardType v10, StringExact + v26:StringExact = GuardType v10, StringExact recompile PatchPoint MethodRedefined(String@0x1008, ==@0x1040, cme:0x1048) v35:TrueClass = Const Value(true) v32:TrueClass = Const Value(true) @@ -13448,7 +13462,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, size@0x1010, cme:0x1018) - v25:StringExact = GuardType v10, StringExact + v25:StringExact = GuardType v10, StringExact recompile v26:Fixnum = CCall v25, :String#size@0x1040 CheckInterrupts Return v26 @@ -13480,7 +13494,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, size@0x1010, cme:0x1018) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v20:Fixnum[5] = Const Value(5) CheckInterrupts Return v20 @@ -13511,7 +13525,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, bytesize@0x1010, cme:0x1018) - v24:StringExact = GuardType v10, StringExact + v24:StringExact = GuardType v10, StringExact recompile v25:CInt64 = LoadField v24, :len@0x1040 v26:Fixnum = BoxFixnum v25 CheckInterrupts @@ -13544,7 +13558,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, bytesize@0x1010, cme:0x1018) - v28:StringExact = GuardType v10, StringExact + v28:StringExact = GuardType v10, StringExact recompile v19:Fixnum[5] = Const Value(5) CheckInterrupts Return v19 @@ -13575,7 +13589,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, length@0x1010, cme:0x1018) - v25:StringExact = GuardType v10, StringExact + v25:StringExact = GuardType v10, StringExact recompile v26:Fixnum = CCall v25, :String#length@0x1040 CheckInterrupts Return v26 @@ -13669,7 +13683,7 @@ mod hir_opt_tests { v25:ClassSubclass[String@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, is_a?@0x1011, cme:0x1018) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v30:BoolExact = IsA v29, v25 CheckInterrupts Return v30 @@ -13701,7 +13715,7 @@ mod hir_opt_tests { v25:ModuleSubclass[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, is_a?@0x1020, cme:0x1028) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v30:BasicObject = CCallWithFrame v29, :Kernel#is_a?@0x1050, v25 CheckInterrupts Return v30 @@ -13736,7 +13750,7 @@ mod hir_opt_tests { v29:ClassSubclass[Integer@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, is_a?@0x1020, cme:0x1028) - v33:StringExact = GuardType v10, StringExact + v33:StringExact = GuardType v10, StringExact recompile v21:Fixnum[5] = Const Value(5) CheckInterrupts Return v21 @@ -13802,7 +13816,7 @@ mod hir_opt_tests { v25:ClassSubclass[String@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1011, cme:0x1018) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v30:BoolExact = IsA v29, v25 CheckInterrupts Return v30 @@ -13834,7 +13848,7 @@ mod hir_opt_tests { v25:ModuleSubclass[Kernel@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, kind_of?@0x1020, cme:0x1028) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v30:BasicObject = CCallWithFrame v29, :Kernel#kind_of?@0x1050, v25 CheckInterrupts Return v30 @@ -13869,7 +13883,7 @@ mod hir_opt_tests { v29:ClassSubclass[Integer@0x1010] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(String@0x1018) PatchPoint MethodRedefined(String@0x1018, kind_of?@0x1020, cme:0x1028) - v33:StringExact = GuardType v10, StringExact + v33:StringExact = GuardType v10, StringExact recompile v21:Fixnum[5] = Const Value(5) CheckInterrupts Return v21 @@ -14100,7 +14114,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, length@0x1010, cme:0x1018) - v29:StringExact = GuardType v10, StringExact + v29:StringExact = GuardType v10, StringExact recompile v20:Fixnum[4] = Const Value(4) CheckInterrupts Return v20 @@ -14140,7 +14154,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint NoSingletonClass(C@0x1000) PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) - v43:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] + v43:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] recompile v46:ClassSubclass[C@0x1000] = Const Value(VALUE(0x1000)) v13:StaticSymbol[:_lex_actions] = Const Value(VALUE(0x1038)) v15:TrueClass = Const Value(true) @@ -14176,7 +14190,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, class@0x1010, cme:0x1018) - v25:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v25:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v28:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Class@0x1040, name@0x1048, cme:0x1050) v32:StringExact|NilClass = CCall v28, :Module#name@0x1078 @@ -14208,7 +14222,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, class@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] + v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] recompile v26:ClassSubclass[C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts Return v26 @@ -14258,7 +14272,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) - v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] + v18:ObjectSubclass[class_exact*:Object@VALUE(0x1000)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1000)] recompile v21:ClassSubclass[Object@0x1038] = Const Value(VALUE(0x1038)) CheckInterrupts Return v21 @@ -14699,7 +14713,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): PatchPoint NoSingletonClass(TestDynamic@0x1008) PatchPoint MethodRedefined(TestDynamic@0x1008, val@0x1010, cme:0x1018) - v23:ObjectSubclass[class_exact:TestDynamic] = GuardType v10, ObjectSubclass[class_exact:TestDynamic] + v23:ObjectSubclass[class_exact:TestDynamic] = GuardType v10, ObjectSubclass[class_exact:TestDynamic] recompile v26:CShape = LoadField v23, :shape_id@0x1040 v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) recompile v28:BasicObject = LoadField v23, :@val@0x1042 @@ -14806,7 +14820,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint NoSingletonClass(C@0x1000) PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] + v19:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] recompile v21:Fixnum[42] = Const Value(42) CheckInterrupts Return v21 @@ -14864,7 +14878,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint NoSingletonClass(BasicObject@0x1000) PatchPoint MethodRedefined(BasicObject@0x1000, initialize@0x1008, cme:0x1010) - v21:BasicObjectExact = GuardType v6, BasicObjectExact + v21:BasicObjectExact = GuardType v6, BasicObjectExact recompile v22:NilClass = Const Value(nil) CheckInterrupts Return v22 @@ -14948,7 +14962,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint NoSingletonClass(C@0x1000) PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) - v19:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] + v19:ObjectSubclass[class_exact:C] = GuardType v6, ObjectSubclass[class_exact:C] recompile v21:Fixnum[42] = Const Value(42) CheckInterrupts Return v21 @@ -15031,7 +15045,7 @@ mod hir_opt_tests { v19:BasicObject = Send v12, :length # SendFallbackReason: Singleton class previously created for receiver class PatchPoint NoSingletonClass(Proc@0x1008) PatchPoint MethodRedefined(Proc@0x1008, call@0x1010, cme:0x1018) - v40:ObjectSubclass[class_exact:Proc] = GuardType v13, ObjectSubclass[class_exact:Proc] + v40:ObjectSubclass[class_exact:Proc] = GuardType v13, ObjectSubclass[class_exact:Proc] recompile v41:BasicObject = InvokeProc v40 PatchPoint NoEPEscape(test) v32:BasicObject = Send v12, :length # SendFallbackReason: Singleton class previously created for receiver class @@ -15187,7 +15201,7 @@ mod hir_opt_tests { v33:BasicObject = SendDirect v9, 0x1058, :foo (0x1068), v10 v18:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1070, +@0x1078, cme:0x1080) - v36:Fixnum = GuardType v33, Fixnum + v36:Fixnum = GuardType v33, Fixnum recompile v37:Fixnum = FixnumAdd v36, v18 CheckInterrupts Return v37 @@ -15448,7 +15462,7 @@ mod hir_opt_tests { bb3(v11:HeapBasicObject, v12:BasicObject, v13:NilClass): PatchPoint NoSingletonClass(B@0x1008) PatchPoint MethodRedefined(B@0x1008, proc@0x1010, cme:0x1018) - v39:ObjectSubclass[class_exact:B] = GuardType v11, ObjectSubclass[class_exact:B] + v39:ObjectSubclass[class_exact:B] = GuardType v11, ObjectSubclass[class_exact:B] recompile v40:BasicObject = CCallWithFrame v39, :Kernel#proc@0x1040, block=0x1048 v19:CPtr = GetEP 0 v20:BasicObject = LoadField v19, :blk@0x1050 @@ -15661,7 +15675,7 @@ mod hir_opt_tests { bb4(v35:BasicObject, v36:BasicObject, v37:BasicObject): v40:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Integer@0x1070, +@0x1078, cme:0x1080) - v59:Fixnum = GuardType v37, Fixnum + v59:Fixnum = GuardType v37, Fixnum recompile v60:Fixnum = FixnumAdd v59, v40 CheckInterrupts Return v60 @@ -16078,7 +16092,7 @@ mod hir_opt_tests { bb11(): v71:Falsy = RefineType v60, Falsy PatchPoint MethodRedefined(Object@0x1010, lambda@0x1018, cme:0x1020) - v118:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v58, ObjectSubclass[class_exact*:Object@VALUE(0x1010)] + v118:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v58, ObjectSubclass[class_exact*:Object@VALUE(0x1010)] recompile v119:BasicObject = CCallWithFrame v118, :Kernel#lambda@0x1048, block=0x1050 v75:CPtr = GetEP 0 v76:BasicObject = LoadField v75, :list@0x1001 @@ -16387,7 +16401,7 @@ mod hir_opt_tests { v19:Truthy = RefineType v10, Truthy v23:Fixnum[42] = Const Value(42) PatchPoint MethodRedefined(Object@0x1008, greet_recompile@0x1010, cme:0x1018) - v43:ObjectSubclass[class_exact*:Object@VALUE(0x1008)] = GuardType v9, ObjectSubclass[class_exact*:Object@VALUE(0x1008)] + v43:ObjectSubclass[class_exact*:Object@VALUE(0x1008)] = GuardType v9, ObjectSubclass[class_exact*:Object@VALUE(0x1008)] recompile v44:BasicObject = SendDirect v43, 0x1040, :greet_recompile (0x1050), v23 CheckInterrupts Return v44 @@ -16541,7 +16555,7 @@ mod hir_opt_tests { bb3(v9:BasicObject, v10:BasicObject): v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, -@0x1010, cme:0x1018) - v35:Fixnum = GuardType v10, Fixnum + v35:Fixnum = GuardType v10, Fixnum recompile v36:Fixnum = FixnumSub v35, v15 v21:Fixnum[2] = Const Value(2) v40:Fixnum = FixnumSub v35, v21 @@ -16731,7 +16745,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, nan?@0x1010, cme:0x1018) - v23:Flonum = GuardType v10, Flonum + v23:Flonum = GuardType v10, Flonum recompile v24:BoolExact = CCall v23, :Float#nan?@0x1040 CheckInterrupts Return v24 @@ -16759,7 +16773,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, finite?@0x1010, cme:0x1018) - v23:Flonum = GuardType v10, Flonum + v23:Flonum = GuardType v10, Flonum recompile v24:BoolExact = CCall v23, :Float#finite?@0x1040 CheckInterrupts Return v24 @@ -16787,7 +16801,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, infinite?@0x1010, cme:0x1018) - v23:Flonum = GuardType v10, Flonum + v23:Flonum = GuardType v10, Flonum recompile v24:NilClass|Fixnum = CCall v23, :Float#infinite?@0x1040 CheckInterrupts Return v24 @@ -16815,7 +16829,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, even?@0x1010, cme:0x1018) - v22:Fixnum = GuardType v10, Fixnum + v22:Fixnum = GuardType v10, Fixnum recompile v24:BoolExact = InvokeBuiltin leaf <inline_expr>, v22 CheckInterrupts Return v24 @@ -16843,7 +16857,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Integer@0x1008, odd?@0x1010, cme:0x1018) - v22:Fixnum = GuardType v10, Fixnum + v22:Fixnum = GuardType v10, Fixnum recompile v24:BoolExact = InvokeBuiltin leaf <inline_expr>, v22 CheckInterrupts Return v24 @@ -16871,7 +16885,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, zero?@0x1010, cme:0x1018) - v22:Flonum = GuardType v10, Flonum + v22:Flonum = GuardType v10, Flonum recompile v24:BoolExact = InvokeBuiltin leaf <inline_expr>, v22 CheckInterrupts Return v24 @@ -16899,7 +16913,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, positive?@0x1010, cme:0x1018) - v22:Flonum = GuardType v10, Flonum + v22:Flonum = GuardType v10, Flonum recompile v24:BoolExact = InvokeBuiltin leaf <inline_expr>, v22 CheckInterrupts Return v24 @@ -16927,7 +16941,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, negative?@0x1010, cme:0x1018) - v22:Flonum = GuardType v10, Flonum + v22:Flonum = GuardType v10, Flonum recompile v24:BoolExact = InvokeBuiltin leaf <inline_expr>, v22 CheckInterrupts Return v24 @@ -16956,7 +16970,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Float@0x1008, +@0x1010, cme:0x1018) - v28:Flonum = GuardType v12, Flonum + v28:Flonum = GuardType v12, Flonum recompile v29:Flonum = GuardType v13, Flonum v30:Float = FloatAdd v28, v29 CheckInterrupts @@ -16987,7 +17001,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Float@0x1008, *@0x1010, cme:0x1018) - v28:Flonum = GuardType v12, Flonum + v28:Flonum = GuardType v12, Flonum recompile v29:Flonum = GuardType v13, Flonum v30:Float = FloatMul v28, v29 CheckInterrupts @@ -17018,7 +17032,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Float@0x1008, -@0x1010, cme:0x1018) - v28:Flonum = GuardType v12, Flonum + v28:Flonum = GuardType v12, Flonum recompile v29:Flonum = GuardType v13, Flonum v30:Float = FloatSub v28, v29 CheckInterrupts @@ -17049,7 +17063,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Float@0x1008, /@0x1010, cme:0x1018) - v28:Flonum = GuardType v12, Flonum + v28:Flonum = GuardType v12, Flonum recompile v29:Flonum = GuardType v13, Flonum v30:Float = FloatDiv v28, v29 CheckInterrupts @@ -17078,7 +17092,7 @@ mod hir_opt_tests { Jump bb3(v6, v7) bb3(v9:BasicObject, v10:BasicObject): PatchPoint MethodRedefined(Float@0x1008, to_i@0x1010, cme:0x1018) - v23:Flonum = GuardType v10, Flonum + v23:Flonum = GuardType v10, Flonum recompile v24:Integer = FloatToInt v23 CheckInterrupts Return v24 @@ -17108,7 +17122,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Float@0x1008, *@0x1010, cme:0x1018) - v28:Flonum = GuardType v12, Flonum + v28:Flonum = GuardType v12, Flonum recompile v29:Fixnum = GuardType v13, Fixnum v30:Float = FloatMul v28, v29 CheckInterrupts @@ -17155,7 +17169,7 @@ mod hir_opt_tests { v17:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, var@0x1010, cme:0x1018) - v138:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] + v138:ObjectSubclass[class_exact:C] = GuardType v12, ObjectSubclass[class_exact:C] recompile v139:BasicObject = LoadField v138, :var@0x1040 PatchPoint MethodRedefined(Integer@0x1048, +@0x1050, cme:0x1058) v179:Fixnum = GuardType v139, Fixnum @@ -17263,7 +17277,7 @@ mod hir_opt_tests { Jump bb3(v7, v8, v9) bb3(v11:BasicObject, v12:BasicObject, v13:BasicObject): PatchPoint MethodRedefined(Symbol@0x1008, ==@0x1010, cme:0x1018) - v48:StaticSymbol = GuardType v12, StaticSymbol + v48:StaticSymbol = GuardType v12, StaticSymbol recompile v49:CBool = IsBitEqual v48, v13 v50:BoolExact = BoxBool v49 CheckInterrupts @@ -17278,4 +17292,117 @@ mod hir_opt_tests { Return v40 "); } + + #[test] + fn test_trigger_guard_type_recompilation() { + eval(" + class C + def f(x) + @a = 1 + y = x + 1 + @a = y + end + end + + # As of 06/04/2026, zjit/src/options.rs uses 5 as the default number of profiles + # Let's pick a number that is reasonably larger to ensure compilation, even if + # the default value changes a bit + num_to_compile = 30 + + c = C.new + + # Repeatedly call an integer until this fast path gets JITed + num_to_compile.times { c.f(1) } + + "); + + let intermediate_hir = hir_string_proc("C.new.method(:f)"); + + eval(" + # Supposed to be the same as the earlier Ruby method in this test + num_to_compile = 30 + c = C.new + # Call this with a float in order to trigger a guard failure + # Do this enough times to cause a recompilation + num_to_compile.times { c.f(1.5) } + "); + + let final_hir = hir_string_proc("C.new.method(:f)"); + + assert_snapshot!(format!("{intermediate_hir}\n{final_hir}"), @" + fn f@<compiled>:4: + bb1(): + EntryPoint interpreter + v1:HeapBasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + v4:NilClass = Const Value(nil) + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:HeapBasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :x@1 + v9:NilClass = Const Value(nil) + Jump bb3(v7, v8, v9) + bb3(v11:HeapBasicObject, v12:BasicObject, v13:NilClass): + v17:Fixnum[1] = Const Value(1) + PatchPoint SingleRactorMode + SetIvar v11, :@a, v17 + PatchPoint NoEPEscape(f) + v27:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) + v46:Fixnum = GuardType v12, Fixnum recompile + v47:Fixnum = FixnumAdd v46, v27 + PatchPoint SingleRactorMode + SetIvar v11, :@a, v47 + CheckInterrupts + Return v47 + + fn f@<compiled>:4: + bb1(): + EntryPoint interpreter + v1:HeapBasicObject = LoadSelf + v2:CPtr = LoadSP + v3:BasicObject = LoadField v2, :x@0x1000 + v4:NilClass = Const Value(nil) + Jump bb3(v1, v3, v4) + bb2(): + EntryPoint JIT(0) + v7:HeapBasicObject = LoadArg :self@0 + v8:BasicObject = LoadArg :x@1 + v9:NilClass = Const Value(nil) + Jump bb3(v7, v8, v9) + bb3(v11:HeapBasicObject, v12:BasicObject, v13:NilClass): + v17:Fixnum[1] = Const Value(1) + PatchPoint SingleRactorMode + SetIvar v11, :@a, v17 + PatchPoint NoEPEscape(f) + v27:Fixnum[1] = Const Value(1) + v30:CBool = HasType v12, Flonum + CondBranch v30, bb5(v11, v12, v13, v12, v27), bb6() + bb5(v31:HeapBasicObject, v32:BasicObject, v33:NilClass, v34:BasicObject, v35:Fixnum[1]): + v37:Flonum = RefineType v34, Flonum + PatchPoint MethodRedefined(Float@0x1008, +@0x1010, cme:0x1018) + v74:Float = FloatAdd v37, v35 + Jump bb4(v31, v32, v33, v74) + bb6(): + v41:CBool = HasType v12, Fixnum + CondBranch v41, bb7(v11, v12, v13, v12, v27), bb8() + bb7(v42:HeapBasicObject, v43:BasicObject, v44:NilClass, v45:BasicObject, v46:Fixnum[1]): + v48:Fixnum = RefineType v45, Fixnum + PatchPoint MethodRedefined(Integer@0x1040, +@0x1010, cme:0x1048) + v77:Fixnum = FixnumAdd v48, v46 + Jump bb4(v42, v43, v44, v77) + bb8(): + PatchPoint MethodRedefined(Float@0x1008, +@0x1010, cme:0x1018) + v80:Flonum = GuardType v12, Flonum recompile + v81:Float = FloatAdd v80, v27 + Jump bb4(v11, v80, v13, v81) + bb4(v54:HeapBasicObject, v55:BasicObject, v56:NilClass, v57:Float|Fixnum): + PatchPoint SingleRactorMode + SetIvar v54, :@a, v57 + CheckInterrupts + Return v57 + "); + } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index a17a5262f4..09ca3687e2 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -120,7 +120,7 @@ mod snapshot_tests { v16:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13, v15], locals: [] } v23:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v13, v15, v11], locals: [] } PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) - v25:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1010)] + v25:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1010)] recompile v26:BasicObject = SendDirect v25, 0x1048, :foo (0x1058), v13, v15, v11 v18:Any = Snapshot FrameState { pc: 0x1060, stack: [v26], locals: [] } PatchPoint NoTracePoint @@ -156,7 +156,7 @@ mod snapshot_tests { v13:Fixnum[2] = Const Value(2) v14:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13], locals: [] } PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) - v22:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1010)] + v22:ObjectSubclass[class_exact*:Object@VALUE(0x1010)] = GuardType v6, ObjectSubclass[class_exact*:Object@VALUE(0x1010)] recompile v23:BasicObject = SendDirect v22, 0x1048, :foo (0x1058), v11, v13 v16:Any = Snapshot FrameState { pc: 0x1060, stack: [v23], locals: [] } PatchPoint NoTracePoint diff --git a/zjit/src/payload.rs b/zjit/src/payload.rs index 807c33b8b7..51b6f4721b 100644 --- a/zjit/src/payload.rs +++ b/zjit/src/payload.rs @@ -3,6 +3,7 @@ use std::ptr::NonNull; use crate::codegen::IseqCallRef; use crate::stats::CompileError; use crate::{cruby::*, profile::IseqProfile, virtualmem::CodePtr}; +use crate::options::get_option; pub use crate::jit_frame::JITFrame; @@ -35,6 +36,14 @@ impl IseqPayload { self_is_heap_object: false, } } + + /// Profile counts are used for compilation policy. + /// When we deoptimize a method that can be recompiled, we need to update the count to collect more profiles. + /// Otherwise, we will generate the same code that was just deoptimized. + pub fn reset_profiles_remaining(&mut self, insn_idx: YarvInsnIdx) { + let num_profiles = get_option!(num_profiles); + self.profile.entry_mut(insn_idx).set_profiles_remaining(num_profiles); + } } /// JIT code version. When the same ISEQ is compiled with a different assumption, a new version is created. diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index 000c424da4..56a0f9bc3d 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -417,7 +417,7 @@ impl ProfiledType { /// Per-instruction profile entry, stored sparsely in a sorted Vec. #[derive(Debug)] -struct ProfileEntry { +pub struct ProfileEntry { /// YARV instruction index insn_idx: u32, /// Type information of YARV instruction operands @@ -426,6 +426,12 @@ struct ProfileEntry { profiles_remaining: NumProfiles, } +impl ProfileEntry { + pub fn set_profiles_remaining(&mut self, num_profiles: NumProfiles) { + self.profiles_remaining = num_profiles; + } +} + #[derive(Debug)] pub struct IseqProfile { /// Sparse storage of per-instruction profile data, sorted by instruction index. @@ -445,7 +451,7 @@ impl IseqProfile { } /// Get or create a mutable profile entry for the given instruction index. - fn entry_mut(&mut self, insn_idx: YarvInsnIdx) -> &mut ProfileEntry { + pub fn entry_mut(&mut self, insn_idx: YarvInsnIdx) -> &mut ProfileEntry { let idx = insn_idx as u32; match self.entries.binary_search_by_key(&idx, |e| e.insn_idx) { Ok(i) => &mut self.entries[i], |
