git.fiddlerwoaroof.com
src/genfuns.test.js
406b5587
 import * as uut from './genfuns';
636eaffe
 import { fail } from 'assert';
94944213
 
406b5587
 describe('matches_specializer', () => {
94944213
     test('works in expected cases', () => {
406b5587
         function AThing() { }
         const an_instance = new AThing();
94944213
 
406b5587
         expect(uut.matches_specializer(an_instance, AThing)).toBeTruthy();
         expect(uut.matches_specializer(an_instance, String)).toBeFalsy();
         expect(uut.matches_specializer(an_instance, Object)).toBeTruthy();
94944213
 
406b5587
         expect(uut.matches_specializer([], Array)).toBeTruthy();
         expect(uut.matches_specializer([], Object)).toBeTruthy();
         expect(uut.matches_specializer([], Number)).toBeFalsy();
94944213
 
5c93a70c
         function Foo() { }
94944213
         Foo.prototype = Object.create(null);
         const inst = new Foo();
406b5587
         expect(uut.matches_specializer(inst, Foo)).toBeTruthy();
         expect(uut.matches_specializer(inst, Object)).toBeFalsy();
 
5c93a70c
         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();
406b5587
 
5c93a70c
         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();
94944213
     });
 
406b5587
     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();
     });
94944213
 
406b5587
     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();
     });
94944213
 
406b5587
     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();
94944213
 
406b5587
         expect(uut.matches_specializer(1, Number)).toBeTruthy();
         expect(uut.matches_specializer(1, Object)).toBeTruthy();
         expect(uut.matches_specializer(1, String)).toBeFalsy();
94944213
     });
406b5587
 
     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
 });
 
 describe('defgeneric', () => {
     test('methods get called appropriately', () => {
         expect(
             uut.defgeneric("testing1", "a", "b")
                 .primary([Object, Object], (_, __) => 1)
5c93a70c
                 .fn(1, 2)
94944213
         ).toEqual(1);
 
636eaffe
         try {
406b5587
             uut.defgeneric("foobar", "a")
636eaffe
                 .primary([String], function (a) { })
                 .fn({});
             fail();
         } catch (err) {
             expect(err).toBeInstanceOf(uut.NoApplicableMethodError);
         }
406b5587
 
94944213
         expect(
             uut.defgeneric("testing1", "a", "b")
                 .primary([Number, Number], (_, __) => 1)
5c93a70c
                 .fn(1, 2)
94944213
         ).toEqual(1);
 
         expect(
             uut.defgeneric("testing1", "a", "b")
                 .primary([Number, Number], (_, __) => 2)
                 .primary([String, String], (_, __) => 1)
5c93a70c
                 .fn("1", "2")
94944213
         ).toEqual(1);
 
         let firstCounts = 0;
         expect(
             uut.defgeneric("testing1", "a", "b")
                 .primary([Number, Number], (_, __) => firstCounts += 1)
                 .primary([String, String], (_, __) => firstCounts += 1)
5c93a70c
                 .fn("1", "2")
94944213
         ).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)
5c93a70c
                 .fn("1", "2")
94944213
         ).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)
5c93a70c
                 .fn("1", "2")
94944213
         ).toEqual('hi');
         expect(thirdCounts).toEqual(2);
 
406b5587
         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', () => {
636eaffe
         try {
406b5587
             uut.defgeneric("foobar", "a")
                 .primary([Object], function (a) {
                     this.call_next_method();
                 })
                 .primary([String], function (a) {
                     return 1;
                 })
636eaffe
                 .fn({})
             fail();
         } catch (err) {
             expect(err).toBeInstanceOf(uut.NoNextMethodError);
         }
406b5587
 
         expect(
             uut.defgeneric("foobar", "a")
                 .primary([Object], function (a) {
                     return 1;
                 })
                 .primary([String], function (a) {
                     return this.call_next_method();
                 })
                 .fn("foobar")
         ).toEqual(1);
5c93a70c
 
406b5587
         expect(
             uut.defgeneric("foobar", "a", "b")
5c93a70c
                 .primary([String, String], function (a, b) {
406b5587
                     return `1${this.call_next_method()}`;
                 })
5c93a70c
                 .primary([Object, String], function (a, b) {
406b5587
                     return `3${this.call_next_method()}`;
                 })
5c93a70c
                 .primary([String, Object], function (a, b) {
406b5587
                     return `2${this.call_next_method()}`;
                 })
5c93a70c
                 .primary([Object, Object], function (a, b) {
406b5587
                     return `4`;
                 }).fn("a", "b")
         ).toEqual("1234");
636eaffe
 
         try {
             uut.defgeneric("foobar", "a")
                 .primary([Object], function (a) {
                     this.call_next_method();
                 })
                 .fn({});
             fail();
         } catch (err) {
             expect(err).toBeInstanceOf(uut.NoNextMethodError);
         }
 
406b5587
     });
 });
 
 describe('custom specializers', () => {
     test('Shape works', () => {
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b)
             .primary([Object], _ => null)
             .fn({ a: 1, b: 2 }))
406b5587
             .toEqual(3);
 
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b)
             .primary([Object], _ => null)
             .fn({ a: 1, b: 2, c: 3 }))
406b5587
             .toEqual(3);
 
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b)
             .primary([Object], _ => null)
             .fn({ a: 1 }))
406b5587
             .toEqual(null);
a00b5a50
 
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .primary([uut.Shape(['a', 1], 'b')], ({ a, b }) => a + b)
             .primary([Object], _ => null)
             .fn({ a: 1, b: 3 }))
a00b5a50
             .toEqual(4);
 
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .primary([uut.Shape(['a', null], 'b')], ({ a, b }) => b)
             .primary([Object], _ => null)
             .fn({ a: null, b: 3 }))
a00b5a50
             .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")
5c93a70c
             .primary([uut.Shape(['a', 1], 'b')], ({ a, b }) => a + b)
             .primary([Object], _ => null)
             .fn({ a: 2, b: 3 }))
a00b5a50
             .toEqual(null);
406b5587
     });
 
     test('Shape, prototype precedence', () => {
         expect(uut.defgeneric("foobar4", "a")
5c93a70c
             .primary([uut.Shape('a')], ({ a }) => a)
             .primary([uut.Shape('a', 'b')], ({ a, b }) => { return a + b })
             .primary([Object], _ => null).fn({ a: 1, b: 3 }))
406b5587
             .toEqual(4);
 
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b)
             .primary([uut.Shape('b')], ({ b }) => b)
             .primary([Object], _ => null)
             .fn({ a: 1, b: 2 }))
406b5587
             .toEqual(3);
 
5c93a70c
         const Foo = function () { }
         Foo.prototype = { a: true, b: null };
406b5587
         expect(uut.defgeneric("foobar", "a")
5c93a70c
             .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 })))
a00b5a50
             .toEqual('cbad');
406b5587
     });
 });
94944213
 
406b5587
 describe("Shape", () => {
     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();
a00b5a50
         expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", 3))).toBeTruthy();
406b5587
         expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b"))).toBeFalsy();
         expect(uut.Shape("a", "b").super_of(uut.Shape("a"))).toBeFalsy();
94944213
     });
636eaffe
 });