Problem/Motivation

#2151223: Add a method to SortArray to sort by weight and title added the sortByWeightAndTitleKey() method, with phpunit coverage testing the comparison return values.

but

made the Core\Language\Language::sort() use it, and did not test actual language objects (importantly where weight was a tie to try and force it to use the title comparison)

Language does not have a title property. It has a name property.

Proposed resolution

  • Dont use the generic sort.
  • Have language implement the ArrayAccess
  • fix the sort to use the name property

Remaining tasks

  • (done) write a phpunit test for Language::sort() that uses language objects.
  • (done) think about how the test should actually be.
  • (done) review

User interface changes

No.

API changes

No.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

YesCT’s picture

Issue tags: +Needs tests

talking with Berdir in irc...

the things to sort get casted to array, so they might have a 'name' key, but they definitely do not have a title one.

so I'm still thinking we are lacking a test that has sorts languages with same weights.

guessing LanguageUnitTest is the place to add a sort test.

YesCT’s picture

Issue tags: +D8MI
YesCT’s picture

Status: Active » Needs review
FileSize
1.36 KB

I'm sure this isn't the "right" way for this test to be... but it fails. :)

YesCT’s picture

Title: Core Language sort() should not work. sortByWeightAndTitleKey is only for things that implement the \ArrayAccess Interface » Core Language sort() should not work, needs to use name instead of default title

guess next step is to try and fix the bug with something like #2151223-20: Add a method to SortArray to sort by weight and title
telling it to use the name (instead of the default title)

Status: Needs review » Needs work

The last submitted patch, 3: 2304651.3.patch, failed testing.

YesCT’s picture

Status: Needs work » Needs review
FileSize
2.88 KB
2.31 KB

a test that uses a data provider.
( looked at http://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-t...
and the example in SortArrayTest )

3 passes, 1 fail.
4 tests, for covering more cases than just the one where weight is tied and label was ignored.

Status: Needs review » Needs work

The last submitted patch, 6: 2304651.6.patch, failed testing.

martin107’s picture

Status: Needs work » Needs review
FileSize
1.04 KB
3.35 KB

The much needed test make sense and look clean and elegant.

But I think the title of this issue could do with some work.

Please note that with my patch the failing test still FAILS so I provided a incomplete solution ... just a nudge in the right direction.

I should reason this out in public in order to give confidence in my solution.

Language::sort() could easily be altered using an anonymous function to override the sortByWeightAndTitleKey function to use NAME not TITLE :-

  /**
   * Sort language objects.
   *
   * @param array $languages
   *   The array of language objects keyed by langcode.
   */
  public static function sort(&$languages) {
    $sortByWeightAndName = function ($a, $b) {
      return \Drupal\Component\Utility\SortArray::sortByWeightAndTitleKey($a, $b, 'weight', 'name');
    };
    uasort($languages, $sortByWeightAndName);

But that is not the issue. The underlying thing is that $a and $b as fed from the language array are language objects sortByWeightAndTitle() needs a array of key-values pairs.

in short the weight and name are available as $a->weight and $a->name and sortByWeightAndTitleKey needs $a['weight'] and $b['name'].
( I have verified this is a debugger just to be sure )

This misunderstanding was cleared up in #2226533: Changes to the Language class due to the LanguageInterface (followup) which has stalled and is currently being scrapped for parts. So here is the relevant code from there.

In the spirit of fail early and in public ... I am sure there is just something obviously wrong and so I am posting early :-)

PS Regarding the Drupal\system\Tests\Entity\EntityQueryTest fails looks like #2254011: Drupal\system\Tests\Entity\EntityQueryTest random failure is shadowing any changes to the language subsystem. Nuts!

Status: Needs review » Needs work

The last submitted patch, 8: 2304651.8.patch, failed testing.

YesCT’s picture

@martin107 sounds like you are thinking the same thing I was.
The original title was: Core Language sort() should not work. sortByWeightAndTitleKey is only for things that implement the \ArrayAccess Interface
because sortByWeightAndTitleKey and the array sorts it calls, take arrays as input.

But in #1, I tried to pass on what Berdir said: that because (there is no type on the parameters in the function definition and) the language objects are cast as arrays, the language objects are some kind of array in the function, and php makes array elements keyed by the property names. (I have not verified with a debugger...)

I'm going to try wrapping and telling it to use the 'name' key.

YesCT’s picture

Status: Needs work » Needs review
FileSize
3.11 KB
812 bytes

sigh. this fails too, and I had hoped it would pass.

Interdiff to 6.

[edit: for anyone following along, this runs just this one test]
./vendor/bin/phpunit --filter=testSortArrayOfLanguages

YesCT’s picture

uh.. data set #3 is the one that is failing...

--- Expected Other LanguageUnitTest.php 0 Drupal\Tests\Core\Language\LanguageUnitTest->testSortArrayOfLanguages with data set #3()

that is not the one I thought would be failing.

I think I didn't write the tests correctly... cause after "fixing" it, the fails are on the data sets where I thought they were already in order.

YesCT’s picture

changed the test to be an array keyed by langcode...
and now the tests pass without the fix. :(

YesCT’s picture

clearly I'm doing something wrong.
Cause when I change the order of my expected...

diff --git a/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php b/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
index 769b977..5a803dc 100644
--- a/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
@@ -156,8 +156,8 @@ public function providerTestSortArrayOfLanguages() {
           [$language10B->getId()] => $language10B,
         ),
         array(
-          [$language10A->getId()] => $language10A,
           [$language10B->getId()] => $language10B,
+          [$language10A->getId()] => $language10A,
         ),
       ),
       // Set up data, tied by weight, out of order by name.

the tests still pass.

The last submitted patch, 12: 2304651.12.patch, failed testing.

The last submitted patch, 11: 2304651.11.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 13: 2304651.13.patch, failed testing.

YesCT’s picture

UGH. I think the test trying to use the data provider is comparing two empty arrays. and that is why it is passing.

YesCT’s picture

ok. I have two things to verify... that any test is working, and how to get the provider to work.

Here I went back to the non-provider to see if I can tell if two arrays are in different orders.

The test can... when calling sort though, the one that starts in the wrong order gets sorted correct, and the one that starts in the correct order gets sorted wrong.
Which is fine.. if sort is broke in that way... and I can get sort to work as expected.

testDirectSortArrayOfLanguagesWrong()
testDirectSortArrayOfLanguagesCorrect()
are the only two tests doing what I expect them to.

trying that now.
Note this patch has a bunch of debugging stuffs in it.
No interdiff cause it's messy anyway.

With the fix, both the correct and wrong case pass.

YesCT’s picture

Status: Needs work » Needs review

in my provider I was setting the array wrong, with brackets around the index. :/

testSortArrayOfLanguages() is only giving me one fail right now without the fix...
[edit: that is ok. the data is: in order with different weights, out of order with different weights.. and those two should work with the current sort.
then the other two tests have weight tied, so it should force a sort on the title/name]

I think now, I might think about how this should actually be tested.

YesCT’s picture

files this time.

YesCT’s picture

Issue summary: View changes

updated issue summary.

YesCT’s picture

Issue summary: View changes
FileSize
2.94 KB
3.74 KB

this one takes out the direct test (no phpunit data provider).

So now there is just one new test:
testSortArrayOfLanguages()
and the dataprovider for it.

The last submitted patch, 19: 2304651.19.justtests.patch, failed testing.

The last submitted patch, 21: 2304651.20.justtests.patch, failed testing.

The last submitted patch, 19: 2304651.19.withfix.patch, failed testing.

The last submitted patch, 21: 2304651.20.withfix.patch, failed testing.

The last submitted patch, 23: 2304651.23.justtests.patch, failed testing.

YesCT’s picture

ok. #23 is looking pretty good.

next step is to try making a patch that runs the entity query test 50 time.... without uploading 50 patches and running the full test suite each time.

Making a note of how to do that.... (thanks @sun)
In run-tests.sh, replace the line $test_list = simpletest_script_get_test_list(); with $test_list = array_fill(0, 50, 'Drupal\Test\To\Repeat');

martin107’s picture

PLEASE DO NOT COMMIT

Taking 2304651.23.withfix.patch and altering the run-tes.sh file to run 50 times as per #29

locally first 10 runs all pass.

@YesCT sorry could not follow along until now my week went sideways - but I agree now looking pretty good.

YesCT’s picture

We did the repeated test to make sure we are not introducing something like #2254011: Drupal\system\Tests\Entity\EntityQueryTest random failure in head.

penyaskito’s picture

martin107’s picture

@penyaskito yes they appear to be almost identical in terms of changes to Drupal\Core\Language\Language, So yes it looks like a dup.

Now in terms of which one to advance...

Can I suggest this one should be committed.

A) It overrides 'name' not 'label which is think is the more correct thing to do, as in

-    uasort($languages, 'Drupal\Component\Utility\SortArray::sortByWeightAndTitleKey');
+    uasort($languages, function ($a, $b) {
+      return SortArray::sortByWeightAndTitleKey($a, $b, 'weight', 'name');
+    });

B) It has proved that the instability is resolved. 2304651.23.withfix.patch is the active patch and is ready to move to RTBC.

C) While I can see the test from the other issue, there are no comments and it is hard at a glance to see exactly what is covered and what is not.
YesCT comments show a systematic covering of all bases, and this gives me more confidence.

YesCT’s picture

@penyaskito wow! it has been a while since I looked at that whole patch on that issue!

I'm not totally sure it's a complete dup.

I thought the other was changing it to sort by label.
Which still might change what this is doing.

Hopefully, this will just get head doing what it should have, and then we can return to #2239399: Languages should be sorted by label instead of title

I'm still perplexed as to why there were not random fails in the run of 50 tests above...
*maybe* this *fixes* the random fails?

Gábor Hojtsy’s picture

Priority: Normal » Major
Issue tags: +language-base
penyaskito’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

If we don't have random failures as demoed by #30 and we have proper tests, then let's get this in and unblock the issues depending on this one.

penyaskito’s picture

RTBC is for #23. @YesCT or @martin107, could you please upload the files again avoiding any potential mistake?

martin107’s picture

FileSize
3.74 KB

Sure, just to be safe this is a copy of 2304651.23.withfix.patch

I have just given it a quick visual scan to double check.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
@@ -91,4 +91,94 @@ public function testGetNegotiationMethodId() {
+    reset($expected);
+    reset($languages);
+    $this->assertEquals(current($expected), key($languages));
+    next($expected);
+    next($languages);
+    $this->assertEquals(current($expected), key($languages));

This looks a little complex. I think we can replace with $this->assertSame($expected, array_keys($languages)); which will also allow for testing sorting more than 2 languages at a time.

YesCT’s picture

I'll do that now.

YesCT’s picture

Status: Needs work » Needs review
FileSize
2.79 KB
3.58 KB

ran the phpunit test locally, and without the fix, 3 passes and 1 fail, with the fix, 4 passes.
same as in #38, fail is in data set #2. where the they are out of order by weight.

I expected head to pass on that one, and to fail on one of the tests sorting by title (name).

YesCT’s picture

FileSize
773 bytes

interdiff.

YesCT’s picture

oh, good. it was the third data set that was failing (which is data set #2, since it starts counting at zero. Thanks @alexpott).
that is the set where the language title/name was *in order*. Note this means it was reordering the one that was out of order... this is because it was tieing on title (cause there was no title) and ties have unpredictable behavior.

phew.
Updated my comments so they start counting at dataset #0.

Note also that --tap is helpful (Thanks again @alexpott)

so (for those new to phpunit, and for my future self) I ran the test with:
./vendor/bin/phpunit --filter=testSortArrayOfLanguages
and
./vendor/bin/phpunit --filter=testSortArrayOfLanguages --tap

with sanity restored, I think this is ready.

The last submitted patch, 41: 2304651.41.tests-only.patch, failed testing.

tim.plunkett’s picture

  1. +++ b/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
    @@ -91,4 +91,94 @@ public function testGetNegotiationMethodId() {
    +  public function testSortArrayOfLanguages(array $languages, array $expected) {
    

    This should have @covers ::sort

  2. +++ b/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
    @@ -91,4 +91,94 @@ public function testGetNegotiationMethodId() {
    +    $language9A->setId('dd');
    +    $language10A = new Language();
    ...
    +    $language10A->setId('ee');
    +    $language10B = new Language();
    

    While you're rerolling, can you put a blank line in between these?

    Or, you could follow the pattern of ConditionAccessResolverTraitTest::providerTestResolveConditions(), and use $data = array(); $data[] = array(...); return $data;
    And then you can instantiate the $languageX object in line with their test case...

YesCT’s picture

thanks! done.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

Thanks!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/tests/Drupal/Tests/Core/Language/LanguageUnitTest.php
@@ -91,4 +91,98 @@ public function testGetNegotiationMethodId() {
+    reset($expected);
+    reset($languages);
+    $this->assertEquals(current($expected), key($languages));
+    next($expected);
+    next($languages);
+    $this->assertEquals(current($expected), key($languages));

So lets change this to the assertSame (as in #39) so if we add more than 2 languages to the data set it'll still work.

YesCT’s picture

sorry. looks like I lost the change from 41.

YesCT’s picture

Status: Needs work » Needs review
FileSize
773 bytes
2.82 KB
3.61 KB

The last submitted patch, 50: 2304651.50.just-tests.patch, failed testing.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

Ahhh, whoops. Nice catch @alexpott.

YesCT’s picture

Issue summary: View changes
alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 9bbfede and pushed to 8.x. Thanks!

  • alexpott committed 9bbfede on 8.x
    Issue #2304651 by YesCT, martin107: Fixed Core Language sort() should...
YesCT’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.