Conversation
PR Summary
This collection of changes overall leans towards refactoring the code for improved readability, maintainability, and performance. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1311 +/- ##
============================================
+ Coverage 91.82% 91.97% +0.14%
+ Complexity 3159 3152 -7
============================================
Files 316 316
Lines 6229 6232 +3
Branches 633 634 +1
============================================
+ Hits 5720 5732 +12
+ Misses 353 338 -15
- Partials 156 162 +6 ☔ View full report in Codecov by Sentry. |
604b3b3 to
58aa282
Compare
| } | ||
|
|
||
| @Override | ||
| public String toString() { |
There was a problem hiding this comment.
isn't this the default toString?
There was a problem hiding this comment.
Almost. Except that it's shorter due to getSimpleName instead of getName.
I would like to make it even more shorter, but it doesn't have any human-readable fields...
There was a problem hiding this comment.
In debug logs.
To make output of such logs a bit better readable (than the default):
LOG.fine(() -> "resExp(%s [%s]) current: %s, root: %s, context: %s, regExpContext: %s -> res: %s".formatted(directive, Arrays.toString(args), current, root, context, regExpContext, res));
| } catch (IOException e) { | ||
| e.printStackTrace(); | ||
| return null; | ||
| throw new RuntimeException("Failed to generate image %s of size %sx%s".formatted(imageType, width, height), e); |
There was a problem hiding this comment.
I don't see why you'd want to throw an exception here.
There was a problem hiding this comment.
Because it's much better to clearly show the problem instead of hiding it.
- When you return null, it almost always causes NullPointerException in the next step. And user will not understand what happened. For example, in DF own test:
assertThat(faker.image().base64TIFF()).startsWith("data:image/tiff;base64,");- If user wants to protect, he will need to write IF:
String image = faker.image().base64TIFF();
if (image == null) {
// WHAT SHOULD I DO HERE?..
throw new RuntimeException("WTF, the image is null, and I have no idea why.");
}Here is more about "Throw Early, Catch Late" Principle
There was a problem hiding this comment.
How is writing that if(null) check different from:
try {
String image = faker.image().base64TIFF();
} catch(Exception e) {
throw new RuntimeException("WTF, the image generation failed");
}
I don't mind throwing exceptions early if the code is in my control, but this is a library, and how would a user know this code can throw an exception?
The implicit contract in DF is that none of the methods will ever throw an exception,this changes that approach.
There was a problem hiding this comment.
It differs a lot.
- User doesn't need to check if image is null.
- User doesn't need to pollute his code with unneeded IFs and try/catches.
- User will see a clear error message explaining WHY the image could not be generated.
The implicit contract in DF is that none of the methods will ever throw an exception
It sounds very strange to me.
Nobody can ever guarantee that his code never throws any exceptions.
Anyway, in this case, such an exception actually cannot happen. We need to catch IOException only because java interfaces like InputStream declare throws IOException. But in this case we operate with ByteArrayOutputStream which never throws IOException.
| } catch (UnknownHostException e) { | ||
| return "127.0.0.1"; | ||
| } | ||
| return getIpV4Address().getHostAddress(); |
There was a problem hiding this comment.
I don't think this is ideal, I believe the code can throw exceptions now sometimes, and I don't think we should.
There was a problem hiding this comment.
I also had a doubt, but no, this code doesn't fail.
This is a test that tries through all possible values:
@Test
void allPossibleAddresses() {
for (int first = 2; first < 256; first++) {
for (int second = 2; second < 256; second++) {
for (int third = 2; third < 256; third++) {
for (int fourth = 2; fourth < 256; fourth++) {
InetAddress inetAddress = inet4Address((byte) first, (byte) second, (byte) third, (byte) fourth);
assertThat(inetAddress).isNotNull();
}
}
}
}
}| } catch (UnknownHostException e) { | ||
| return "127.0.0.1"; | ||
| } | ||
| return getPrivateIpV4Address().getHostAddress(); |
There was a problem hiding this comment.
again, I don't think this is correct.
There was a problem hiding this comment.
same as above: it's correct :)
| } catch (UnknownHostException e) { | ||
| return "0:0:0:0:0:0:0:1"; | ||
| } | ||
| return getIpV6Address().getHostAddress(); |
There was a problem hiding this comment.
same as above. :)
|
@bodiam Let me summarize: I am pretty sure it's a good PR and should be merged. ;) |
|
Then feel free to merge it! :) |
…olver) it allows to log what objects were returned when debugging flaky test failures.
... to make it easier to read these values in logs
... especially with FINE level (== DEBUG). This pattern causes flaky failures that are very hard to debug. If something went wrong, it's always safer to throw it with a clear description of what happened. See "Throw early, catch late" principle.
…fore() It was WTF moment when I realized that method BaseFakerTest.before() resets all logger levels to INFO. It took few hours to find out why my logging configuration doesn't work. :(
* by default, FINE logs are NOT written to console, BUT * if some test failed, all its FINE logs are written to console. Thus we can investigate flaky tests.
there is no need to initialize static Faker's vars in some specific way. They should stably work in any order.
b01d682 to
5a81bbf
Compare
This PR doesn't fix #1306, but makes much easier debugging such problems.