Closed (fixed)
Project:
Feeds
Version:
7.x-2.x-dev
Component:
Code
Priority:
Normal
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
28 Dec 2016 at 16:30 UTC
Updated:
22 Apr 2017 at 11:45 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
wmostrey commentedComment #3
megachrizGreat idea! Do note that this introduces an API change. Code may look for a FeedsNotExistingException now to determine if it can do something with the importer in question. See for example
drush_feeds_import()in feeds.drush.inc.Re-uploading the patch here so the testbot would pick it up.
Comment #4
megachrizHm, the tests lack coverage for disabled importers.
Anyway, this has quite some impact on existing code. The changes in the patch will result into content being imported for disabled importers as a lot of code is doing something like this to perform an import:
Examples:
drush_feeds_import()in feeds.drush.inc.FeedsHTTPFetcher::request()in plugins/FeedsHTTPFetcher.inc._feeds_queue_worker_helper()in feeds.module.This could probably be fixed by checking for enabled importers in
FeedsSource::existing().Anyway, the code in feeds.drush.inc would require changes, as right now it says "importer does not exist or is not enabled.":
Comment #5
wmostrey commentedYes, let's determine when we should be able to manipulate an importer when it exists but is disabled (like editing the importer). This might be easier to implement:
Comment #6
megachrizThat looks easier to deal with indeed, but it would still require code changes elsewhere:
Code like above that is currently only checking for a FeedsNotExistingException, now need to check for FeedsNotEnabledException as well.
I would also turn on the
$enabledcheck by default. Now code would need to callexisting()with argumentTRUEto also check if the imported is enabled:This would break existing code as well.
I think that we needs tests that covers cases of disabled importers. It would be great if we had tests for the drush code too, but so far I didn't find out how to write for these and have them executed by the testbot.
Comment #7
megachriz@wmostrey
Do you like to write tests that cover cases of disabled importers? If you are not familiar with writing tests, would you like to get help with that?
Comment #8
wmostrey commented@MegaChriz
If you could point me into the right direction I'd be happy to write the tests. I have no experience with writing test for the drush code however.
Comment #9
megachrizAlright!
The first step in writing the tests is to figure out what to test. One scenario for example is that nothing should be imported for disabled importers, even if configured that imports happen periodically. An other one is that disabled importers should be editable. Can you think of other scenarios? The next step would be to roughly write the steps (in plain text) needed to fulfill the test, for example:
You could of course skip this step if you know right from the start how the test should be written (in code).
I haven't experience with writing tests for Drush either. If you would like to go down that path: the test suite for Drush is called 'Unish' and is based on PHPUnit. See drush/tests/Unish. I would use Drush 8 to base the tests on. See also https://github.com/drush-ops/drush/blob/master/tests/README.md
But maybe it's better to start with the Simpletest tests, as there aren't any drush tests for Feeds yet.
Let me know if I'm overwhelming you.
Comment #10
wmostrey commentedIn what .test file should these code go? Or shall I create a new one for these tests?
Comment #11
megachrizFor the case of editing a disabled importer, the test would be best placed in feeds_ui/feeds_ui.test.
For the case of involving periodic imports, tests/feeds_scheduler.test may be a good fit.
For other cases involving disabled importers I'm not sure yet which .test file would fit, so that may need a new file.
Have you made up a list of cases to test? Else the case of editing a disabled importer would be a great one to start with, as that is what this issue is trying to solve. It is unfortunate that there is no test coverage for other cases involving disabled importers, but I think we need them to prevent introducing new bugs.
Comment #12
wmostrey commentedHere is a simple test to see if I'm going in the right direction.
One thing I noticed in all feeds tests is that no t() functions are called for clickLink() or assertText() for example. Is this on purpose?
Comment #13
megachrizGreat start. Technically, the test for checking if the importer is available on the import page would more belong in the general Feeds tests, as the import page is also available when the Feeds Admin UI module is not enabled. For Feeds Admin UI the only test I think we need is to test if a disabled importer is editable (which is currently not).
About
assertText()andclickLink(): I think this is on purpose yes, because in most cases, the "final" text is checked, thus the text for which the translation placeholders have already been replaced. So there is no original translation string to check for. If I remember well, there were also occasions where only a portion of the text was checked. Since tests are always run in English (at least, by my knowledge), this should have no harm.Comment #14
wmostrey commentedOk here are two tests:
I'm not 100% sure the feeds_scheduler.test is created in the best way, feel free to comment.
Comment #15
megachrizYou are well on your way!
I see that
FeedsUIUserInterfaceTestCase::testDisabledFeeds()is passing. So - in this case - instead of asserting what should not be there, assert what you expect to be there. Actually, most of what happens already inFeedsUIUserInterfaceTestCase::testEditFeedConfiguration(). Maybe create a method calleddoEditFeedsConfiguration()and then call that method from both test methods?FeedsSchedulerTestCase::testScheduling()is probably a bad example: I would need to read the whole test method to get an idea of what it should all be able to do.Comment #16
wmostrey commentedI created two helper methods:
doEditFeedsConfiguration()for feeds_ui.test:testDisabledFeeds()andtestEditFeedConfiguration()initSyndication()for feeds_scheduler.test:testDisabledScheduling()andtestEnabledScheduling()Comment #18
megachrizThat's quite good. :)
Review
A few remarks:
doEditFeedsConfiguration()andinitSyndication()should live in the test classes where they are called, instead of feeds.test, in order to keep the related test code together. If it would be very generic code, feeds.test would be a good place.testDisabledScheduling()needs an extra call tocronRun(), as the other scheduling test has that as well. I don't remember the reason why there are two cron run calls though, maybe it is explained in one of the other tests.testScheduling(), renametestDisabledScheduling()totestSchedulingWithDisabledImporter()and renametestDisabledFeeds()totestEditDisabledImporter().Drush tests
I've made an attempt to write Drush integration tests for a disabled importer. See attached patch. Currently, when running these tests, Drush downloads the latest stable release of Feeds (7.x-2.0-beta3), which isn't very useful: it will then only test the Drush integration code as present in the stable release. I've yet to figure out how I can let Drush to pick the local copy of Feeds, so Drush tests can run using a patched version of Feeds.
I also don't know if the drupal.org testbot can support these Drush tests.
To run the Drush tests:
Replace '/path/to' with the appriopiate path to the directory in question.
Comment #19
wmostrey commentedHere's a patch that takes your remarks into account. I'll now dig into the Drush tests.
Comment #20
megachrizThe Drush integration tests patch
The Drush integration tests for Feeds are updated in the attached patch! Thanks @wmostrey for figuring out how to the execute the tests against the local copy of Feeds. Unish happens to install a complete Drupal website at a different location on the hard drive. In my case that is:
The Feeds directory and its contents are now copied to this folder.
How to run the Drush tests
The instructions for running the Drush tests as explained in #18 were not entirely clear. Here are some more detailed instructions:
In order the run Drush integration tests, Drush itself needs to be installed with its dev dependencies. Furthermore, the phpunit version that comes with Drush should be used for running the tests (instead of a globally installed phpunit), as that one has proven to be compatible with the Drush tests.
And ensure that the following text is displayed:
Especially note that Composer says 'including require-dev'. This means that the Drush dev dependencies are installed (including phpunit).
UNISH_NO_TIMEOUTS=1 UNISH_DRUPAL_MAJOR_VERSION=7 /path/to/drush/vendor/bin/phpunit --configuration /path/to/drush/tests /path/to/feeds/tests/drushReplace '/path/to' with the appropriate path to the directory in question. Also be sure to point to the phpunit version that comes with Drush.
So if Drush is installed in /users/megachriz/drush and the Drupal 7 site is installed in /users/megachriz/Sites/drupal7:
I've added these instructions to the README in the attached patch.
What's next?
I should look at the patch in #19. Assuming the tests in there are alright, the next step is to work on a fix. The last proposal for the fix was in #5, for which some remarks were made in #6.
Comment #21
megachriz@wmostrey
The tests look good. Some nitpicking: code comments should end with a dot.
Do you want to complete this issue by improving the fix, combined with all the tests from this issue?
Comment #22
wmostrey commentedThanks. Okay, I'm on it.
Comment #23
megachrizOkay, great!
Comment #24
wmostrey commentedHere's a first stab at it. I'm not quite sure what to put in
feeds.drush.inc'scatch (FeedsNotEnabledException $e) {Comment #25
megachrizThis is a though one. I've been thinking about it for a while. I'm not completely sure about adding a parameter to the
existing()method.Let's analyze. We have two situations to deal with:
existing()method.This code is expecting a FeedsNotExistingException if the importer or source should not be used for import or clear tasks.
feeds_importer_load().Code calling this method expect to retrieve an importer object, but only if it exist and is enabled.
The
existing()methodCode that is calling this method usually is in the following pattern:
So code would break if the
existing()method would suddenly return a FeedsNotEnabledException. In the first place because the code isn't prepared for such situation. Secondly, because you cannot catch two different types of exceptions at the same time (unless one is a parent of the other).Possible ways to fix this:
feeds_importer_load()If we change the (default) behavior of this method to return a feeds importer even if this is disabled, this could possibly break code that is expecting an usable importer (existing and enabled). However, I've seen code in other modules (like my feedspreview module) that actually will need to act on disabled importers once it becomes possible to edit disabled importers. Since it is also advertised as menu callback, I think we can justify an API change here.
To wrap up
Now that I've been through this analyze process I think we need two extra methods on the FeedsConfigurable class:
doesExist().isEnabled().Both methods would return a boolean (TRUE or FALSE). The existing
existing()method would then call both methods and throw a FeedsNotExistingException for each method returningFALSE.feeds_importer_load()would then load the importer and only return it ifdoesExist()returnsTRUE.In this scenario there would be no need for introducing a FeedsNotEnabledException class, I think.
What do you think?
Comment #26
wmostrey commentedGood thinking! Here's my take on it.
Comment #27
megachrizOkay, great! Looks good.
A few remarks:
doesExist()method.feeds_importer_load()can return a disabled importer, the text "The importer '@importer' does not exist or is not enabled." no longer is true. Furthermore, code should abort when the importer is disabled. So probably we need two texts here: one for not enabled and one for not existing. Abort in both cases. Changes need to be made indrush_feeds_import()anddrush_feeds_clear().doesExist()andisEnabled()could be a single line of code.Comment #28
wmostrey commentedRight,
drush_feeds_import()anddrush_feeds_clear().doesExist()andisEnabled().Comment #29
megachrizThanks, wmostrey. Code-wise this looks good!
I'll look into making
FeedsSource::doesExist()consistent.Comment #30
megachrizThis adds two methods to the
FeedsSourceobject:hasValidConfiguration(): checks if the source configuration is valid.FeedsSource::existing()uses this to throw a FeedsNotExistingException with a specific message.doesExist(): checks if both the importer and the source exist.I will do some manual tests in a few days and if I found no further issues, then I'll commit the patch!
Thanks for all your work, wmostrey!
Comment #31
megachrizForgot to include the interdiff.
Comment #32
megachrizJust noticed that the drush tests from #20 were missing. Added them.
Comment #34
megachrizI looked at all the code changes once more, executed the drush tests, and performed the following manual tests:
And I noticed two small things:
This is good, however I thought it would be useful to add how it can be enabled. Now it reads:
feeds_importer_load()previously catched a InvalidArgumentException. I found out that an exception like this could happen on multilingual sites or when Tamper configuration was not properly cleaned up after the deletion of an importer. See #1887632: Exception: Empty configuration identifier. in FeedsConfigurable::instance() (line 55 of feeds/includes/FeedsConfigurable.inc).. So I added that exception catching back.If this patch still passes tests, then I'll commit it!
Comment #36
megachrizCommitted #34.
Thanks again for all your work, wmostrey!