Followup to #1804688: Download and import interface translations.
Problem
The localization update test has project override data in the test module but the actual test file setup is in the test. @webchick suggested to make the test module more like a standalone test system which you can enable and see working like update.module's test. @berdir suggested we can make the .po files menu callbacks and manipulate the timestamps that way.
Proposed solution
Starting comment #52 a new solution is being developed. A genuine OO solution with better DX in creating and maintaining tests.
* Decide on architecture and test strategy (DX)
* Create a stand-alone mock module
* Write test for the mock module
* Refactor local module tests using the mock module
Comments
Comment #1
berdirHm.
My argument for providing the .po files as menu callbacks was actually that it would be possible to test the functions which actually download stuff from drupal.org. I think using the test module for manual tests isn't that useful, manual testing right now is only a problem because we don't have any real data to work with. That will change in the next months, I'm really not worried about that. I think people will want to test this with real data and real translations anyway :)
What happened since I suggested to do this is that guzzle got added to core, which has support for mocking HTTP requests ( It's an optional plugin that's not yet in core, but we can change that). We already started thinking about using that for the remote download tests, see #1447736-181: Adopt Guzzle library to replace drupal_http_request(). That would allow to make normal drupal.org requests in the remote download code, without any kind of local overrides or other hacks and guzzle would return what drupal.org would, as previously set up by the test. It would also allow to use DrupalUnitTestBase for these tests I think, which means fast tests! :)
I had a completely different idea right now that would not only help with testing but also when actually using it. In 7.x, l10n_update doesn't support dev releases, I guess that's still true now? Drupal allows to identify how recent a dev release is now, the version string looks like this: "7.x-1.0-alpha1+29-dev". We should be able to fall back to "7.x-1.0-alpha1" and download translations for that. That would allow people to download 8.x-dev as soon as an unstable/alpha/whatever release is out and download translations. Should we open an issue for this?
Comment #2
yesct commentedFalling back on a previous version .po file seems like a good idea to me, as does opening an issue for that.
Comment #3
gábor hojtsy@YesCT, @Berdir: Fallback should definitely be unrelated :)
I must say I'm a bit surprised because I totally read you @Berdir as supporting @webchick's request to make the test module standalone so it integrates in itself (without the tests setting up the data) with the l10n_update system and can be used to interactively test the functionality by humans until something becomes available for Drupal 8 as real translations. I think this would still hold great value, if only to make it much easier for @webchick (and any patch reviewers for that matter) to evaluate fixes/changes to this system as needed.
Comment #4
yesct commentedIs the suggestion to make the test treat the test .po files it contains act as if the come from l.d.o?
And not to fool it into using the actual 7.x l.d.o po files?
So to be able to test with a .po file for Spanish for views, a 7.x po file would be manually downloaded from l.d.o, renamed and added to the test module? The views module .info file would have to be changed to a alpha (non dev) release too?
Comment #5
gábor hojtsyThis is a suggestion to not change any of the logic but move the .po file setup from the tests to the test module, so you can enable the test module and see it working (i.e downloading the .po files from the "remote", etc). Having tests that depend on localize.drupal.org is not really feasible, they need to be able to stand alone.
Comment #6
yesct commentedWhat about for manual testing or live usability testing?
Comment #7
gábor hojtsyI think we will need a temper module for usability testing.
Comment #8
berdirFor clarification, webchick and I wanted the same thing, but for a different reason. My reason was better automated test coverage, and as explained above, I think we have now better ways to do that with Guzzle.
The fallback stuff is of course a separate issue, that thought just occured to me while I was responding here and I wanted to write it down :)
Comment #9
yesct commented:) #1862622: localization fallback so people can test translations with dev releases
Comment #10
balintbrewsI'll try to tackle this issue.
Comment #11
gábor hojtsy@balintk: thanks a lot!
Comment #12
sutharsan commentedI have not investigated yet how Update modules made its test, as Berdir suggested in http://drupal.org/node/1804688#comment-6784366. That could be a starting point.
My thought was to replace the programatically generated modules, which is how the current test does it, by 'physical' modules. The attached patch is a POC for such a module. It adds a module locale_test_contrib_one which has a translation file on a remote url. Just like locale_test_translation is a module with local po file, locale_test_contrib_one is a module with remote po file. The patch is over a week old and my not apply, but perhaps it is of some help.
Comment #13
balintbrewsI'll look into your work. (Assigning the issue back to me.)
Comment #14
sutharsan commentedOne of Berdir's suggestions was to provide an URL where a translation can be downloaded, instead of a 'physical' file as provided by locale_test_translation. I'm not sure if this will work with the time stamp that the download/import requires (
locale_translation_http_check()) . If it works it can be used for all remote translation URL's. Needs investigation.Some thought about the above patch. The functions and (timestamp) definitions required by this module can be moved to the locale_test. All locale_test_* modules can now use this code.
Each locale_test_* modules takes care of all changes required for this module to mock a contrib module or a custom module. It changes how it is listed (
locale_translation_project_list()), sets the initial timestamp of the translation file and sets the URL where the remote translation is found.Comment #15
gábor hojtsy@Sutharsan: I think the idea with the menu callback is that it would set HTTP headers about time explicitly, overriding any Drupal set defaults.
Comment #16
sutharsan commentedLong IRC discussion. May be usefull to others who want to know more about the internals of Locale's po detection.
Comment #17
gábor hojtsyIf we can only move the remote or local tests at first, that also works. It was kind of hard for me to follow the thinking in the IRC log, although I admit I did not have the time to read it line by line, just managed to skim through.
Comment #18
balintbrewsI can summarize that IRC discussion. @Sutharsan was teaching me about how Locale module detects translation files, what the different scenarios are and so forth. In the end he came to the conclusion that we should incorporate @Berdir's menu callback idea for testing remote translation files, but keep "physical" po files for testing local translation files, so that we stick to the reality as much as possible.
I'm on this issue and probably deliver a patch by the end of the weekend. Sorry for the delay.
Comment #19
balintbrewsHere is the work I've done so far. The patch doesn't change anything yet in the current tests, it only adds a new mock contrib module and helper functions. I would love to hear some feedback, and if you find this approach good, my plan is to create two more mock contrib modules and one custom. They will be very similar to the existing one, but will ship with different po files with different timestamps sticking to the scenarios in the current working tests.
Comment #20
gábor hojtsyGenerally looks good to me :)
I'm a bit puzzled by the extent of the FinishResponseSubscriber trickery. Is the base implementation coming from Symfony so we cannot modify it for our needs or people were against being able to modify this header from the application layer?
Comment #22
balintbrewsI had a discussion with @chx on IRC, I've changed the event subscriber for working around the 'Last-Modified' HTTP header override issue based on his suggestions. So now it adds a new subscriber instead of overriding an existing one.
@Gábor:
FinishResponseSubscriberdoesn't come from Symfony, so I guess we can improve it to at least respect HTTP headers when they are already set and then we could dismiss this trickery with our subscriber and make the test module way more understandable.Comment #23
gábor hojtsy@balintk: I can see several use cases where a custom timestamp would be generally good to be possible to set, eg. if you sell digital downloads (so you need to authenticate and pipe them through PHP/Drupal) and want to represent their original change/creation time in the response, which sounds like a pretty natural requirement to me. So I think this has uses in the base system and would be a pity if not supported there.
Comment #24
balintbrews@Gábor: Agreed, so how about opening an issue (I can take care of this later) for fixing it in
FinishResponseSubscriberand remove the trickery from here once that fix is committed, but leave the event subscriber here until then to be able to run successful tests?Comment #25
gábor hojtsySounds good to me!
Comment #26
sutharsan commentedGenerally looks good, looking forward to the next step. I think you should optimize for understandability of the code.
Write positive sentences, double negative is much harder to understand. e.g. This is a workaround to make sure the 'Last-Modified' HTTP header is not overridden by FinishResponseSubscriber ...
Add: By default the timestamp is the current time.
This seems like a very complex way to build the file uri. While at the same time locale_test_copy_translation_file() depends on the po file to be in the module's directory. And I think you can do the same here too.
Difficult sentence. Suggestion: 'Set the timestamp of the newly copied file.' or 'Use the timestamp as the creation date of the copied file.'
Are there really helper function? Just describe what it does.
Needs more docs. It is important to understand what happens here. A crucial action of the module.
How about defining all module properties here as constants. Local po timestamp, remote po timestamp and perhaps even module name, version, language code, and perhaps even local po file name, and remote po filename. In this way the only difference between locale_test_contrib_one and *_two and *_three would be a set of definitions at the start of the module.
Comment #27
yesct commentedJust want to add a link for reference in terms of the comment about adding default to the comments.
http://drupal.org/node/1354#functions
requires it. It is not just Sutharsan being meticulous. :)
It also specifies that optional should be in paren's ().
Comment #28
yesct commented@balintk Did you have any intermediate work to post back?
Comment #29
gábor hojtsy@balintk: any updates?
Comment #30
balintbrewsI came back from vacation today, had been away from my computer during the holidays. So no progress for a while, but I'm eager to continue, only that I probably won't have time before the weekend. If that's too late, someone can take the issue over, otherwise I will be happy to proceed, especially after this great feedback! :)
Comment #31
balintbrewsThank you very much for the fantastic feedback. I went through all your suggestions; improved comments, simplified some code, introduced constants everywhere. I really like the idea of constants.
This is what I had time for during the weekend. The next step is producing two new contrib and one custom mockup module which will be almost identical with the existing one, but with the help of constants, the differences can be defined easily. After that, reworking
LocaleUpdateTest. I'll continue working on these tasks this the week. Thank you for your patience.Comment #32
sutharsan commented@balintk, Nice work. Most is of my comments are on documentation. The purpose of the module is to provide some magic, so we should be clear about what is does and why.
locale_test module should unhide all locale_test_* modules.
Variable names are inconsistent and not proper English. Use $translation_directory instead of $translate_directory
The use of drupal_realpath() is discouraged. Is it needed here? If so, add some comment.
Add some comment explaining to disable the 'hidden = TRUE' line for manual testing.
Add some comment here to describe the concepts and the purpose of this module. And how to use it in manual testing.
This is magic. But looking at the .info file one would think the default remote po path will be used. Add some comment in the .info file which points to this function.
Use a constant for this magic string: 'locale-test-remote-translations'
Don't hard code '8.x' use DRUPAL_CORE_COMPATIBILITY instead.
Use ':' instead of ';'. Serves instead of serves.
The comment can be moved to the function's comment. Use less words (don't make me think (tm) )e.g. 'essentially the exact same' is 'the same'.
Comment #33
gábor hojtsy@balintk: any updates? :)
Comment #34
balintbrews@Sutharsan: thanks for the thorough review again. I incorporated all your suggestions into this new patch.
file_transfer()needs the absolute local filesystem path, and I've seen thatdrupal_realpath()is used inlocale_translation_source_check_file(), that's why I decided to do the same here. Do you have a better suggestion?You referred to the .install file, but I added the comments to the .module file, because I think most people would look at that file first.
Comment #35
balintbrewsI have forgotten this one:
Comment #36
gábor hojtsyCleaning up sprint board.
Comment #37
sutharsan commentedIf there is no alternative for drupal_realpath() then, add a line of comment. But file_transfer() has been removed, see http://drupal.org/node/1957078. So it's back to the drawing board ;)
Back to needs work, and for #35 too.
Comment #38
berdirAs said before, @webchick and I had different reasons to do this issue.
As far as I understand, we now support 8.x translation downloads without any test modules, you can just pick the language and it will get whatever is there. That, IMHO, invalidates the reason @webchick had for this issue.
My reason is being able to provide better test coverage for this functionality, which we now should be able to do using a mocked guzzle client, once the guzzle conversion is through. That should be possible using fast DUBT tests, I'm not actually sure if it's possible to mock guzzle requests on drupalGet()/Post requests.
Comment #39
sutharsan commentedChanging assignment after talking with @balinkt in IRC.
Comment #40
yesct commented@Berdir
Since there is no 8.x release, and we dont have fallback, adding a language does not find any translation file to load.
... your #1 and my #9 speak to still wanting this easy to enable module, right?
Comment #41
sutharsan commentedRe-roll of #34 with required changes from .info to .info.yml
Comment #42
yesct commentedComment #43
yesct commentedoverrides and implements are {@inheritdoc} now.
http://drupal.org/node/1354#inheritdoc
This is Contains and full path with \ in all comments for namespaces.
I added a @var block.. is it a string?
missing @param
mmm callbacks.
check into #1971384: [META] Convert page callbacks to controllers
======
why are all these -do-not-test.patch ?
Comment #44
jair commentedComment #45
sutharsan commentedPAtch re-rolled.
Comment #46
sutharsan commentedOops, re-rolled the wrong patch :(
Re-roll of #43.
Comment #51
sutharsan commentedRe-roll patch #46
Comment #52
sutharsan commentedAfter many years I'm circling back to locale module.
It is almost 5 years since serious work was done on this issue. Although the patch still applies, the API it uses is partly gone and the architecture is very outdated. Still I see the need to improve the way we mock modules and translations for interface translation tests.
Issue summary:
#38 and #40:
This use case has become less demanding since translations files for both stable and dev releases are available. But when developing off-line and when developing tests there is still a demand for an easy way to mock modules and their translations. The current approach is backed into the test script which makes it hard to reuse outside the tests and hard to make changes or additions.
@berdir in #38:
As far as I understand, Guzzle's MockPlugin can only be used with the requests from Guzzle's Client. An other library like InterNations/http-mock could be used to mock any http response. But mocking the http reponse is probably only needed for unit test. When the full Drupal stack is available, a Drupal response can be used too.
Comment #53
sutharsan commentedMy plan is to make a module that uses a yaml type plugin to register mock projects. The module serves as a project repository. Mock projects can be provides on demand. Each project can have a local and a remote translation source file. The local source file is copied (on demand) to the translations:// directory. The remote source file is made available (on demand) via a dynamic route.
Comment #54
sutharsan commentedComment #55
sutharsan commentedThis patch contains a module locale_test_mock that provides project and translation mock files which can be enabled via by code from within the tests. More details can be found in the README.txt.
I am currently looking for a review at conceptual and architectural level, as well as DX feedback.
No interdiff provided as this is a completely new start.
Comment #56
sutharsan commentedComment #62
sutharsan commentedComment #63
sutharsan commented