There's been an intermittent test failure in the Field module lately. Here's an instance (hopefully the test won't get erased with a retest):
http://qa.drupal.org/pifr/test/109119
The test failure is:
Field form tests (FieldFormTestCase) [Field API] 236 1 0
Widgets are displayed in the correct order Other field.test 1458 FieldFormTestCase->testFieldFormJSAddMore()
Here's the code that is generating the failure -- it uses some random numbers to generate weights, then tests that they are in the right order -- I suspect that in some rare cases, something strange is happening with the random numbers so that the test is failing:
for ($delta = 0; $delta <= $delta_range; $delta++) {
// Assign unique random weights.
do {
$weight = mt_rand(-$delta_range, $delta_range);
} while (in_array($weight, $weights));
$value = mt_rand(1, 127);
$edit["$this->field_name[$langcode][$delta][value]"] = $value;
$edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
// We'll need three slightly different formats to check the values.
$values[$delta] = $value;
$weights[$delta] = $weight;
$field_values[$weight]['value'] = (string) $value;
$pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
}
// Press 'add more' button through AJAX, and place the expected HTML result
// as the tested content.
$commands = $this->drupalPostAJAX(NULL, $edit, $this->field_name . '_add_more');
$this->content = $commands[1]['data'];
for ($delta = 0; $delta <= $delta_range; $delta++) {
$this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
$this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
}
ksort($pattern);
$pattern = implode('.*', array_values($pattern));
$this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
It's the last line here where the failure happens.
Comments
Comment #1
carlos8f commentedsubscribing
Comment #2
rfaysubscribe
Comment #3
jhodgdonLooking at this, I'm wondering if it's a problem with duplications in the $value values.
That is to say, the test line that's failing is basically testing that a regexp like
input value="val1" .* input value="val2" .* input val="val3" ...exists on the page, wehre val1, val2, etc. are random integers. But what if val6 and val20 are the same, for instance? Maybe the regexp when it gets to val6 will skip all the way to val20 and then the rest of the regexp will fail? I'm not all up on regexp greediness, so I'm not sure if that would happen or not (or if I'm making sense).
Suggested solution (if that's the problem) would be to do something with values like what is being done with the weights [i.e. make sure they are all different].
Comment #4
yched commentedjhodgdon is right, failure happens with the greediness of the regexp when some values are the same.
- added a
for (10 times)around the tested part, ran the test 5 times: no failure- changed the test so that values are always the same, ran the test once: 10 failures
Patch makes sure that the values are always different
Comment #5
jhodgdonyched: this looks like it would work, but perhaps it would be better to have the "unique weights" and "unique values" loops separate instead of put into one loop together?
Comment #6
jhodgdonAlso, in the first hunk you forgot to remove the $value call that was there, so it will be reset after you carefully made sure it was unique:
Comment #7
yched commentedTrue. Fixed.
Comment #8
jhodgdonThat looks more reasonable. :)
Let's make sure the test bot passes it (at least once), and then it's probably good for RTBC.
Comment #9
carlos8f commentedFix looks correct... bot will set it back to CNW if it fails.
Comment #10
webchickDoes this have to be random at all..?
Comment #11
carlos8f commentedDoesn't *have* to be, but it makes sense since we are asserting that anything within those ranges is valid.
Comment #12
yched commentedvalues don't really have to be random - but are better random since we test with a regexp.
weights have to be random: this is what we are testing
Comment #13
jhodgdonThe values could probably just be generated with a $i++ type thing to make them unique, instead of random.
Comment #14
webchickCould we do that instead? The code as written is more difficult to read than what's in HEAD, and if we don't need randomness, I'd prefer not to use it. Randomness is almost always a recipe for "random test failures".
Comment #15
carlos8f commentedRandomness in tests is a Good Thing(tm) if it causes failures that allow you to find a real bug. But in this case there's no real bug :) I think this is still fine to be RTBC.
Comment #16
jhodgdonI am not sure I agree with you carlos8f. Randomness like this causes intermittent test failures that are not reproducible and cause people to pull their hair out. If these failures are not detected when the test is first committed (like this one), they occur rancomly, breaking the test bot, later on.
I just got bitten by this failure, just now, on a totally unrelated patch (a doc-only patch), though, so I hope we can get this fixed one way or another.
Comment #17
jhodgdonOr to put it another way, maybe randomness is a good thing, but if you use it as a big feature of your test, maybe you should have a loop in your test that runs the random section several times. This would verify that it's really passing based on several runs at the randomness, rather than relying on the fact that it worked when you first submitted the patch.
PHP and other programming languages don't really have "random" numbers -- they just have pseudo-random numbers, which will come out the same every time, given the same random seed. This test was not failing much until recently (at least, so I believe). Maybe if it had had a loop in it, the failure would have been detected and fixed before the patch was initially committed?
Comment #18
carlos8f commentedSometimes tests fail only when a bot is run under high concurrency, sometimes they fail due to being on 32 bit hardware, sometimes a version of PCRE or libxml causes a fail. Sometimes tests get committed that aren't even deterministic.
There are a lot of reasons why test fails wouldn't be caught before initial commit. Randomness is often useful to catch bugs that happen when you venture outside of 'foo' and 'bar', but when using randomness you should be sure that you assert correctly. So I wouldn't discourage the use of randomness based on a few incorrect assertions.
Comment #19
webchickWell, since this once again broke HEAD and stalled out testbot, I decided to commit #7 to HEAD.
While I was going through I noticed that there are actually lots of uses of mt_rand in field.test, including this same do { ... } while pattern used elsewhere. So cleaning all that up would likely be the subject of another issue anyway.
Thanks for the fix!