L10nUpdateTestBase.test 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /**
  3. * @file
  4. * Contains L10nUpdateTest.
  5. */
  6. /**
  7. * Tests for update translations.
  8. */
  9. class L10nUpdateTestBase extends DrupalWebTestCase {
  10. /**
  11. * Timestamp for an old translation.
  12. *
  13. * @var integer
  14. */
  15. protected $timestamp_old;
  16. /**
  17. * Timestamp for a medium aged translation.
  18. *
  19. * @var integer
  20. */
  21. protected $timestamp_medium;
  22. /**
  23. * Timestamp for a new translation.
  24. *
  25. * @var integer
  26. */
  27. protected $timestamp_new;
  28. function setUp() {
  29. parent::setUp('update', 'locale', 'l10n_update', 'l10n_update_test');
  30. // Setup timestamps to identify old and new translation sources.
  31. $this->timestamp_old = REQUEST_TIME - 300;
  32. $this->timestamp_medium = REQUEST_TIME - 200;
  33. $this->timestamp_new = REQUEST_TIME - 100;
  34. $this->timestamp_now = REQUEST_TIME;
  35. }
  36. /**
  37. * Sets the value of the default translations directory.
  38. *
  39. * @param string $path
  40. * Path of the translations directory relative to the drupal installation
  41. * directory.
  42. */
  43. protected function setTranslationsDirectory($path) {
  44. file_prepare_directory($path, FILE_CREATE_DIRECTORY);
  45. variable_set('l10n_update_download_store', $path);
  46. }
  47. /**
  48. * Adds a language.
  49. *
  50. * @param $langcode
  51. * The language code of the language to add.
  52. */
  53. protected function addLanguage($langcode) {
  54. $edit = array('langcode' => $langcode);
  55. $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
  56. drupal_static_reset('language_list');
  57. $languages = language_list();
  58. $this->assertTrue(isset($languages[$langcode]), format_string('Language %langcode added.', array('%langcode' => $langcode)));
  59. }
  60. /**
  61. * Creates a translation file and tests its timestamp.
  62. *
  63. * @param string $path
  64. * Path of the file relative to the public file path.
  65. * @param string $filename
  66. * Name of the file to create.
  67. * @param integer $timestamp
  68. * Timestamp to set the file to. Defaults to current time.
  69. * @param array $translations
  70. * Array of source/target value translation strings. Only singular strings
  71. * are supported, no plurals. No double quotes are allowed in source and
  72. * translations strings.
  73. */
  74. protected function makePoFile($path, $filename, $timestamp = NULL, $translations = array()) {
  75. $timestamp = $timestamp ? $timestamp : REQUEST_TIME;
  76. $path = 'public://' . $path;
  77. $text = '';
  78. $po_header = <<<EOF
  79. msgid ""
  80. msgstr ""
  81. "Project-Id-Version: Drupal 7\\n"
  82. "MIME-Version: 1.0\\n"
  83. "Content-Type: text/plain; charset=UTF-8\\n"
  84. "Content-Transfer-Encoding: 8bit\\n"
  85. "Plural-Forms: nplurals=2; plural=(n > 1);\\n"
  86. EOF;
  87. // Convert array of translations to Gettext source and translation strings.
  88. if ($translations) {
  89. foreach ($translations as $source => $target) {
  90. $text .= 'msgid "'. $source . '"' . "\n";
  91. $text .= 'msgstr "'. $target . '"' . "\n";
  92. }
  93. }
  94. file_prepare_directory($path, FILE_CREATE_DIRECTORY);
  95. $file = (object) array(
  96. 'uid' => 1,
  97. 'filename' => $filename,
  98. 'uri' => $path . '/' . $filename,
  99. 'filemime' => 'text/x-gettext-translation',
  100. 'timestamp' => $timestamp,
  101. 'status' => FILE_STATUS_PERMANENT,
  102. );
  103. file_put_contents($file->uri, $po_header . $text);
  104. touch(drupal_realpath($file->uri), $timestamp);
  105. file_save($file);
  106. }
  107. /**
  108. * Setup the environment containing local and remote translation files.
  109. *
  110. * Update tests require a simulated environment for local and remote files.
  111. * Normally remote files are located at a remote server (e.g. ftp.drupal.org).
  112. * For testing we can not rely on this. A directory in the file system of the
  113. * test site is designated for remote files and is addressed using an absolute
  114. * URL. Because Drupal does not allow files with a po extension to be accessed
  115. * (denied in .htaccess) the translation files get a _po extension. Another
  116. * directory is designated for local translation files.
  117. *
  118. * The environment is set up with the following files. File creation times are
  119. * set to create different variations in test conditions.
  120. * contrib_module_one
  121. * - remote file: timestamp new
  122. * - local file: timestamp old
  123. * - current: timestamp medium
  124. * contrib_module_two
  125. * - remote file: timestamp old
  126. * - local file: timestamp new
  127. * - current: timestamp medium
  128. * contrib_module_three
  129. * - remote file: timestamp old
  130. * - local file: timestamp old
  131. * - current: timestamp medium
  132. * custom_module_one
  133. * - local file: timestamp new
  134. * - current: timestamp medium
  135. * Time stamp of current translation set by setCurrentTranslations() is always
  136. * timestamp medium. This makes it easy to predict which translation will be
  137. * imported.
  138. */
  139. protected function setTranslationFiles() {
  140. // A flag is set to let the l10n_update_test module replace the project data with
  141. // a set of test projects which match the below project files.
  142. variable_set('l10n_update_test_projects_alter', TRUE);
  143. // Setup the environment.
  144. $public_path = drupal_realpath('public://');
  145. $this->setTranslationsDirectory($public_path . '/local');
  146. variable_set('l10n_update_default_filename', '%project-%release.%language._po');
  147. // Setting up sets of translations for the translation files.
  148. $translations_one = array('January' => 'Januar_1', 'February' => 'Februar_1', 'March' => 'Marz_1');
  149. $translations_two = array( 'February' => 'Februar_2', 'March' => 'Marz_2', 'April' => 'April_2');
  150. $translations_three = array('April' => 'April_3', 'May' => 'Mai_3', 'June' => 'Juni_3');
  151. // Add a number of files to the local file system to serve as remote
  152. // translation server and match the project definitions set in
  153. // l10n_update_test_l10n_update_projects_alter().
  154. $this->makePoFile('remote/7.x/contrib_module_one', 'contrib_module_one-7.x-1.1.de._po', $this->timestamp_new, $translations_one);
  155. $this->makePoFile('remote/7.x/contrib_module_two', 'contrib_module_two-7.x-2.0-beta4.de._po', $this->timestamp_old, $translations_two);
  156. $this->makePoFile('remote/7.x/contrib_module_three', 'contrib_module_three-7.x-1.0.de._po', $this->timestamp_old, $translations_three);
  157. // Add a number of files to the local file system to serve as local
  158. // translation files and match the project definitions set in
  159. // l10n_update_test_l10n_update_projects_alter().
  160. $this->makePoFile('local', 'contrib_module_one-7.x-1.1.de._po', $this->timestamp_old, $translations_one);
  161. $this->makePoFile('local', 'contrib_module_two-7.x-2.0-beta4.de._po', $this->timestamp_new, $translations_two);
  162. $this->makePoFile('local', 'contrib_module_three-7.x-1.0.de._po', $this->timestamp_old, $translations_three);
  163. $this->makePoFile('local', 'custom_module_one.de.po', $this->timestamp_new);
  164. }
  165. /**
  166. * Setup existing translations in the database and set up the status of
  167. * existing translations.
  168. */
  169. protected function setCurrentTranslations() {
  170. // Setup to add German translations to the database.
  171. $langcode = 'de';
  172. $writer = new PoDatabaseWriter();
  173. $writer->setLangcode($langcode);
  174. $writer->setOptions(array(
  175. 'overwrite_options' => array(
  176. 'not_customized' => TRUE,
  177. 'customized' => TRUE,
  178. ),
  179. ));
  180. // Add non customized translations to the database.
  181. $writer->setOptions(array('customized' => L10N_UPDATE_NOT_CUSTOMIZED));
  182. $non_customized_translations = array(
  183. 'March' => 'Marz',
  184. 'June' => 'Juni',
  185. );
  186. foreach ($non_customized_translations as $source => $translation) {
  187. $poItem = new PoItem();
  188. $poItem->setFromArray(array(
  189. 'source' => $source,
  190. 'translation' => $translation,
  191. ));
  192. $writer->writeItem($poItem);
  193. }
  194. // Add customized translations to the database.
  195. $writer->setOptions(array('customized' => L10N_UPDATE_CUSTOMIZED));
  196. $customized_translations = array(
  197. 'January' => 'Januar_customized',
  198. 'February' => 'Februar_customized',
  199. 'May' => 'Mai_customized',
  200. );
  201. foreach ($customized_translations as $source => $translation) {
  202. $poItem = new PoItem();
  203. $poItem->setFromArray(array(
  204. 'source' => $source,
  205. 'translation' => $translation,
  206. ));
  207. $writer->writeItem($poItem);
  208. }
  209. // Add a state of current translations in l10n_update_files.
  210. $default = array(
  211. 'language' => $langcode,
  212. 'uri' => '',
  213. 'timestamp' => $this->timestamp_medium,
  214. 'last_checked' => $this->timestamp_medium,
  215. );
  216. $data[] = array(
  217. 'project' => 'contrib_module_one',
  218. 'filename' => 'contrib_module_one-7.x-1.1.de._po',
  219. 'version' => '7.x-1.1',
  220. );
  221. $data[] = array(
  222. 'project' => 'contrib_module_two',
  223. 'filename' => 'contrib_module_two-7.x-2.0-beta4.de._po',
  224. 'version' => '7.x-2.0-beta4',
  225. );
  226. $data[] = array(
  227. 'project' => 'contrib_module_three',
  228. 'filename' => 'contrib_module_three-7.x-1.0.de._po',
  229. 'version' => '7.x-1.0',
  230. );
  231. $data[] = array(
  232. 'project' => 'custom_module_one',
  233. 'filename' => 'custom_module_one.de.po',
  234. 'version' => '',
  235. );
  236. foreach ($data as $file) {
  237. $file = (object) array_merge($default, $file);
  238. drupal_write_record('l10n_update_file', $file);
  239. }
  240. }
  241. /**
  242. * Checks the translation of a string.
  243. *
  244. * @param string $source
  245. * Translation source string
  246. * @param string $translation
  247. * Translation to check. Use empty string to check for a not existing
  248. * translation.
  249. * @param string $langcode
  250. * Language code of the language to translate to.
  251. * @param string $message
  252. * (optional) A message to display with the assertion.
  253. */
  254. protected function assertTranslation($source, $translation, $langcode, $message = '') {
  255. $db_translation = db_query('SELECT translation FROM {locales_target} lt INNER JOIN {locales_source} ls ON ls.lid = lt.lid WHERE ls.source = :source AND lt.language = :langcode', array(':source' => $source, ':langcode' => $langcode))->fetchField();
  256. $db_translation = $db_translation == FALSE ? '' : $db_translation;
  257. $this->assertEqual($translation, $db_translation, $message ? $message : format_string('Correct translation of %source (%language)', array('%source' => $source, '%language' => $langcode)));
  258. }
  259. }