function assertEq(actual, expected) {
  if (expected !== actual) {
    throw "Expected '" + expected + "' but was '" + actual + "'";
  }
}

function assertException(exception, fn) {
  try {
    fn();
  } catch (e if e instanceof exception) {
    return;
  }
  throw "Expected to throw '" + exception + "' but not thrown";
}

var f = a => a * a;
assertEq(f(2), 4);
assertEq(f.toString(), '\na => a * a\n');

var f = (a) => a * a;
assertEq(f(2), 4);
assertEq(f.toString(), '\n(a) => a * a\n');

var f = (a, b) => a * b;
assertEq(f(2, 3), 6);

var f = (a, b, c) => a * b + c;
assertEq(f(2, 3, 4), 10);

var f = () => 10;
assertEq(f(), 10);

var f = () => 10 * 2;
assertEq(f(), 20);

assertException(SyntaxError, function() {
  eval('var f = ) => a * b;');
});

assertException(SyntaxError, function() {
  eval('var f = ();');
});

var f = ([a, b]) => a * b;
assertEq(f([2, 3]), 6);

var f = ({ a: a, b: b}) => a * b;
assertEq(f({ a: 10, b: 20 }), 200);

var f = a => {
  return a * a;
};
assertEq(f(10), 100);
assertEq(f.toString(), '\na => {\n    return a * a;\n}\n');

var f = a => {
  a * a;
};
assertEq(f(10), undefined);

var f = (a) => {
  return a * a;
};
assertEq(f(10), 100);

var f = (a) => {
  a * a;
};
assertEq(f(10), undefined);

var f = () => {
  return 10;
};
assertEq(f(), 10);

var f = () => {
  10;
};
assertEq(f(), undefined);

assertException(SyntaxError, function() {
  eval("function() { 'use strict'; var f = (a, a) => {} }");
});

assertException(SyntaxError, function() {
  eval("function() { 'use strict'; var f = (eval) => {} }");
});

assertException(SyntaxError, function() {
  eval("function() { 'use strict'; var f = (arguments) => {} }");
});

assertException(SyntaxError, function() {
  eval("function() { 'use strict'; var f = (123) => {} }");
});

assertException(SyntaxError, function() {
  eval("var f = () => { yield 10; };");
});

var o = {
  a: 1
};
(function() {
  var f = () => this;
  assertEq(f(), o);
  assertEq(f().a, 1);
}).call(o);

var o = {
  a: 1
};
var f = (function() {
  return () => this;
}).call(o);
assertEq(f(), o);
assertEq(f().a, 1);

var o = {};
(function() {
  var f = () => () => this;
  assertEq(f()(), o);
}).call(o);

var o1 = {}, o2 = {};
(function() {
  (function() {
    var f = () => this;
    assertEq(f(), o2);
  }).call(o2);
}).call(o1);

var o = {};
(function() {
  var f;
  if (String) {
    f = () => this;
  }
  assertEq(f(), o);
}).call(o);

var o = {};
(function() {
  var f;
  with (String) {
    f = () => this;
  }
  assertEq(f(), o);
}).call(o);

var f = (function() {
  return () => this.a + 3;
}).call({ a: 1 });
assertEq(f(), 4);

assertException(TypeError, function() {
  var f = () => 10;
  new f();
});

assertException(TypeError, function() {
  var f = () => 10, g = f.bind();
  new g();
});

var f = (function() {
  return () => arguments[0] + arguments[1];
})(1, 2);
assertEq(f(3, 4), 3);

var f = () => 10;
assertEq(f.prototype, undefined);

var f = () => {
  var n = 0;
  return () => n++;
};
var g = f();
assertEq(g(), 0);
assertEq(g(), 1);

"success";
