1<?php
2
3namespace Drupal\Tests\Core\Site;
4
5use Drupal\Core\Site\Settings;
6use Drupal\Tests\Traits\ExpectDeprecationTrait;
7use Drupal\Tests\UnitTestCase;
8use org\bovigo\vfs\vfsStream;
9
10/**
11 * @coversDefaultClass \Drupal\Core\Site\Settings
12 * @group Site
13 */
14class SettingsTest extends UnitTestCase {
15
16  use ExpectDeprecationTrait;
17
18  /**
19   * Simple settings array to test against.
20   *
21   * @var array
22   */
23  protected $config = [];
24
25  /**
26   * The class under test.
27   *
28   * @var \Drupal\Core\Site\Settings
29   */
30  protected $settings;
31
32  /**
33   * @covers ::__construct
34   */
35  protected function setUp() {
36    $this->config = [
37      'one' => '1',
38      'two' => '2',
39      'hash_salt' => $this->randomMachineName(),
40    ];
41    $this->settings = new Settings($this->config);
42  }
43
44  /**
45   * @covers ::get
46   */
47  public function testGet() {
48    // Test stored settings.
49    $this->assertEquals($this->config['one'], Settings::get('one'), 'The correct setting was not returned.');
50    $this->assertEquals($this->config['two'], Settings::get('two'), 'The correct setting was not returned.');
51
52    // Test setting that isn't stored with default.
53    $this->assertEquals('3', Settings::get('three', '3'), 'Default value for a setting not properly returned.');
54    $this->assertNull(Settings::get('four'), 'Non-null value returned for a setting that should not exist.');
55  }
56
57  /**
58   * @covers ::getAll
59   */
60  public function testGetAll() {
61    $this->assertEquals($this->config, Settings::getAll());
62  }
63
64  /**
65   * @covers ::getInstance
66   */
67  public function testGetInstance() {
68    $singleton = $this->settings->getInstance();
69    $this->assertEquals($singleton, $this->settings);
70  }
71
72  /**
73   * Tests Settings::getHashSalt();
74   *
75   * @covers ::getHashSalt
76   */
77  public function testGetHashSalt() {
78    $this->assertSame($this->config['hash_salt'], $this->settings->getHashSalt());
79  }
80
81  /**
82   * Tests Settings::getHashSalt() with no hash salt value.
83   *
84   * @covers ::getHashSalt
85   *
86   * @dataProvider providerTestGetHashSaltEmpty
87   */
88  public function testGetHashSaltEmpty(array $config) {
89    // Re-create settings with no 'hash_salt' key.
90    $settings = new Settings($config);
91    $this->expectException(\RuntimeException::class);
92    $settings->getHashSalt();
93  }
94
95  /**
96   * Data provider for testGetHashSaltEmpty.
97   *
98   * @return array
99   */
100  public function providerTestGetHashSaltEmpty() {
101    return [
102      [[]],
103      [['hash_salt' => '']],
104      [['hash_salt' => NULL]],
105    ];
106  }
107
108  /**
109   * Ensures settings cannot be serialized.
110   *
111   * @covers ::__sleep
112   */
113  public function testSerialize() {
114    $this->expectException(\LogicException::class);
115    serialize(new Settings([]));
116  }
117
118  /**
119   * Tests Settings::getApcuPrefix().
120   *
121   * @covers ::getApcuPrefix
122   */
123  public function testGetApcuPrefix() {
124    $settings = new Settings([
125      'hash_salt' => 123,
126      'apcu_ensure_unique_prefix' => TRUE,
127    ]);
128    $this->assertNotEquals($settings::getApcuPrefix('cache_test', '/test/a'), $settings::getApcuPrefix('cache_test', '/test/b'));
129
130    $settings = new Settings([
131      'hash_salt' => 123,
132      'apcu_ensure_unique_prefix' => FALSE,
133    ]);
134    $this->assertNotEquals($settings::getApcuPrefix('cache_test', '/test/a'), $settings::getApcuPrefix('cache_test', '/test/b'));
135  }
136
137  /**
138   * Tests that an exception is thrown when settings are not initialized yet.
139   *
140   * @covers ::getInstance
141   */
142  public function testGetInstanceReflection() {
143    $settings = new Settings([]);
144
145    $class = new \ReflectionClass(Settings::class);
146    $instace_property = $class->getProperty("instance");
147    $instace_property->setAccessible(TRUE);
148    $instace_property->setValue(NULL);
149
150    $this->expectException(\BadMethodCallException::class);
151    $settings->getInstance();
152  }
153
154  /**
155   * @runInSeparateProcess
156   * @group legacy
157   * @covers ::__construct
158   * @dataProvider configDirectoriesBcLayerProvider
159   */
160  public function testConfigDirectoriesBcLayer($settings_file_content, $directory, $expect_deprecation) {
161    global $config_directories;
162    $class_loader = NULL;
163
164    $vfs_root = vfsStream::setup('root');
165    $sites_directory = vfsStream::newDirectory('sites')->at($vfs_root);
166    vfsStream::newFile('settings.php')
167      ->at($sites_directory)
168      ->setContent($settings_file_content);
169
170    if ($expect_deprecation) {
171      $this->addExpectedDeprecationMessage('$config_directories[\'sync\'] has moved to $settings[\'config_sync_directory\']. See https://www.drupal.org/node/3018145.');
172    }
173
174    Settings::initialize(vfsStream::url('root'), 'sites', $class_loader);
175    $this->assertSame($directory, Settings::get('config_sync_directory'));
176    $this->assertSame($directory, $config_directories['sync']);
177  }
178
179  /**
180   * Data provider for self::testConfigDirectoriesBcLayer().
181   */
182  public function configDirectoriesBcLayerProvider() {
183    $no_config_directories = <<<'EOD'
184<?php
185$settings['config_sync_directory'] = 'foo';
186EOD;
187
188    $only_config_directories = <<<'EOD'
189<?php
190$config_directories['sync'] = 'bar';
191EOD;
192
193    $both = <<<'EOD'
194<?php
195$settings['config_sync_directory'] = 'foo';
196$config_directories['sync'] = 'bar';
197EOD;
198
199    return [
200      'Only $settings[\'config_sync_directory\']' => [
201        $no_config_directories,
202        'foo',
203        FALSE,
204      ],
205      'Only $config_directories' => [$only_config_directories, 'bar', TRUE],
206      'Both' => [$both, 'foo', FALSE],
207    ];
208  }
209
210  /**
211   * @runInSeparateProcess
212   * @group legacy
213   */
214  public function testConfigDirectoriesBcLayerEmpty() {
215    global $config_directories;
216    $class_loader = NULL;
217
218    $vfs_root = vfsStream::setup('root');
219    $sites_directory = vfsStream::newDirectory('sites')->at($vfs_root);
220    vfsStream::newFile('settings.php')->at($sites_directory)->setContent(<<<'EOD'
221<?php
222$settings = [];
223EOD
224    );
225
226    Settings::initialize(vfsStream::url('root'), 'sites', $class_loader);
227    $this->assertNull(Settings::get('config_sync_directory'));
228    $this->assertNull($config_directories);
229  }
230
231  /**
232   * @runInSeparateProcess
233   * @group legacy
234   */
235  public function testConfigDirectoriesBcLayerMultiple() {
236    global $config_directories;
237    $class_loader = NULL;
238
239    $vfs_root = vfsStream::setup('root');
240    $sites_directory = vfsStream::newDirectory('sites')->at($vfs_root);
241    vfsStream::newFile('settings.php')->at($sites_directory)->setContent(<<<'EOD'
242<?php
243$settings['config_sync_directory'] = 'foo';
244$config_directories['sync'] = 'bar';
245$config_directories['custom'] = 'custom';
246EOD
247    );
248
249    Settings::initialize(vfsStream::url('root'), 'sites', $class_loader);
250    $this->assertSame('foo', Settings::get('config_sync_directory'));
251    $this->assertSame('foo', $config_directories['sync']);
252    $this->assertSame('custom', $config_directories['custom']);
253  }
254
255}
256