opal 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +4 -4
  3. data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/build.yml +11 -5
  6. data/.gitignore +1 -0
  7. data/.jshintrc +1 -1
  8. data/.rubocop.yml +2 -1
  9. data/CHANGELOG.md +95 -1
  10. data/Gemfile +0 -4
  11. data/HACKING.md +1 -1
  12. data/README.md +19 -15
  13. data/UNRELEASED.md +37 -96
  14. data/benchmark-ips/bm_array_unshift.rb +7 -0
  15. data/bin/build-browser-source-map-support +2 -3
  16. data/bin/opal-mspec +2 -0
  17. data/docs/compiler.md +1 -1
  18. data/examples/rack/Gemfile +0 -1
  19. data/examples/rack/Gemfile.lock +0 -4
  20. data/lib/opal/cli.rb +1 -0
  21. data/lib/opal/cli_options.rb +4 -0
  22. data/lib/opal/cli_runners/nodejs.rb +5 -1
  23. data/lib/opal/cli_runners/source-map-support-browser.js +8 -2
  24. data/lib/opal/cli_runners/source-map-support-node.js +3706 -0
  25. data/lib/opal/cli_runners/source-map-support.js +3 -1
  26. data/lib/opal/compiler.rb +2 -2
  27. data/lib/opal/nodes/args/arity_check.rb +1 -0
  28. data/lib/opal/nodes/args/parameters.rb +6 -0
  29. data/lib/opal/nodes/class.rb +1 -13
  30. data/lib/opal/nodes/literal.rb +14 -7
  31. data/lib/opal/nodes/module.rb +13 -9
  32. data/lib/opal/nodes/variables.rb +13 -4
  33. data/lib/opal/nodes/while.rb +54 -17
  34. data/lib/opal/parser.rb +1 -5
  35. data/lib/opal/parser/patch.rb +44 -0
  36. data/lib/opal/repl.rb +7 -0
  37. data/lib/opal/rewriter.rb +4 -0
  38. data/lib/opal/rewriters/arguments.rb +4 -1
  39. data/lib/opal/rewriters/forward_args.rb +54 -0
  40. data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
  41. data/lib/opal/rewriters/opal_engine_check.rb +5 -7
  42. data/lib/opal/rewriters/pattern_matching.rb +287 -0
  43. data/lib/opal/version.rb +1 -1
  44. data/opal/corelib/array.rb +42 -20
  45. data/opal/corelib/array/pack.rb +6 -1
  46. data/opal/corelib/complex.rb +2 -0
  47. data/opal/corelib/constants.rb +3 -3
  48. data/opal/corelib/hash.rb +45 -38
  49. data/opal/corelib/module.rb +2 -7
  50. data/opal/corelib/number.rb +2 -180
  51. data/opal/corelib/numeric.rb +156 -0
  52. data/opal/corelib/object_space.rb +102 -0
  53. data/opal/corelib/pattern_matching.rb +159 -0
  54. data/opal/corelib/random.rb +31 -66
  55. data/opal/corelib/random/formatter.rb +122 -0
  56. data/opal/corelib/range.rb +50 -19
  57. data/opal/corelib/runtime.js +82 -21
  58. data/opal/corelib/string.rb +86 -52
  59. data/opal/corelib/string/encoding.rb +140 -25
  60. data/opal/corelib/string/unpack.rb +26 -40
  61. data/opal/opal.rb +1 -0
  62. data/opal/opal/full.rb +2 -0
  63. data/package.json +1 -1
  64. data/spec/filters/bugs/array.rb +0 -23
  65. data/spec/filters/bugs/basicobject.rb +3 -0
  66. data/spec/filters/bugs/encoding.rb +0 -2
  67. data/spec/filters/bugs/exception.rb +1 -0
  68. data/spec/filters/bugs/float.rb +0 -2
  69. data/spec/filters/bugs/hash.rb +3 -13
  70. data/spec/filters/bugs/integer.rb +0 -2
  71. data/spec/filters/bugs/kernel.rb +16 -3
  72. data/spec/filters/bugs/language.rb +27 -90
  73. data/spec/filters/bugs/marshal.rb +1 -3
  74. data/spec/filters/bugs/module.rb +16 -1
  75. data/spec/filters/bugs/numeric.rb +4 -12
  76. data/spec/filters/bugs/objectspace.rb +67 -0
  77. data/spec/filters/bugs/pack_unpack.rb +0 -9
  78. data/spec/filters/bugs/pathname.rb +1 -0
  79. data/spec/filters/bugs/proc.rb +8 -0
  80. data/spec/filters/bugs/random.rb +3 -6
  81. data/spec/filters/bugs/range.rb +83 -113
  82. data/spec/filters/bugs/set.rb +2 -0
  83. data/spec/filters/bugs/string.rb +32 -70
  84. data/spec/filters/bugs/struct.rb +2 -10
  85. data/spec/filters/bugs/time.rb +8 -2
  86. data/spec/filters/unsupported/float.rb +3 -0
  87. data/spec/filters/unsupported/freeze.rb +1 -0
  88. data/spec/filters/unsupported/integer.rb +3 -0
  89. data/spec/filters/unsupported/refinements.rb +8 -0
  90. data/spec/filters/unsupported/string.rb +100 -95
  91. data/spec/filters/unsupported/time.rb +4 -0
  92. data/spec/lib/compiler_spec.rb +16 -0
  93. data/spec/lib/rewriters/forward_args_spec.rb +61 -0
  94. data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
  95. data/spec/lib/rewriters/numblocks_spec.rb +44 -0
  96. data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
  97. data/spec/opal/core/language/forward_args_spec.rb +53 -0
  98. data/spec/opal/core/language/infinite_range_spec.rb +13 -0
  99. data/spec/opal/core/language/memoization_spec.rb +16 -0
  100. data/spec/opal/core/language/pattern_matching_spec.rb +124 -0
  101. data/spec/opal/core/module_spec.rb +38 -2
  102. data/spec/opal/core/number/to_i_spec.rb +28 -0
  103. data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
  104. data/spec/opal/core/runtime/constants_spec.rb +20 -1
  105. data/spec/opal/core/string/subclassing_spec.rb +16 -0
  106. data/spec/opal/core/string/unpack_spec.rb +22 -0
  107. data/spec/opal/core/string_spec.rb +4 -4
  108. data/spec/ruby_specs +4 -1
  109. data/stdlib/json.rb +3 -1
  110. data/stdlib/promise/v1.rb +1 -0
  111. data/stdlib/promise/v2.rb +386 -0
  112. data/stdlib/securerandom.rb +55 -35
  113. data/tasks/releasing.rake +1 -1
  114. data/tasks/testing.rake +6 -4
  115. data/test/nodejs/test_string.rb +25 -0
  116. data/test/opal/promisev2/test_always.rb +63 -0
  117. data/test/opal/promisev2/test_error.rb +16 -0
  118. data/test/opal/promisev2/test_rescue.rb +59 -0
  119. data/test/opal/promisev2/test_then.rb +90 -0
  120. data/test/opal/promisev2/test_trace.rb +52 -0
  121. data/test/opal/promisev2/test_value.rb +16 -0
  122. data/test/opal/promisev2/test_when.rb +35 -0
  123. data/vendored-minitest/minitest/assertions.rb +2 -0
  124. metadata +47 -8
  125. data/lib/opal/parser/with_c_lexer.rb +0 -15
@@ -1,44 +1,64 @@
1
1
  module SecureRandom
2
- def self.hex(count = nil)
3
- count ||= 16
4
- count = count.to_int unless `typeof count === "number"`
5
- raise ArgumentError, 'count of hex numbers must be positive' if count < 0
6
- %x{
7
- count = Math.floor(count);
8
- var repeat = Math.floor(count / 6),
9
- remain = count % 6,
10
- remain_total = remain * 2,
11
- string = '',
12
- temp;
13
- for (var i = 0; i < repeat; i++) {
14
- // parseInt('ff'.repeat(6), 16) == 281474976710655
15
- temp = Math.floor(Math.random() * 281474976710655).toString(16);
16
- if (temp.length < 12) {
17
- // account for leading zeros gone missing
18
- temp = '0'.repeat(12 - temp.length) + temp;
19
- }
20
- string = string + temp;
21
- }
22
- if (remain > 0) {
23
- temp = Math.floor(Math.random()*parseInt('ff'.repeat(remain), 16)).toString(16);
24
- if (temp.length < remain_total) {
25
- // account for leading zeros gone missing
26
- temp = '0'.repeat(remain_total - temp.length) + temp;
2
+ extend Random::Formatter
3
+
4
+ %x{
5
+ var gen_random_bytes;
6
+
7
+ if ((Opal.global.crypto && Opal.global.crypto.getRandomValues) ||
8
+ (Opal.global.msCrypto && Opal.global.msCrypto.getRandomValues)) {
9
+ // This method is available in all non-ancient web browsers.
10
+
11
+ var crypto = Opal.global.crypto || Opal.global.msCrypto;
12
+ gen_random_bytes = function(count) {
13
+ var storage = new Uint8Array(count);
14
+ crypto.getRandomValues(storage);
15
+ return storage;
16
+ };
17
+ }
18
+ else if (Opal.global.crypto && Opal.global.crypto.randomBytes) {
19
+ // This method is available in Node.js
20
+
21
+ gen_random_bytes = function(count) {
22
+ return Opal.global.crypto.randomBytes(count);
23
+ };
24
+ }
25
+ else {
26
+ // Let's dangerously polyfill this interface with our MersenneTwister
27
+ // xor native JS Math.random xor something about current time...
28
+ // That's hardly secure, but the following warning should provide a person
29
+ // deploying the code a good idea on what he should do to make his deployment
30
+ // actually secure.
31
+ // It's possible to interface other libraries by adding an else if above if
32
+ // that's really desired.
33
+
34
+ #{warn 'Can\'t get a Crypto.getRandomValues interface or Crypto.randomBytes.' \
35
+ 'The random values generated with SecureRandom won\'t be ' \
36
+ 'cryptographically secure'}
37
+
38
+ gen_random_bytes = function(count) {
39
+ var storage = new Uint8Array(count);
40
+ for (var i = 0; i < count; i++) {
41
+ storage[i] = #{rand(0xff)} ^ Math.floor(Math.random() * 256);
42
+ storage[i] ^= +(new Date())>>#{rand(0xff)}&0xff;
27
43
  }
28
- string = string + temp;
44
+ return storage;
29
45
  }
30
- return string;
31
46
  }
32
- end
47
+ }
33
48
 
34
- def self.uuid
35
- 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.gsub(/[xy]/) do |ch,|
36
- %x{
37
- var r = Math.random() * 16 | 0,
38
- v = ch == "x" ? r : (r & 3 | 8);
49
+ def self.bytes(bytes = nil)
50
+ gen_random(bytes)
51
+ end
39
52
 
40
- return v.toString(16);
53
+ def self.gen_random(count = nil)
54
+ count = Random._verify_count(count)
55
+ out = ''
56
+ %x{
57
+ var bytes = gen_random_bytes(#{count});
58
+ for (var i = 0; i < #{count}; i++) {
59
+ out += String.fromCharCode(bytes[i]);
41
60
  }
42
- end
61
+ }
62
+ out.encode('ASCII-8BIT')
43
63
  end
44
64
  end
data/tasks/releasing.rake CHANGED
@@ -49,7 +49,7 @@ CHANGELOG_HEADING = <<~MARKDOWN
49
49
  - **Security** to invite users to upgrade in case of vulnerabilities.
50
50
  MARKDOWN
51
51
 
52
- desc "Update CHANGELOG.md usign info from published GitHub releases (the first unreleased section is preserved)"
52
+ desc "Update CHANGELOG.md using info from published GitHub releases (the first unreleased section is preserved)"
53
53
  task :changelog do
54
54
  changelog_path = "#{__dir__}/../CHANGELOG.md"
55
55
  unreleased_path = "#{__dir__}/../UNRELEASED.md"
data/tasks/testing.rake CHANGED
@@ -113,6 +113,7 @@ module Testing
113
113
 
114
114
  require 'spec_helper'
115
115
  require 'opal/full'
116
+ require 'securerandom'
116
117
  #{enter_benchmarking_mode}
117
118
 
118
119
  #{filter_requires}
@@ -354,13 +355,14 @@ task :minitest_node_nodejs do
354
355
  files = %w[
355
356
  nodejs
356
357
  opal-parser
357
- nodejs/test_file.rb
358
358
  nodejs/test_dir.rb
359
359
  nodejs/test_env.rb
360
- nodejs/test_io.rb
361
360
  nodejs/test_error.rb
361
+ nodejs/test_file.rb
362
362
  nodejs/test_file_encoding.rb
363
+ nodejs/test_io.rb
363
364
  nodejs/test_opal_builder.rb
365
+ nodejs/test_string.rb
364
366
  ]
365
367
 
366
368
  filename = "tmp/minitest_node_nodejs.rb"
@@ -369,8 +371,9 @@ task :minitest_node_nodejs do
369
371
  stubs = "-soptparse -sio/console -stimeout -smutex_m -srubygems -stempfile -smonitor"
370
372
  includes = "-Itest -Ilib -Ivendored-minitest"
371
373
 
374
+ use_strict_opt = ENV['USE_STRICT'] ? ' --use-strict' : ''
372
375
  sh "ruby -rbundler/setup "\
373
- "exe/opal #{includes} #{stubs} -R#{platform} -Dwarning -A --enable-source-location #{filename}"
376
+ "exe/opal #{includes} #{stubs} -R#{platform} -Dwarning -A --enable-source-location#{use_strict_opt} #{filename}"
374
377
  end
375
378
 
376
379
  desc 'Run smoke tests with opal-rspec to see if something is broken'
@@ -464,4 +467,3 @@ task :test_all => [:rspec, :mspec, :minitest]
464
467
  task(:cruby_tests) { warn "The task 'cruby_tests' has been renamed to 'minitest_cruby_nodejs'."; exit 1 }
465
468
  task(:test_cruby) { warn "The task 'test_cruby' has been renamed to 'minitest_cruby_nodejs'."; exit 1 }
466
469
  task(:test_nodejs) { warn "The task 'test_nodejs' has been renamed to 'minitest_node_nodejs'."; exit 1 }
467
-
@@ -0,0 +1,25 @@
1
+ require 'test/unit'
2
+ require 'nodejs'
3
+
4
+ class TestString < Test::Unit::TestCase
5
+ def test_should_get_bytes
6
+ assert_equal('foo'.bytesize, 3)
7
+ assert_equal('foo'.each_byte.to_a, [102, 111, 111])
8
+ assert_equal('foo'.bytes, [102, 111, 111])
9
+ assert_equal('foo'.bytes, [102, 111, 111])
10
+ end
11
+
12
+ def test_frozen
13
+ # Commented out examples somehow work in the strict mode,
14
+ # but otherwise they don't. I have unfortunately no idea
15
+ # on where it's mangled, but then I don't see it really
16
+ # as a really harmful thing.
17
+
18
+ #assert_equal((-'x').frozen?, true)
19
+ assert_equal((+'x').frozen?, false)
20
+ #assert_equal((-+-'x').frozen?, true)
21
+ assert_equal((+-+'x').frozen?, false)
22
+ #assert_equal((`'x'`).frozen?, true)
23
+ assert_equal((+`'x'`).frozen?, false)
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ require 'test/unit'
2
+ require 'promise/v2'
3
+
4
+ class TestPromiseAlways < Test::Unit::TestCase
5
+ def test_calls_the_block_when_it_was_resolved
6
+ x = 42
7
+ PromiseV2.value(23)
8
+ .then { |v| x = v }
9
+ .always { |v| x = 2 }
10
+ .then { assert_equal(x, 2) }
11
+ end
12
+
13
+ def test_calls_the_block_when_it_was_rejected
14
+ x = 42
15
+ PromiseV2.error(23)
16
+ .rescue { |v| x = v }
17
+ .always { |v| x = 2 }
18
+ .always { assert_equal(x, 2) }
19
+ end
20
+
21
+ def test_acts_as_resolved
22
+ x = 42
23
+ PromiseV2.error(23)
24
+ .rescue { |v| x = v }
25
+ .always { x = 2 }
26
+ .then { x = 3 }
27
+ .always { assert_equal(x, 3) }
28
+ end
29
+
30
+ def test_can_be_called_multiple_times_on_resolved_promises
31
+ p = PromiseV2.value(2)
32
+ x = 1
33
+ ps = []
34
+ ps << p.then { x += 1 }
35
+ ps << p.fail { x += 2 }
36
+ ps << p.always { x += 3 }
37
+
38
+ PromiseV2.when(ps).always do
39
+ assert_equal(x, 5)
40
+ end
41
+ end
42
+
43
+ def test_can_be_called_multiple_times_on_rejected_promises
44
+ p = PromiseV2.error(2)
45
+ x = 1
46
+ ps = []
47
+ ps << p.then { x += 1 }.fail{}
48
+ ps << p.fail { x += 2 }
49
+ ps << p.always { x += 3 }.fail{}
50
+
51
+ PromiseV2.when(ps).then do
52
+ assert_equal(x, 6)
53
+ end
54
+ end
55
+
56
+ def test_raises_with_alwaysB_if_a_promise_has_already_been_chained
57
+ p = PromiseV2.new
58
+
59
+ p.then! {}
60
+
61
+ assert_raise(ArgumentError) { p.always! {} }
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'promise/v2'
3
+
4
+ class TestPromiseError < Test::Unit::TestCase
5
+ def test_rejects_the_promise_with_the_given_error
6
+ assert_equal(PromiseV2.error(23).error, 23)
7
+ end
8
+
9
+ def test_marks_the_promise_as_realized
10
+ assert_equal(PromiseV2.error(23).realized?, true)
11
+ end
12
+
13
+ def test_marks_the_promise_as_rejected
14
+ assert_equal(PromiseV2.error(23).rejected?, true)
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ require 'test/unit'
2
+ require 'promise/v2'
3
+
4
+ class TestPromiseRescue < Test::Unit::TestCase
5
+ def test_calls_the_block_when_the_promise_has_already_been_rejected
6
+ x = 42
7
+ PromiseV2.error(23)
8
+ .rescue { |v| x = v }
9
+ .always { assert_equal(x, 23) }
10
+ end
11
+
12
+ def test_calls_the_block_when_the_promise_is_rejected
13
+ a = PromiseV2.new
14
+ x = 42
15
+
16
+ pr = a.rescue { |v| x = v }
17
+ a.reject(23)
18
+
19
+ pr.always { assert_equal(x, 23) }
20
+ end
21
+
22
+ def test_does_not_call_then_blocks_when_the_promise_is_rejected
23
+ x = 42
24
+ y = 23
25
+
26
+ PromiseV2.error(23).then { y = 42 }.rescue { |v| x = v }.always do
27
+ assert_equal(x, 23)
28
+ assert_equal(y, 23)
29
+ end
30
+ end
31
+
32
+ def test_does_not_call_subsequent_rescue_blocks
33
+ x = 42
34
+ PromiseV2.error(23).rescue { |v| x = v }.rescue { x = 42 }.always do
35
+ assert_equal(x, 23)
36
+ end
37
+ end
38
+
39
+ def test_can_be_called_multiple_times_on_the_same_promise
40
+ p = PromiseV2.error(2)
41
+ x = 1
42
+
43
+ ps = []
44
+
45
+ ps << p.then { x += 1 }.rescue{}
46
+ ps << p.rescue { x += 3 }
47
+ ps << p.rescue { x += 3 }
48
+
49
+ PromiseV2.when(ps).always { assert_equal(x, 7) }
50
+ end
51
+
52
+ def test_raises_with_rescueB_if_a_promise_has_already_been_chained
53
+ p = PromiseV2.new
54
+
55
+ p.then! {}
56
+
57
+ assert_raise(ArgumentError) { p.rescue! {} }
58
+ end
59
+ end
@@ -0,0 +1,90 @@
1
+ require 'test/unit'
2
+ require 'promise/v2'
3
+
4
+ class TestPromiseThen < Test::Unit::TestCase
5
+ def test_calls_the_block_when_the_promise_has_already_been_resolved
6
+ x = 42
7
+ PromiseV2.value(23)
8
+ .then { |v| x = v }
9
+ .always { assert_equal(x, 23) }
10
+ end
11
+
12
+ def test_calls_the_block_when_the_promise_is_resolved
13
+ a = PromiseV2.new
14
+ x = 42
15
+
16
+ p = a.then { |v| x = v }
17
+ a.resolve(23)
18
+
19
+ p.always { assert_equal(x, 23) }
20
+ end
21
+
22
+ def test_works_with_multiple_chains
23
+ x = 42
24
+ PromiseV2.value(2)
25
+ .then { |v| v * 2 }
26
+ .then { |v| v * 4 }
27
+ .then { |v| x = v }
28
+ .always { assert_equal(x, 16) }
29
+ end
30
+
31
+ def test_works_when_a_block_returns_a_promise
32
+ a = PromiseV2.new
33
+ b = PromiseV2.new
34
+
35
+ x = 42
36
+ p = a.then { b }.then { |v| x = v }
37
+
38
+ a.resolve(42)
39
+ b.resolve(23)
40
+
41
+ p.always { assert_equal(x, 23) }
42
+ end
43
+
44
+ def test_sends_raised_exceptions_as_rejections
45
+ x = nil
46
+
47
+ PromiseV2.value(2)
48
+ .then { raise "hue" }
49
+ .rescue { |v| x = v }
50
+ .always { assert_equal(x.class, RuntimeError) }
51
+ end
52
+
53
+ def test_sends_raised_exceptions_inside_rescue_blocks_as_next_errors
54
+ x = nil
55
+
56
+ PromiseV2.value(2)
57
+ .then { raise "hue" }
58
+ .rescue { raise "omg" }
59
+ .rescue { |v| x = v }
60
+ .always { assert_equal(x.class, RuntimeError) }
61
+ end
62
+
63
+ def test_allows_then_to_be_called_multiple_times
64
+ pr = PromiseV2.value(2)
65
+ x = 1
66
+
67
+ ps = []
68
+
69
+ ps << pr.then { x += 1 }
70
+ ps << pr.then { x += 1 }
71
+
72
+ PromiseV2.when(ps).always { assert_equal(x, 3) }
73
+ end
74
+
75
+ def test_raises_with_thenB_if_a_promise_has_already_been_chained
76
+ pr = PromiseV2.new
77
+
78
+ pr.then! {}
79
+
80
+ assert_raise(ArgumentError) { pr.then! {} }
81
+ end
82
+
83
+ def test_should_pass_a_delayed_falsy_value
84
+ pr = PromiseV2.new.resolve(5).then { nil }
85
+
86
+ pr.always do |value|
87
+ assert_equal(value, nil)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,52 @@
1
+ require 'test/unit'
2
+ require 'promise/v2'
3
+
4
+ class TestPromiseTrace < Test::Unit::TestCase
5
+ def test_calls_the_block_with_all_the_previous_results
6
+ x = 42
7
+
8
+ PromiseV2.value(1)
9
+ .then { 2 }
10
+ .then { 3 }
11
+ .trace {|a, b, c| x = a + b + c }
12
+ .always { assert_equal(x, 6) }
13
+ end
14
+
15
+ def test_calls_the_then_after_the_trace
16
+ x = 42
17
+
18
+ PromiseV2.value(1)
19
+ .then { 2 }
20
+ .then { 3 }
21
+ .trace { |a, b, c| a + b + c }
22
+ .then { |v| x = v }
23
+ .always { assert_equal(x, 6) }
24
+ end
25
+
26
+ def test_includes_the_first_value
27
+ x = 42
28
+
29
+ PromiseV2.value(1)
30
+ .trace { |a| x = a }
31
+ .always { assert_equal(x, 1) }
32
+ end
33
+
34
+ def test_works_after_a_when
35
+ x = 42
36
+
37
+ PromiseV2.value(1).then {
38
+ PromiseV2.when PromiseV2.value(2), PromiseV2.value(3)
39
+ }.trace {|a, b|
40
+ #x = a + b[0] + b[1]
41
+ x = "#{a},#{b}"
42
+ }.always { assert_equal(x, "1,native") } # 6
43
+ end
44
+
45
+ def test_raises_with_traceB_if_a_promise_has_already_been_chained
46
+ p = PromiseV2.new
47
+
48
+ p.then! {}
49
+
50
+ assert_raise(ArgumentError) { p.trace! {} }
51
+ end
52
+ end