-
Notifications
You must be signed in to change notification settings - Fork 76
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
Game Runner thread / subprocess improvements #571
Conversation
69dbd2b
to
66b1ba4
Compare
Hmm. Github action is failing with:
...you want to give me any other details about that, github action? like which invocation of WaitForAsyncUtils that was, or what the heck is on line 173 of GtkApplication.java? Well. I guess we add "get tests to run under CI" to the list. It's quite possible there's a thing we have to do to tell TestFX it's in headless mode or something. |
My copy of GtkApplication.java says that UnsupportedOperationException should come with a message:
|
For adding an option to force headless mode, gradle can add system properties to the tests: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:systemProperties(java.util.Map) not sure what the most ergonomic way would be to invoke that. I was thinking of adding a |
Okay. I added a bunch of javadocs. Let me know when you see other things I've added in here that still could use documenting. |
469122c
to
1cdfd7c
Compare
Okay, I think I've fixed the regression where this branch wasn't considering the launch a failure if it came back right away. With that, I think this branch is viable. There's a little more polish and rigor it could have, described by the checklist in the main body of this issue, but I think a lot of those are negotiable depending on how much weight the reviewers give them. @skaldarnar & @jdrueckert, I'd like for this branch to not get lost in the land of things-that-almost-were. I think it does a significant amount to improve the sort of experience described in #550, and in my humble opinion leaves the code in a better place technically. Let me know if the test-logs dependency is a blocker and I can pull that out. I never wanted to get in to a fight with slf4j on the classpath, I was just trying to get to a place where I could do some test-driven development for working on GameRunner's quirks. That now accomplished, I care more about having the application code merged than I do for the mess of groovy code that's trying to manage the dependency conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Part 1 (up to src/main/java/org/terasology/launcher/game/GameStarter.java
)
I still don't fully understand the details of what's going on, but I guess you can live with merging this now and reacting to changes and requirements in the future. If this becomes a blocker in going forward with Java 14+, we'll throw it out/adapt it/replace it 😉
... and conflicts in build.gradle
o.O
build.gradle
Outdated
testImplementation('org.spf4j:spf4j-slf4j-test:8.8.+') { | ||
because "testable logging" | ||
} | ||
testImplementation("org.slf4j:slf4j-api:1.7.+!!") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.+!!"
the elder speak to me - what do they want to tell me o.O
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.7.+
prefixed version range, any 1.7.x version
!!
strict version. that is, if something else pulled in slf4j-api 1.8, do not consider that to satisfy this dependency, because it does not match 1.7.
oh, that page shows there are ways we could write this out in the DSL instead of the symbol-heavy shorthand:
testImplementation("org.slf4j:slf4j-api") {
version {
strictly("1.7.+")
}
}
This is a slf4j provider made to write test assertions against. But there can be only one slf4j provider on your classpath at the same time, otherwise who knows which will be used. the remal.component-metadata plugin identifies a lot of these sorts of conflicts so they don't happen silently. but that doesn't have spf4j-slf4j-test in its list of things it knows about, so we also need to specify that. once you've identified conflicts, you have to resolve them, which takes ten lines for some reason, so I put it in a function. debugging wasn't working very well on scripts included with `apply from`, and that's how I ended up writing a gradle plugin.
with slight refactor to avoid mocking static method on Thread.
…ibility to satisfy both app and test code.
The more I look at it the more it circles back to the current design.
like `hasItems` but it checks from one collection you pass to it, not each one of a variable number of arguments.
so other tests can use jupiter Extension
We use TestFX so the JavaFX Task has an Application Thread to run in.
Replace the GameStarter Interface with Callable<Process>.
…if it exits with error
We don't want thread cancellation to terminate the process.
This pull request introduces 1 alert when merging 6667350 into 761f149 - view on LGTM.com new alerts:
|
There might be an intermittent test failure? because the checks shown on this PR pass, but there's a failure reported on https://github.com/keturn/TerasologyLauncher/actions/runs/243384472 |
Good to know. There's a lot of platform-specific quirks to both filesystems and process execution, and I haven't had a Windows environment to test in. and, bleh, that's not a very informative test failure message. Looks like I'll have to get that Windows VM up when I have more disk space. |
wait, I don't see any TestFileUtils changes in this branch. Do those failures happen for you on the main branch? |
Continuing from discord:
I feel like I'm misunderstanding something significant about what you're looking for out of this. From just a GitHub UI perspective, if you're reviewing by file, implementation is already separated from tests, and the From a reviewer's perspective, I don't know why a reviewer would ask to review the code without its tests. Knowing what is tested and how is a big concern when I do reviews. Ideally, tests also inform the reader about how the code under test is used and why it has some of the wrinkles it does. (In practice, I often find that when reading a test I have a hard time identifying the important parts of it among all the other stuff that's boilerplate. I'm interested in getting better at that, in both reading and writing tests, so feedback on test legibility is very welcome here.) all of which to say I don't know how to implement that PR-splitting suggestion in a way that seems like it will be helpful. |
Some landmarks for navigating this package:
|
(dis)honorable mention: StringIteratorInputStream (under TerasologyLauncher/src/test/java/org/terasology/launcher/StringIteratorInputStream.java Lines 28 to 32 in 6667350
This is a class that I'm inordinately proud I got to work, and I also fear is the worst for maintainability. I think the way to get it to go away is to factor out this bit of RunGameTask.monitorProcess TerasologyLauncher/src/main/java/org/terasology/launcher/game/RunGameTask.java Lines 129 to 131 in 6667350
that is The fear I have about getting rid of Is that paranoid? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went through all the files once 💪 😩 - looks good so far, some comments and merge conflicts to resolve, but otherwise we're close to getting this merged!
if ((!valueSet) && START_MATCH.test(line)) { | ||
declareSurvival(); | ||
} | ||
logger.info("Game output: {}", line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we log the game's output on INFO level? How complicated is it to still get the launcher logs itself? I image this could be hard to untangle in case somebody really needs to look at the logs... the game does write log files separately anyways, doesn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the short-term answer for this is that the Launcher tab where you see log output is implemented as a log destination, so if we don't re-log stuff it's not visible there.
* | ||
* @see <a href="https://github.com/TomasMikula/ReactFX/blob/v2.0-M5/reactfx/src/main/java/org/reactfx/util/FxTimer.java">ReactFX</a> | ||
*/ | ||
public static final class FxTimer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in #586 this may just live in a file on it's own.
* For example: | ||
* <p> | ||
* {@code | ||
* assertThat(Arrays.asList("foo", "bar", "baz"), hasItemsFrom(List.of("baz", "foo"))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in the example it would make sense to highlight that this is not about includes but a weaker form. The list on the right might contain elements that are not present on the left side. In other words, the intersection of the lists interpreted as sets is not empty - is my understanding correct?
* assertThat(Arrays.asList("foo", "bar", "baz"), hasItemsFrom(List.of("baz", "foo"))) | |
* assertThat(Arrays.asList("foo", "bar", "baz"), hasItemsFrom(List.of("faz", "foo"))) |
return "nope" + new Random() | ||
.ints(16, 0, 255).mapToObj( | ||
i -> Integer.toString(i, Character.MAX_RADIX) | ||
) | ||
.collect(Collectors.joining()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe less custom coding here?
return "nope" + new Random() | |
.ints(16, 0, 255).mapToObj( | |
i -> Integer.toString(i, Character.MAX_RADIX) | |
) | |
.collect(Collectors.joining()); | |
return "nope-" + UUID.randomUUID().toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, this was just me making up garbage names for testing files? Yeah, sure, UUID is fine.
You get less entropy-per-character in UUID.toString() because it's formatted as base-16 and I think even type-4 UUIDs have at least a couple bits that aren't actually random, but I can't really defend the claim that those details are important to this use case.
String.format("for i in $( seq %s ) ; do echo $i ; sleep 1 ; done", seconds) | ||
); | ||
var proc = pb.start(); | ||
LoggerFactory.getLogger(SlowTicker.class).debug(" ⏲ Ticker PID {}", proc.pid()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I'm pretty sure my Windows complained about these nice little icons.... ⏲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that what it was! Okay. We should add that character encoding setting to build.gradle if that fixed it.
static final String GAME_DATA_DIR = "game_data"; | ||
static final JavaHeapSize HEAP_MIN = JavaHeapSize.NOT_USED; | ||
static final JavaHeapSize HEAP_MAX = JavaHeapSize.GB_4; | ||
static final LogLevel LOG_LEVEL = LogLevel.INFO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just removed LogLevel
so this has to be adjusted to use slf4j's Level
- should be an easy fix.
|
||
@Timeout(5) | ||
@ExtendWith(ApplicationExtension.class) | ||
public class TestRunGameTask { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also not reviewing this in detail. I still think that testing the game log bridging and introducing complex dependency mangling for that is a bit of overkill, but I'm impressed you pulled this off. As talked about earlier, as long as we can keep these tests without too much maintenance effort and investment I'm fine with having them, but I don't want them to become a burden for other projects 🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency-configuration for slf4j testing did turn in to a complete clownshow, yes. I was hoping that by this point in the year, as Java 15 came out and whatnot, we'd see slf4j 2.0 stabilize and then we'd have better options, but... turns out that hoping people you don't know will do the work you want doesn't necessarily get results.
Testing log output seemed important at the time because that was really the only way that code communicated its outputs. That could be something to keep in mind for future iterations: "my slf4j logs in this TextArea" is currently the only user interface to some important information. But as Launcher develops a richer user interface to communicate about its status and troubleshoot failures, it'll also be developing APIs for that information that don't require going through the logger, and we can base tests on those instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Back to the present, though: I hope someone can confirm that TestRunGameTask is comprehensible, because this is where all the persnickity stuff about process state and inter-thread communication is. If things go wrong, or we discover other edge-cases on other platforms, this is where the work will be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can confirm that it looks comprehensible 😉 it's nicely structured, has comments, and so on. I think I should have put more emphasis on "in detail" rather than "not reviewing" somehow 🙃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will double-check on Windows, but otherwise looks fine!
This pull request introduces 1 alert when merging 6693419 into 24252ec - view on LGTM.com new alerts:
|
This pull request introduces 1 alert when merging aa70a8b into 8065ef6 - view on LGTM.com new alerts:
|
gnarf, this introduces a flaky test... may need to revert this (or fix fast to not become a problem/annoying) for other PRs... retriggering seems to help, which is even more confusing 🙈
See https://github.com/MovingBlocks/TerasologyLauncher/pull/592/checks?check_run_id=1235517318 for a CI run with failed test, test report attached as build artifact. |
This reverts commit 81b566c.
This is all about what happens when you push that "⏵ Run Game" button.
Things in scope include:
This branch introduces the use of TestFX in order to test code relying on JavaFX classes.
How to test
All the things that involve starting a game and configuring what happens when it starts and exits.
Outstanding before merging
This branch is in a working state at the moment but there are some things to clean up before it'll be out of draft status.
maybe nice to have
Comment if these look important: