We picked Team Fortress 2 as the game - the reason why is a whole different story I won't go into here. TF2 used the Source 1 engine, and as it happened two Valve games also using that same version of the engine were Half Life 2 and Portal 1. So as a side-effect they also got to work in VR.
Well, Portal 1 "worked" - but all the tricks with perspective when you went through a portal were of course a nauseating disaster - it was pretty unplayable.
But HL2 did actually work pretty well. Joe spent a lot of time making the boat sequences work in a reasonable way.
There's a sequence of stacking boxes near the start that is somewhat infuriating in the original - the stack keeps falling over - but in VR it's really easy to place them well.
Also, whacking the manhacks with your crowbar goes from being a panicked flailing in flatscreen, to being an elegant one-swing home-run hit in VR.
What is meant to happen is a guard (spoiler alert - it's actually Barney in disguise) bangs on a door, the door opens, he says "get in", and then the game waits for you to enter the room before the script proceeds.
But in this case the door sort of rattled, but didn't open, and then locked shut again. So you can't get in the room, and the gate closed behind you, so you can't go do anything else. The guard waits forever, pointing at the locked door, and you're stuck.
So problem solved, right? 80 bits of precision means the collision didn't happen, but in 32 bits of precision it does, and that's your problem, more bits better, QED, right? Well not quite.
The guard's toe overlaps in both cases - a few millimeters is still significantly larger than ANY of the possible precisions. In both the SSE and x87 versions, the door hits the guard's toe. So far, both agree.
@TomF @AT1ST @knutaf
Unit tests are valuable in languages without static type checking.
Essentially, your unit test suite becomes your second compiler/linter, because JavaScript/Python/PHP/Lua doesn't give it to you for free like C++/Haskell/Java/Rust.
They're also good as documentation: "here's how to call it and what it's expected to do!"
@AT1ST @TomF @knutaf integration tests say "when the user presses A on the controller, the character jumps" but unit tests say "attempting to jump when not in contact with the floor does not change velocity."
Unit tests can also test a number of use cases of some unit that may not be currently in use by the full system, but are meaningful for a complete library/component.
It's also much easier to unit test error handling, than integration test the same...
@AT1ST @TomF @knutaf that being said. There's not a global definition of the separation of the kinds of test.
I tend to think of "unit tests" as "tests that run entirely in process/RAM and have controlled for any possible external system like file system, network, clock, etc."
Integration tests then use the actual uncontrollables, and thus cannot be guaranteed to be 100% free of flakes.
And flaky tests are the absolute worst!
@StompyRobot @TomF @knutaf Unit testing error handling I can understand, but I would hedge that integration testing can test "When attempting to jump after jumping while in contact with the ground, you cannot jump again once leaving the ground", which involves verifying that the "Jumpability" state changing while jumping.
While integration testing can be flaky because of that, it is more likely to catch unexpected double jumping capabilities (Or expected double jumping with limitations that are not Kirby-like.).
@StompyRobot @TomF @knutaf (For example of where you might see Unexpected double jumping capabilities, this comes to mind [ https://youtu.be/gUTPINL7UQs?si=BBTdI6Iv2FciYzA6 ].
It's used in the world record speedruns like this [ https://youtu.be/lBffcbr7dmI?si=oyyZ8AVgdMVBZQdN ] at 29:18 (Also using the Tingle Turner.).
@TomF @AT1ST @knutaf I don't know about easier to support.
My strategy lately has been unit test to test weird edge cases (that we know of, at that time) and try to reach a very good coverage, so going in every branches .
And then, an end to end test covering the happy path. I guess I tend to skip the integration testing. I feel integration testing is harder to test (like end to end), but doesn't provide the real full value because it is so mocked already.
But all of this is based on me not working in video games, so not same complexity and push for performances (which might break some clean separation of concerns, maybe?)
Also, maybe we don't have the same definition of unit test, integration test, end to end test (which is really a pain to be able to talk about with fellow developpers)
@dolanor @TomF @knutaf Perhaps it would be better for me to define what I am thinking of as "Integration Test", because I suspect it's pretty close to the "End to End testing" - most of my experience with it has been Selenium pointed at a website under test in a staging/dev/production environment, and then running the commands to do either a sanity test or a test for things (Recently, I did try and do an accessibility testing version in Selenium, although that...can be difficult to codify as I understand. Well, that and SauceLabs RemoteWebDriver connection stuff.). Other than that, it is usually me doing manual tests of an application in a test environment, ensuring that inputs get the expected outputs.
That is, automating the testing of a thing with as much of the app wired up so that the tooling only tests the same endpoints as the user might. As much as possible, avoiding mocking dependencies, and just having everything connected and using the things like they would in production.
Maybe that's more E2E?
@dolanor @TomF @knutaf I think for me, the difference with E2E testing is that while it's essentially integration testing, it would also involve testing the backend storage mechanism instead of just "Can the user get the system to a state that makes it clear that they broke it I'm their UI?".
At least, for an automation level, it would need to have an aspect of white-box testing, rather than just black-box testing.
You're the first one I see here talking about a compiler bug. One has said that "the compiler broke it", but that doesn't mean the compiler has a bug.
@TomF This is amazing. Thanks Tom!
More! More!
@TomF thank you for this story.
I work in embedded with bleeding-edge toolchains targeting unreleased hardware. Despite C being older than I am, it remains staggering to me how much flux still happens in the generated code from compiler changes. Our team has learned long ago to freeze the toolchain alongside the code…
@davidcanadasmazo @TomF a floating point equals comparison should never be done without specifying an epsilon that specifies how close the values must be to be considered equal. Also both absolute and relative limits might be needed. Did you add an epsilon in this case? Or did you just let it go once the bug disappeared?
I have seen these bugs in design rule checkers for PCB layout software. In that case it happens even for a greater than comparison.
@davidcanadasmazo @poleguy @TomF
With FMA I ran into the problem that a simple cross product didn't work as intended, like when both vectors had the exact same numbers (or even were identical) the cross product is supposed to be (0, 0, 0), but with FMA it isn't because suddenly `x1*y2 - y1*x2` is calculated with one regular multiplication and FMA for the rest of the term and FMA uses higher internal precision so even with identical values the result is != 0
Solution in GCC: `-ffp-contract=off`
@Doomed_Daniel @davidcanadasmazo @TomF Calling "-ffp-contract=off" a "solution" for GCC is an interesting choice. It is a solution if the problem is defined as the answer doesn't come out quite at zero as expected.
But I would argue that the floating point equals comparison is the problem. So the pepper solution is to define a proper epsilon and do a less than comparison.
Am I wrong to think equals comparisons are never a great idea?
@poleguy @davidcanadasmazo @TomF
That's true generally, though especially around 0 using an epsilon is a bit tricky if you have checks that use < 0 or similar (like "on which side of a line is a point" or "is point in triangle") - the rounding error might change the sign.
And of course usually you run into such problems with code you didn't even write yourself that worked for years and suddenly stops working which makes decisions other than "try to restore previous behavior" harder