PageRenderTime 47ms CodeModel.GetById 18ms app.highlight 24ms RepoModel.GetById 2ms app.codeStats 0ms

/test/operators.coffee

http://github.com/jashkenas/coffee-script
CoffeeScript | 453 lines | 339 code | 85 blank | 29 comment | 23 complexity | 4fa7985a93f13778c1890fadae2eaf4f MD5 | raw file
  1# Operators
  2# ---------
  3
  4# * Operators
  5# * Existential Operator (Binary)
  6# * Existential Operator (Unary)
  7# * Aliased Operators
  8# * [not] in/of
  9# * Chained Comparison
 10
 11test "binary (2-ary) math operators do not require spaces", ->
 12  a = 1
 13  b = -1
 14  eq +1, a*-b
 15  eq -1, a*+b
 16  eq +1, a/-b
 17  eq -1, a/+b
 18
 19test "operators should respect new lines as spaced", ->
 20  a = 123 +
 21  456
 22  eq 579, a
 23
 24  b = "1#{2}3" +
 25  "456"
 26  eq '123456', b
 27
 28test "multiple operators should space themselves", ->
 29  eq (+ +1), (- -1)
 30
 31test "compound operators on successive lines", ->
 32  a = 1
 33  a +=
 34  1
 35  eq a, 2
 36
 37test "bitwise operators", ->
 38  eq  2, (10 &   3)
 39  eq 11, (10 |   3)
 40  eq  9, (10 ^   3)
 41  eq 80, (10 <<  3)
 42  eq  1, (10 >>  3)
 43  eq  1, (10 >>> 3)
 44  num = 10; eq  2, (num &=   3)
 45  num = 10; eq 11, (num |=   3)
 46  num = 10; eq  9, (num ^=   3)
 47  num = 10; eq 80, (num <<=  3)
 48  num = 10; eq  1, (num >>=  3)
 49  num = 10; eq  1, (num >>>= 3)
 50
 51test "`instanceof`", ->
 52  ok new String instanceof String
 53  ok new Boolean instanceof Boolean
 54  # `instanceof` supports negation by prefixing the operator with `not`
 55  ok new Number not instanceof String
 56  ok new Array not instanceof Boolean
 57
 58test "use `::` operator on keywords `this` and `@`", ->
 59  nonce = {}
 60  obj =
 61    withAt:   -> @::prop
 62    withThis: -> this::prop
 63  obj.prototype = prop: nonce
 64  eq nonce, obj.withAt()
 65  eq nonce, obj.withThis()
 66
 67
 68# Existential Operator (Binary)
 69
 70test "binary existential operator", ->
 71  nonce = {}
 72
 73  b = a ? nonce
 74  eq nonce, b
 75
 76  a = null
 77  b = undefined
 78  b = a ? nonce
 79  eq nonce, b
 80
 81  a = false
 82  b = a ? nonce
 83  eq false, b
 84
 85  a = 0
 86  b = a ? nonce
 87  eq 0, b
 88
 89test "binary existential operator conditionally evaluates second operand", ->
 90  i = 1
 91  func = -> i -= 1
 92  result = func() ? func()
 93  eq result, 0
 94
 95test "binary existential operator with negative number", ->
 96  a = null ? - 1
 97  eq -1, a
 98
 99
100# Existential Operator (Unary)
101
102test "postfix existential operator", ->
103  ok (if nonexistent? then false else true)
104  defined = true
105  ok defined?
106  defined = false
107  ok defined?
108
109test "postfix existential operator only evaluates its operand once", ->
110  semaphore = 0
111  fn = ->
112    ok false if semaphore
113    ++semaphore
114  ok(if fn()? then true else false)
115
116test "negated postfix existential operator", ->
117  ok !nothing?.value
118
119test "postfix existential operator on expressions", ->
120  eq true, (1 or 0)?, true
121
122
123# `is`,`isnt`,`==`,`!=`
124
125test "`==` and `is` should be interchangeable", ->
126  a = b = 1
127  ok a is 1 and b == 1
128  ok a == b
129  ok a is b
130
131test "`!=` and `isnt` should be interchangeable", ->
132  a = 0
133  b = 1
134  ok a isnt 1 and b != 0
135  ok a != b
136  ok a isnt b
137
138
139# [not] in/of
140
141# - `in` should check if an array contains a value using `indexOf`
142# - `of` should check if a property is defined on an object using `in`
143test "in, of", ->
144  arr = [1]
145  ok 0 of arr
146  ok 1 in arr
147  # prefixing `not` to `in and `of` should negate them
148  ok 1 not of arr
149  ok 0 not in arr
150
151test "`in` should be able to operate on an array literal", ->
152  ok 2 in [0, 1, 2, 3]
153  ok 4 not in [0, 1, 2, 3]
154  arr = [0, 1, 2, 3]
155  ok 2 in arr
156  ok 4 not in arr
157  # should cache the value used to test the array
158  arr = [0]
159  val = 0
160  ok val++ in arr
161  ok val++ not in arr
162  val = 0
163  ok val++ of arr
164  ok val++ not of arr
165
166test "`of` and `in` should be able to operate on instance variables", ->
167  obj = {
168    list: [2,3]
169    in_list: (value) -> value in @list
170    not_in_list: (value) -> value not in @list
171    of_list: (value) -> value of @list
172    not_of_list: (value) -> value not of @list
173  }
174  ok obj.in_list 3
175  ok obj.not_in_list 1
176  ok obj.of_list 0
177  ok obj.not_of_list 2
178
179test "#???: `in` with cache and `__indexOf` should work in argument lists", ->
180  eq 1, [Object() in Array()].length
181
182test "#737: `in` should have higher precedence than logical operators", ->
183  eq 1, 1 in [1] and 1
184
185test "#768: `in` should preserve evaluation order", ->
186  share = 0
187  a = -> share++ if share is 0
188  b = -> share++ if share is 1
189  c = -> share++ if share is 2
190  ok a() not in [b(),c()]
191  eq 3, share
192
193test "#1099: empty array after `in` should compile to `false`", ->
194  eq 1, [5 in []].length
195  eq false, do -> return 0 in []
196
197test "#1354: optimized `in` checks should not happen when splats are present", ->
198  a = [6, 9]
199  eq 9 in [3, a...], true
200
201test "#1100: precedence in or-test compilation of `in`", ->
202  ok 0 in [1 and 0]
203  ok 0 in [1, 1 and 0]
204  ok not (0 in [1, 0 or 1])
205
206test "#1630: `in` should check `hasOwnProperty`", ->
207  ok undefined not in length: 1
208
209test "#1714: lexer bug with raw range `for` followed by `in`", ->
210  0 for [1..2]
211  ok not ('a' in ['b'])
212
213  0 for [1..2]; ok not ('a' in ['b'])
214
215  0 for [1..10] # comment ending
216  ok not ('a' in ['b'])
217
218  # lexer state (specifically @seenFor) should be reset before each compilation
219  CoffeeScript.compile "0 for [1..2]"
220  CoffeeScript.compile "'a' in ['b']"
221
222test "#1099: statically determined `not in []` reporting incorrect result", ->
223  ok 0 not in []
224
225test "#1099: make sure expression tested gets evaluted when array is empty", ->
226  a = 0
227  (do -> a = 1) in []
228  eq a, 1
229
230# Chained Comparison
231
232test "chainable operators", ->
233  ok 100 > 10 > 1 > 0 > -1
234  ok -1 < 0 < 1 < 10 < 100
235
236test "`is` and `isnt` may be chained", ->
237  ok true is not false is true is not false
238  ok 0 is 0 isnt 1 is 1
239
240test "different comparison operators (`>`,`<`,`is`,etc.) may be combined", ->
241  ok 1 < 2 > 1
242  ok 10 < 20 > 2+3 is 5
243
244test "some chainable operators can be negated by `unless`", ->
245  ok (true unless 0==10!=100)
246
247test "operator precedence: `|` lower than `<`", ->
248  eq 1, 1 | 2 < 3 < 4
249
250test "preserve references", ->
251  a = b = c = 1
252  # `a == b <= c` should become `a === b && b <= c`
253  # (this test does not seem to test for this)
254  ok a == b <= c
255
256test "chained operations should evaluate each value only once", ->
257  a = 0
258  ok 1 > a++ < 1
259
260test "#891: incorrect inversion of chained comparisons", ->
261  ok (true unless 0 > 1 > 2)
262  ok (true unless (this.NaN = 0/0) < 0/0 < this.NaN)
263
264test "#1234: Applying a splat to :: applies the splat to the wrong object", ->
265  nonce = {}
266  class C
267    method: -> @nonce
268    nonce: nonce
269
270  arr = []
271  eq nonce, C::method arr... # should be applied to `C::`
272
273test "#1102: String literal prevents line continuation", ->
274  eq "': '", '' +
275     "': '"
276
277test "#1703, ---x is invalid JS", ->
278  x = 2
279  eq (- --x), -1
280
281test "Regression with implicit calls against an indented assignment", ->
282  eq 1, a =
283    1
284
285  eq a, 1
286
287test "#2155 ... conditional assignment to a closure", ->
288  x = null
289  func = -> x ?= (-> if true then 'hi')
290  func()
291  eq x(), 'hi'
292
293test "#2197: Existential existential double trouble", ->
294  counter = 0
295  func = -> counter++
296  func()? ? 100
297  eq counter, 1
298
299test "#2567: Optimization of negated existential produces correct result", ->
300  a = 1
301  ok !(!a?)
302  ok !b?
303
304test "#2508: Existential access of the prototype", ->
305  eq NonExistent?::nothing, undefined
306  eq(
307    NonExistent
308    ?::nothing
309    undefined
310  )
311  ok Object?::toString
312  ok(
313    Object
314    ?::toString
315  )
316
317test "floor division operator", ->
318  eq 2, 7 // 3
319  eq -3, -7 // 3
320  eq NaN, 0 // 0
321
322test "floor division operator compound assignment", ->
323  a = 7
324  a //= 1 + 1
325  eq 3, a
326
327test "modulo operator", ->
328  check = (a, b, expected) ->
329    eq expected, a %% b, "expected #{a} %%%% #{b} to be #{expected}"
330  check 0, 1, 0
331  check 0, -1, -0
332  check 1, 0, NaN
333  check 1, 2, 1
334  check 1, -2, -1
335  check 1, 3, 1
336  check 2, 3, 2
337  check 3, 3, 0
338  check 4, 3, 1
339  check -1, 3, 2
340  check -2, 3, 1
341  check -3, 3, 0
342  check -4, 3, 2
343  check 5.5, 2.5, 0.5
344  check -5.5, 2.5, 2.0
345
346test "modulo operator compound assignment", ->
347  a = -2
348  a %%= 5
349  eq 3, a
350
351test "modulo operator converts arguments to numbers", ->
352  eq 1, 1 %% '42'
353  eq 1, '1' %% 42
354  eq 1, '1' %% '42'
355
356test "#3361: Modulo operator coerces right operand once", ->
357  count = 0
358  res = 42 %% valueOf: -> count += 1
359  eq 1, count
360  eq 0, res
361
362test "#3363: Modulo operator coercing order", ->
363  count = 2
364  a = valueOf: -> count *= 2
365  b = valueOf: -> count += 1
366  eq 4, a %% b
367  eq 5, count
368
369test "#3598: Unary + and - coerce the operand once when it is an identifier", ->
370  # Unary + and - do not generate `_ref`s when the operand is a number, for
371  # readability. To make sure that they do when the operand is an identifier,
372  # test that they are consistent with another unary operator as well as another
373  # complex expression.
374  # Tip: Making one of the tests temporarily fail lets you easily inspect the
375  # compiled JavaScript.
376
377  assertOneCoercion = (fn) ->
378    count = 0
379    value = valueOf: -> count++; 1
380    fn value
381    eq 1, count
382
383  eq 1, 1 ? 0
384  eq 1, +1 ? 0
385  eq -1, -1 ? 0
386  assertOneCoercion (a) ->
387    eq 1, +a ? 0
388  assertOneCoercion (a) ->
389    eq -1, -a ? 0
390  assertOneCoercion (a) ->
391    eq -2, ~a ? 0
392  assertOneCoercion (a) ->
393    eq 0.5, a / 2 ? 0
394
395  ok -2 <= 1 < 2
396  ok -2 <= +1 < 2
397  ok -2 <= -1 < 2
398  assertOneCoercion (a) ->
399    ok -2 <= +a < 2
400  assertOneCoercion (a) ->
401    ok -2 <= -a < 2
402  assertOneCoercion (a) ->
403    ok -2 <= ~a < 2
404  assertOneCoercion (a) ->
405    ok -2 <= a / 2 < 2
406
407  arrayEq [0], (n for n in [0] by 1)
408  arrayEq [0], (n for n in [0] by +1)
409  arrayEq [0], (n for n in [0] by -1)
410  assertOneCoercion (a) ->
411    arrayEq [0], (n for n in [0] by +a)
412  assertOneCoercion (a) ->
413    arrayEq [0], (n for n in [0] by -a)
414  assertOneCoercion (a) ->
415    arrayEq [0], (n for n in [0] by ~a)
416  assertOneCoercion (a) ->
417    arrayEq [0], (n for n in [0] by a * 2 / 2)
418
419  ok 1 in [0, 1]
420  ok +1 in [0, 1]
421  ok -1 in [0, -1]
422  assertOneCoercion (a) ->
423    ok +a in [0, 1]
424  assertOneCoercion (a) ->
425    ok -a in [0, -1]
426  assertOneCoercion (a) ->
427    ok ~a in [0, -2]
428  assertOneCoercion (a) ->
429    ok a / 2 in [0, 0.5]
430
431test "'new' target", ->
432  nonce = {}
433  ctor  = -> nonce
434
435  eq (new ctor), nonce
436  eq (new ctor()), nonce
437
438  ok new class
439
440  ctor  = class
441  ok (new ctor) instanceof ctor
442  ok (new ctor()) instanceof ctor
443
444  # Force an executable class body
445  ctor  = class then a = 1
446  ok (new ctor) instanceof ctor
447
448  get   = -> ctor
449  ok (new get()) not instanceof ctor
450  ok (new (get())()) instanceof ctor
451
452  # classes must be called with `new`. In this case `new` applies to `get` only
453  throws -> new get()()