On Thu, 10 Mar 2022 01:11:03 GMT, Stuart Marks <sma...@openjdk.org> wrote:

> Sorry, the test changes look like they're heading in the wrong direction. I 
> tried to provide some hints for what I was looking for in my previous 
> comments. At this point, I felt it would have been too time-consuming to 
> provide a bunch of review comments to try to get your proposed tests closer 
> to what I was looking for, so instead I've extended/rewritten the existing 
> `WhiteBoxResizeTest` file:
> 
> https://github.com/stuart-marks/jdk/blob/JDK-8281631-smarks/test/jdk/java/util/HashMap/WhiteBoxResizeTest.java
> 
> I hope you're ok with this.
> 
> Here are some issues I have with the multiple-file approach:
> 
> * The separate classes tends to obscure things, as the separate files are 
> actually tightly coupled; infrastructure (reflection stuff) and test case 
> setup (Util, TestSuite) are separated from the test, making it hard to see 
> what's going on.
> * Each test method still does too much work. In general, test methods should 
> do one thing in three phases: setup, perform action, assert results -- the 
> [GivenWhenThen](https://martinfowler.com/bliki/GivenWhenThen.html) pattern. 
> (Yes, I know a lot of tests currently in the regression suite violate this, 
> but I think it's reasonable for new fine-grained combo tests like this one to 
> be held to this standard.) Several of the test methods here do setup, perform 
> an op, make assertions, perform another op, make assertions, perform another 
> op, make more assertions, etc. The reason this is bad is that it's hard to 
> separate out special cases. Consider `WhiteBoxHashMapInitialCapacityTest`. 
> Since this test method tests both lazy allocation and initial capacity 
> allocation, it can't handle the different behavior of `WeakHashMap`.
> * The need for separate classes is possibly driven by JUnit 4, where the test 
> parameterization mechanism seems to only be available on the granularity of 
> an entire class. (I'm not a JUnit expert, so I might have missed something.) 
> This makes it difficult to structure parameterized tests on a per-method 
> basis, as can be done in TestNG.
> 
> The existing `WhiteBoxResizeTest` is TestNG, so it seems sensible to extend 
> that, and add several parameterized test methods. I believe the new 
> `WhiteBoxResizeTest` covers all the existing cases as well as the new ones in 
> your tests, plus some additional ones that we've uncovered during this 
> discussion. It covers a reasonable set of cases in the following areas:
> 
> * combo of various constructors plus populating the map in different ways all 
> get the same expected table size
> * lazy allocation of table
> * correct default allocation of table
> * verification of fix for `WeakHashMap` premature resizing
> * verification of fix using double instead of float calculation for certain 
> large sizes
> 
> Here are some notes about what I did. Unfortunately all that's here is the 
> end result. I tried a bunch of experiments and different alternatives, which 
> aren't present here, but I'll try to document some of the processes I went 
> through to get to this result.
> 
> * Rearranged the infrastructure to accommodate `WeakHashMap`. The `table()` 
> and `capacity()` methods now work for all map classes under test. Really, 
> there are only three classes, and two (`HashMap` and `LinkedHashMap`) have 
> the table in the `HashMap` class, so a simple conditional will work. If 
> additional classes are added, the conditional might turn into a case 
> expression.
> * Simplified VarHandle setup. The old code had to load some private classes 
> in order to get the exact types to provide to `findVarHandle()`. It's easier 
> to find the field using reflection and then to "unreflect" it into a 
> VarHandle.
> * Rearranged test methods into pairs of data provider and 
> data-provider-consuming test methods, possibly with some helper methods, 
> demarcated into different sections in the test class. Using TestNG data 
> providers is a bit clunky with `Object[][]` or `Iterator<Object[]>` but it 
> ends up working reasonably well.
> * The `tableSizeFor()` test is also now a data provider. It probably would 
> have been ok to leave it as a single method with a bunch of asserts, but 
> using a data provider enables using a tabular form for the test data, which I 
> think makes it easier to manage the test cases.
> * Most test data is tabular. It's fairly straightforward but for some 
> providers it's bit a repetitive to add more test cases. However, I didn't go 
> the next step of compressing out the repetition, because doing so would have 
> made it difficult to exclude special cases. For example, the `WeakHashMap` 
> growth policy for `putAll` differs from the others.
> * Of note is that some of the larger sizes/capacities are tested using a 
> "fake" map, which reports some size but whose `entrySet` iterator returns 
> only one entry. This works for some of the implementations, so only cases for 
> those are included.
> * I used helper methods called from the data provider in order to establish a 
> target type for lambdas. This reduces the clutter in the table data.
> * It's much easier to parameterize constructors using `Supplier` lambdas as 
> method arguments instead of as fields in a helper class. Using a `record` 
> might be helpful here though.
> * If a parameterized test fails, TestNG will print out the arguments obtained 
> from the data provider that caused the test method to fail. The string form 
> of a lambda isn't useful. Providing a string label is a technique (hack) to 
> identify the failing test case. It's a bit cryptic but it's quite effective.
> 
> There is a certain amount of brittleness in these tests such that they're 
> prone to failure if the internals of one of the implementations changes. 
> These are whitebox tests after all. But the new organization should 
> facilitate rearrangement of the test cases and test methods if one of the 
> implementations diverges. In addition, if changing one of the implementations 
> causes test failures here, it will point to other implementations that might 
> also need to be changed.
> 
> The new test is comprehensive enough that we might be able to remove the 
> `Enum/ConstantDirectoryOptimalCapacity` test and the related test that uses 
> those utilities to verify map table sizes. However, that evaluation should be 
> done separately.
> 
> I suggest you merge this file into this PR in place of the other test files. 
> Once you get this done I can run everything through our internal test system 
> and sponsor this PR for integration.

Thanks for your help.

A quick glance: that is really big differences between the designs, but I do 
accept that sometimes there be no absolute right and wrong in designs, and 
consistency to the group is important.

So I will have a look at your version of this test and it functionally correct 
and can cover our newly found cases, I'm glad to change the tests to your 
version.

-------------

PR: https://git.openjdk.java.net/jdk/pull/7431

Reply via email to