Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

p5.Camera unit tests #3083

Merged
merged 7 commits into from Aug 10, 2018

Conversation

AidanNelson
Copy link
Member

This adds unit tests for the p5.Camera object as added in #3080.

I wasn't quite sure how to create tests for projection matrices without simply recreating the ortho() and perspective() functions in the test, so any feedback on that would be appreciated!

@kjhollen
Copy link
Member

hey @AidanNelson, instead of replicating the ortho/perspective/etc functions in the unit tests, what you'll do instead is determine the expected values for each entry of the matrix and hard code them. in theory, the 'expected' values are generally known ahead of time and not computed—things that are easily unit-testable have predictable results.

this feels hard for me to explain, somehow—if this doesn't make sense, we can set up some time on google hangouts.

Copy link
Member

@kjhollen kjhollen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Aidan, this is a good start. I think the ideas/goals of unit testing take a bit of practice to set in, and seeing this gives me a good idea of where you're at and where to go next. I've left some comments in line and thought it would be good to share some general comments, too:

in some cases, because the camera is a complex object, you may want to check that additional values are set

for most functions, you'll want to test them with a variety of inputs, especially if there are any edge cases in the code or you can think of some possible gotchas in the matrix math. the tests in math/calculation.js have some good examples for this—the distance function is an especially good one to look at.

});

test('SetPosition() should set eyeXYZ', function() {
var pos = Math.random();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally speaking, unit tests should be deterministic. a known value should be applied as an argument to a function, then hardcoded values should be tested. instead of testing a true random set of numbers, it's good to be systematic about what types of numbers you're testing.

some things we might consider here:

  • are any or all of the values positive? covers a case where the code might have special cases for >0 or <0
  • are any or all of the values negative?
  • are any or all of the values 0? all 0s is a good case to test with matrix math because it means the magnitude of the supplied vector is 0, which can be trouble in some calculations
  • is there a mix positive/negative/0?
  • are all the values the same, or is each one different? as written, this test would not find a bug where the first argument is accidentally applied for all three (this is something I have done before!)
  • what if a non-number value is passed as one of the arguments?
  • are any other values updated as a result of setPosition that should be checked in their own assertions as part of this test? the assertions will let you provide some more detail in an error message, so you can figure out what if any conditions are not met.

myCam.lookAt(23, 23, 23);
assert.strictEqual(myCam.centerX, 23);
assert.strictEqual(myCam.centerY, 23);
assert.strictEqual(myCam.centerZ, 23);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are other values (up, eye) expected to change here? if so, we should test their values.

myCam.projMatrix.mat4[i],
myp5._renderer.uPMatrix.mat4[i]
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the same test, we should check if the expected values are in the matrix with additional assertions. you can add an argument to the assert call to print a more helpful error so it's obvious if the error was that the matrices didn't match or that they didn't have the expected values (applies to next test, too)

0, -1, 0, 0,
0, 0, -3, -1,
0, 0, -40, 0];

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool. another good value to test is the default! you may want to write a helper function that iterates over two arrays or matrices to check their values—or maybe there's already a chai function that already handles arrays?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, switched to using chai's deepEqual assertion.

test('copy() returns a new p5.Camera object', function() {
var newCam = myCam.copy();
assert.instanceOf(newCam, p5.Camera);
assert.notDeepEqual(newCam, myCam);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should check that the values for both are the same here, to ensure that the copy worked

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test for this.

for (let j = 0; j < 3; j++) {
assert.typeOf(local.x[j], 'number');
assert.typeOf(local.y[j], 'number');
assert.typeOf(local.z[j], 'number');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great. are there any other conditions that should be true here? should the axes be normalized? should they be orthogonal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, those checks are added.

});

test('perspective() sets renderer uPMatrix', function() {
myCam.perspective(Math.PI / 3.0, 1, 1, 100);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a negative value for the field of view allowed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the view will be reversed! I wasn't sure whether / how to check for this (and a few other) edge cases which produce valid matrices but unexpected views.

@AidanNelson
Copy link
Member Author

Ok, @kjhollen I added several unit tests and updated them throughout to ...

  • set camera defaults in the test-suite setup, in case these default values ever change in the library,
  • use a hardcoded camera or projection matrix as the expected value,
  • use chai's deepEqual assertion to iterate over matrix values,
  • also sometime also check eyeXYZ, or upXYZ so that error messages would be more helpful.

Copy link
Member

@kjhollen kjhollen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, Aidan! This is looking much better. One thing I was thinking while I read through this was, is there some way you can test this on values you are likely to encounter using orbitControl? especially for the camera() method!

var delta = 0.001;

// copy of p5.Vector.prototype.angleBetween...
var angleBetween = function(a1, a2, a3, b1, b2, b3) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I think it ought to be ok to just use the vector version (and make some vectors) when you need this? unless I'm missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I was trying to keep it isolated, but not for any particular reason!

@AidanNelson
Copy link
Member Author

Ok! Once #3088 is merged, I could add a unit test for orbitControl using the p5.Camera.prototype._orbit() method directly (without needing mouse input).

@kjhollen kjhollen merged commit f7fdd10 into processing:webgl-gsoc-2018 Aug 10, 2018
@kjhollen
Copy link
Member

cool! a nice bonus is that using p5.Vector made the test more legible too :)

@AidanNelson AidanNelson deleted the camera-unit-tests branch August 11, 2018 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants