spec/grid/GridExtensionSpec.js

/** General tests that every Grid subclass should pass
 * @test {Grid2D}
 * @test {Grid3D} */
describe("Subclasses extending the Grid superclass", function () {
	/*
	Testing for having a _pixelArray, and the p2i/i2p/neighi methods and the
	pixels(i) generators.
	Other methods that should be implemented in grid subclasses (gradienti,
	laplaciani, neighNeumanni) are not enforced since they are not used by all
	CPMs.
	*/


	let CPM = require("../../build/artistoo-cjs.js")
	let testObjects = [], testNames = []
	let addTestObject = function( object, name ){
		testObjects.push( object )
		testNames.push( name )
	}

	// You can add your own grid subclasses to be tested here.
	addTestObject( new CPM.Grid2D( [50,50] ), "Grid2D (torus-Uint16)" )
	addTestObject( new CPM.Grid2D( [100,100],[false,false],"Float32" ),
		"Grid2D (noTorus-Float32)" )
	addTestObject( new CPM.Grid3D( [50,50,50] ), "Grid3D" )


	// Uncomment the following to see that the tests fail when the grid
	// extension does not implement the necessary methods.
	/*class MyGrid extends CPM.Grid {
		myMethod(){
			return 1
		}
	}
	addTestObject( new MyGrid( [50,50] ), "MyGrid" )*/


	for( let i = 0; i < testObjects.length; i++ ){

		let subclassName = " " + testNames[i] + ": "
		let obj = testObjects[i]
		let p = []
		for( let d = 0; d < obj.ndim; d++ ){
			p.push(0)
		}

		describe( subclassName, function(){

			it( "should all have a _pixelArray", function(){
				expect( obj._pixelArray ).toBeDefined()
				expect( obj._pixelArray.length ).toEqual( obj.p2i( obj.extents ) )
			})

			it( "should all have a p2i method", function(){
				expect( obj.p2i ).toBeDefined()
				expect( obj.p2i ).toEqual( jasmine.any(Function))
				expect( function() { obj.p2i(p) } ).not.toThrow(
					"A p2i method should be implemented in every Grid subclass!"
				)
			})

			it( "should all have an i2p method", function(){
				expect( obj.i2p ).toBeDefined()
				expect( obj.i2p ).toEqual( jasmine.any(Function))
				expect( function() { obj.i2p(0) } ).not.toThrow(
					"An i2p method should be implemented in every Grid subclass!"
				)
			})

			/** @test {Grid2D#i2p}
			 * @test {Grid2D#p2i}
			 * @test {Grid3D#p2i}
			 * @test {Grid3D#i2p} */
			it( "i2p and p2i should be inverse for valid grid coordinates", function(){

				// Test 10 randomly sampled pixels on the grid.
				for( let t = 0; t < 10; t++ ){

					// Pick random position on the grid
					let validPosition = []
					for( let d = 0; d < obj.ndim; d++ ){
						let posD = Math.round( Math.random()*(obj.extents[d]-1) )
						validPosition.push( posD )
					}

					// Check if inverse relation holds.
					let validPositionIndex = obj.p2i( validPosition )
					expect( obj.p2i( obj.i2p( validPositionIndex ) ) ).toEqual( validPositionIndex )
					expect( obj.i2p( obj.p2i( validPosition ) ) ).toEqual( validPosition )
				}

			})

			it( "should all have a neighi method", function(){
				expect( obj.neighi ).toBeDefined()
				expect( obj.neighi ).toEqual( jasmine.any(Function))
				expect( function(){ obj.neighi( 0 ) } ).not.toThrow(
					"A neighi method should be implemented in every Grid subclass!"
				)
			})

			it( "should all have pixels(i) generator methods", function(){
				expect( function() {
					let arr = []
					for( p of obj.pixelsi() ){
						arr.push(p)
					}
					return arr
				}).not.toThrow()
				expect( function() {
					let arr = []
					for( p of obj.pixels() ){
						arr.push(p)
					}
					return arr
				}).not.toThrow()
			})
		})

	}
})

/** Grid subclasses supporting diffusion.
 * @test {Grid2D} */
describe("Grid subclasses supporting diffusion", function () {

	let CPM = require("../../build/artistoo-cjs.js")
	let testObjects2 = [], testNames2 = []
	let addTestObject = function( object, name ){
		testObjects2.push( object )
		testNames2.push( name )
	}

	// You can add your own grid subclasses to be tested here.
	addTestObject( new CPM.Grid2D( [100,100],[false,false],"Float32" ),
		"Grid2D (noTorus-Float32)" )
	// 3D is not supported yet.
	//addTestObject( new CPM.Grid3D( [50,50,50], [false,false,false] ), "Grid3D" )

	for( let i = 0; i < testObjects2.length; i++ ) {

		let subclassName = " " + testNames2[i] + ": "
		let obj = testObjects2[i]
		let p = []
		for (let d = 0; d < obj.ndim; d++) {
			p.push(0)
		}

		describe( " [ subclass " + subclassName + " ] ", function () {

			it( "should support a Float datatype", function() {

				expect( obj.datatype ).toBeDefined()
				expect( obj.datatype ).toEqual( "Float32" )

			})

			it( "should be able to store Floating point and negative numbers", function(){

				expect( function() { obj.setpixi(0,-1) } ).not.toThrow()
				expect( obj.pixti(0) ).toEqual( -1 )
				expect( function() { obj.setpixi(0,12.45) } ).not.toThrow()
				expect( obj.pixti(0) ).toBeCloseTo( 12.45, 6 )

			})

			it( "should have a gradienti method", function(){
				expect( obj.gradienti ).toBeDefined()
				expect( obj.gradienti ).toEqual( jasmine.any(Function))
				expect( function(){ obj.neighi( 0 ) } ).not.toThrow(
					"method 'gradienti' not implemented! "
				)
			})

			it( "should have a neighNeumanni generator", function(){
				expect( obj.neighNeumanni ).toBeDefined()
				expect( obj.neighNeumanni ).toEqual( jasmine.any(Function))
				expect( function() {
					obj.neighNeumanni( 0 ).next()
				}).not.toThrow(
					"Trying to call the method neighNeumanni, but you haven't " +
					"implemented this method in the Grid subclass you are using!"
				)
			})


		})
	}
})