L10nUpdateTestBase.test 10 KB

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