git.fiddlerwoaroof.com
src/genfuns.test.js
cc733b08
 import * as uut from "./genfuns";
 import { fail } from "assert";
 
 describe("matches_specializer", () => {
   test("works in expected cases", () => {
     function AThing() {}
     const an_instance = new AThing();
 
     expect(uut.matches_specializer(an_instance, AThing)).toBeTruthy();
     expect(uut.matches_specializer(an_instance, String)).toBeFalsy();
     expect(uut.matches_specializer(an_instance, Object)).toBeTruthy();
 
     expect(uut.matches_specializer([], Array)).toBeTruthy();
     expect(uut.matches_specializer([], Object)).toBeTruthy();
     expect(uut.matches_specializer([], Number)).toBeFalsy();
 
     function Foo() {}
     Foo.prototype = Object.create(null);
     const inst = new Foo();
     expect(uut.matches_specializer(inst, Foo)).toBeTruthy();
     expect(uut.matches_specializer(inst, Object)).toBeFalsy();
 
     expect(uut.matches_specializer({ a: 1 }, uut.Shape("a"))).toBeTruthy();
     expect(
       uut.matches_specializer({ a: 1, b: 2 }, uut.Shape("a"))
     ).toBeTruthy();
     expect(uut.matches_specializer({ b: 2 }, uut.Shape("a"))).toBeFalsy();
 
     expect(
       uut.matches_specializer({ a: 1, b: 2, c: 3 }, uut.Shape("a", "b", "c"))
     ).toBeTruthy();
     expect(
       uut.matches_specializer(
         { a: 1, b: 2, c: 3, d: 4 },
         uut.Shape("a", "b", "c")
       )
     ).toBeTruthy();
     expect(
       uut.matches_specializer({ a: 1, c: 3 }, uut.Shape("a", "b", "c"))
     ).toBeFalsy();
     expect(
       uut.matches_specializer({ c: 3 }, uut.Shape("a", "b", "c"))
     ).toBeFalsy();
     expect(
       uut.matches_specializer({ d: 3 }, uut.Shape("a", "b", "c"))
     ).toBeFalsy();
   });
 
   test("null behavior", () => {
     expect(uut.matches_specializer(null, null)).toBeTruthy();
     expect(uut.matches_specializer(null, Number)).toBeFalsy();
     expect(uut.matches_specializer(null, String)).toBeFalsy();
     expect(uut.matches_specializer(null, Object)).toBeFalsy();
   });
 
   test("undefined (the value) behavior", () => {
     expect(uut.matches_specializer(undefined, undefined)).toBeTruthy();
     expect(uut.matches_specializer(undefined, Number)).toBeFalsy();
     expect(uut.matches_specializer(undefined, String)).toBeFalsy();
     expect(uut.matches_specializer(undefined, Object)).toBeFalsy();
   });
 
   test("works for numbers", () => {
     expect(uut.matches_specializer(new Number(1), Number)).toBeTruthy();
     expect(uut.matches_specializer(new Number(1), Object)).toBeTruthy();
     expect(uut.matches_specializer(new Number(1), String)).toBeFalsy();
 
     expect(uut.matches_specializer(1, Number)).toBeTruthy();
     expect(uut.matches_specializer(1, Object)).toBeTruthy();
     expect(uut.matches_specializer(1, String)).toBeFalsy();
   });
 
   test("handles strings", () => {
     expect(uut.matches_specializer(new String("foobar"), String)).toBeTruthy();
     expect(uut.matches_specializer(new String("foobar"), Object)).toBeTruthy();
 
     expect(uut.matches_specializer("1", String)).toBeTruthy();
     expect(uut.matches_specializer("1", Object)).toBeTruthy();
     expect(uut.matches_specializer("1", Number)).toBeFalsy();
   });
94944213
 });
 
cc733b08
 describe("defgeneric", () => {
   test("methods get called appropriately", () => {
     expect(
       uut
         .defgeneric("testing1", "a", "b")
         .primary([Object, Object], (_, __) => 1)
         .fn(1, 2)
     ).toEqual(1);
 
     try {
       uut
         .defgeneric("foobar", "a")
         .primary([String], function (a) {})
         .fn({});
       fail();
     } catch (err) {
       expect(err).toBeInstanceOf(uut.NoApplicableMethodError);
     }
 
     expect(
       uut
         .defgeneric("testing1", "a", "b")
         .primary([Number, Number], (_, __) => 1)
         .fn(1, 2)
     ).toEqual(1);
 
     expect(
       uut
         .defgeneric("testing1", "a", "b")
         .primary([Number, Number], (_, __) => 2)
         .primary([String, String], (_, __) => 1)
         .fn("1", "2")
     ).toEqual(1);
 
     let firstCounts = 0;
     expect(
       uut
         .defgeneric("testing1", "a", "b")
         .primary([Number, Number], (_, __) => (firstCounts += 1))
         .primary([String, String], (_, __) => (firstCounts += 1))
         .fn("1", "2")
     ).toEqual(1);
     expect(firstCounts).toEqual(1);
 
     let secondCounts = 0;
     expect(
       uut
         .defgeneric("testing1", "a", "b")
         .primary([Object, Object], (_, __) => (secondCounts += 1))
         .primary([String, String], (_, __) => (secondCounts += 1))
         .fn("1", "2")
     ).toEqual(1);
     expect(secondCounts).toEqual(1);
 
     let thirdCounts = 0;
     expect(
       uut
         .defgeneric("testing1", "a", "b")
         .before([Object, Object], (_, __) => (thirdCounts += 1))
         .primary([String, String], (_, __) => "hi")
         .after([Object, String], (_, __) => (thirdCounts += 1))
         .fn("1", "2")
     ).toEqual("hi");
     expect(thirdCounts).toEqual(2);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([Object], function (a) {
           return 1;
         })
         .primary([String], function (a) {
           return 2;
         })
         .fn("foobar")
     ).toEqual(2);
   });
 
   test("next-method-p works", () => {
     expect.assertions(3);
 
     uut
       .defgeneric("foobar", "a")
       .primary([Object], function (a) {
         expect(this.next_method_p).toBe(false);
       })
       .fn({});
 
     uut
       .defgeneric("foobar", "a")
       .primary([Object], function (a) {
         expect(this.next_method_p).toBe(false);
       })
       .primary([String], function (a) {
         expect(this.next_method_p).toBe(true);
       })
       .fn("foobar");
 
     uut
       .defgeneric("foobar", "a")
       .primary([Object], function (a) {
         expect(this.next_method_p).toBe(false);
       })
       .primary([String], function (a) {
         expect(this.next_method_p).toBe(true);
       })
       .fn(1);
   });
 
   test("call-next-method works", () => {
     try {
       uut
         .defgeneric("foobar", "a")
         .primary([Object], function (a) {
           this.call_next_method();
         })
         .primary([String], function (a) {
           return 1;
         })
         .fn({});
       fail();
     } catch (err) {
       expect(err).toBeInstanceOf(uut.NoNextMethodError);
     }
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([Object], function (a) {
           return 1;
         })
         .primary([String], function (a) {
           return this.call_next_method();
         })
         .fn("foobar")
     ).toEqual(1);
 
     expect(
       uut
         .defgeneric("foobar", "a", "b")
         .primary([String, String], function (a, b) {
           return `1${this.call_next_method()}`;
         })
         .primary([Object, String], function (a, b) {
           return `3${this.call_next_method()}`;
         })
         .primary([String, Object], function (a, b) {
           return `2${this.call_next_method()}`;
         })
         .primary([Object, Object], function (a, b) {
           return `4`;
         })
         .fn("a", "b")
     ).toEqual("1234");
 
     try {
       uut
         .defgeneric("foobar", "a")
         .primary([Object], function (a) {
           this.call_next_method();
         })
         .fn({});
       fail();
     } catch (err) {
       expect(err).toBeInstanceOf(uut.NoNextMethodError);
     }
   });
406b5587
 });
 
cc733b08
 describe("custom specializers", () => {
   test("Shape works", () => {
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape("a", "b")], ({ a, b }) => a + b)
         .primary([Object], _ => null)
         .fn({ a: 1, b: 2 })
     ).toEqual(3);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape("a", "b")], ({ a, b }) => a + b)
         .primary([Object], _ => null)
         .fn({ a: 1, b: 2, c: 3 })
     ).toEqual(3);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape("a", "b")], ({ a, b }) => a + b)
         .primary([Object], _ => null)
         .fn({ a: 1 })
     ).toEqual(null);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape(["a", 1], "b")], ({ a, b }) => a + b)
         .primary([Object], _ => null)
         .fn({ a: 1, b: 3 })
     ).toEqual(4);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape(["a", null], "b")], ({ a, b }) => b)
         .primary([Object], _ => null)
         .fn({ a: null, b: 3 })
     ).toEqual(3);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape(["a", undefined], "b")], ({ a, b }) => b)
         .primary([Object], _ => null)
         .fn({ b: 5 })
     ).toEqual(null); //undefined is not a permissible default: treated as if the key is missing
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape(["a", 1], "b")], ({ a, b }) => a + b)
         .primary([Object], _ => null)
         .fn({ a: 2, b: 3 })
     ).toEqual(null);
   });
 
   test("Shape, prototype precedence", () => {
     expect(
       uut
         .defgeneric("foobar4", "a")
         .primary([uut.Shape("a")], ({ a }) => a)
         .primary([uut.Shape("a", "b")], ({ a, b }) => {
           return a + b;
         })
         .primary([Object], _ => null)
         .fn({ a: 1, b: 3 })
     ).toEqual(4);
 
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape("a", "b")], ({ a, b }) => a + b)
         .primary([uut.Shape("b")], ({ b }) => b)
         .primary([Object], _ => null)
         .fn({ a: 1, b: 2 })
     ).toEqual(3);
 
     const Foo = function () {};
     Foo.prototype = { a: true, b: null };
     expect(
       uut
         .defgeneric("foobar", "a")
         .primary([uut.Shape("a")], function ({ a }) {
           return `a${this.call_next_method()}`;
         })
         .primary([uut.Shape("a", "b", "c")], function ({ a, b, c }) {
           return `c${this.call_next_method()}`;
         })
         .primary([uut.Shape("a", "b")], function ({ a, b }) {
           return `b${this.call_next_method()}`;
         })
         .primary([Object], _ => "d")
         .fn(Object.assign(new Foo(), { c: 3 }))
     ).toEqual("cbad");
   });
406b5587
 });
94944213
 
406b5587
 describe("Shape", () => {
cc733b08
   test("super_of", () => {
     expect(uut.Shape().super_of(uut.Shape("a", "b", "c"))).toBeTruthy();
     expect(uut.Shape("a").super_of(uut.Shape("a", "b"))).toBeTruthy();
     expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", "c"))).toBeTruthy();
     expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", 3))).toBeTruthy();
     expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b"))).toBeFalsy();
     expect(uut.Shape("a", "b").super_of(uut.Shape("a"))).toBeFalsy();
   });
636eaffe
 });