1<?php
2
3/**
4 * @file
5 * Contains \Drupal\Tests\Core\Logger\LoggerChannelTest.
6 */
7
8namespace Drupal\Tests\Core\Logger;
9
10use Drupal\Core\Logger\LoggerChannel;
11use Drupal\Core\Session\AccountInterface;
12use Drupal\Tests\UnitTestCase;
13use Symfony\Component\HttpFoundation\Request;
14use Symfony\Component\HttpFoundation\RequestStack;
15use Psr\Log\LoggerInterface;
16use Psr\Log\LoggerTrait;
17
18/**
19 * @coversDefaultClass \Drupal\Core\Logger\LoggerChannel
20 * @group Logger
21 */
22class LoggerChannelTest extends UnitTestCase {
23
24  /**
25   * Tests LoggerChannel::log().
26   *
27   * @param callable $expected
28   *   An anonymous function to use with $this->callback() of the logger mock.
29   *   The function should check the $context array for expected values.
30   * @param \Symfony\Component\HttpFoundation\Request $request
31   *   Will be passed to the channel under test if present.
32   * @param \Drupal\Core\Session\AccountInterface $current_user
33   *   Will be passed to the channel under test if present.
34   *
35   * @dataProvider providerTestLog
36   * @covers ::log
37   * @covers ::setCurrentUser
38   * @covers ::setRequestStack
39   */
40  public function testLog(callable $expected, Request $request = NULL, AccountInterface $current_user = NULL) {
41    $channel = new LoggerChannel('test');
42    $message = $this->randomMachineName();
43    $logger = $this->createMock('Psr\Log\LoggerInterface');
44    $logger->expects($this->once())
45      ->method('log')
46      ->with($this->anything(), $message, $this->callback($expected));
47    $channel->addLogger($logger);
48    if ($request) {
49      $requestStack = new RequestStack();
50      $requestStack->push($request);
51      $channel->setRequestStack($requestStack);
52    }
53    if ($current_user) {
54      $channel->setCurrentUser($current_user);
55    }
56    $channel->log(rand(0, 7), $message);
57  }
58
59  /**
60   * Tests LoggerChannel::log() recursion protection.
61   *
62   * @covers ::log
63   */
64  public function testLogRecursionProtection() {
65    $channel = new LoggerChannel('test');
66    $logger = $this->createMock('Psr\Log\LoggerInterface');
67    $logger->expects($this->exactly(LoggerChannel::MAX_CALL_DEPTH))
68      ->method('log');
69    $channel->addLogger($logger);
70    $channel->addLogger(new NaughtyRecursiveLogger($channel));
71    $channel->log(rand(0, 7), $this->randomMachineName());
72  }
73
74  /**
75   * Tests LoggerChannel::addLoggers().
76   *
77   * @covers ::addLogger
78   * @covers ::sortLoggers
79   */
80  public function testSortLoggers() {
81    $channel = new LoggerChannel($this->randomMachineName());
82    $index_order = '';
83    for ($i = 0; $i < 4; $i++) {
84      $logger = $this->createMock('Psr\Log\LoggerInterface');
85      $logger->expects($this->once())
86        ->method('log')
87        ->will($this->returnCallback(function () use ($i, &$index_order) {
88          // Append the $i to the index order, so that we know the order that
89          // loggers got called with.
90          $index_order .= $i;
91        }));
92      $channel->addLogger($logger, $i);
93    }
94
95    $channel->log(rand(0, 7), $this->randomMachineName());
96    // Ensure that the logger added in the end fired first.
97    $this->assertEquals($index_order, '3210');
98  }
99
100  /**
101   * Data provider for self::testLog().
102   */
103  public function providerTestLog() {
104    $account_mock = $this->createMock('Drupal\Core\Session\AccountInterface');
105    $account_mock->expects($this->any())
106      ->method('id')
107      ->will($this->returnValue(1));
108
109    $request_mock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')
110      ->setMethods(['getClientIp'])
111      ->getMock();
112    $request_mock->expects($this->any())
113      ->method('getClientIp')
114      ->will($this->returnValue('127.0.0.1'));
115    $request_mock->headers = $this->createMock('Symfony\Component\HttpFoundation\ParameterBag');
116
117    // No request or account.
118    $cases[] = [
119      function ($context) {
120        return $context['channel'] == 'test' && empty($context['uid']) && empty($context['ip']);
121      },
122    ];
123    // With account but not request. Since the request is not available the
124    // current user should not be used.
125    $cases[] = [
126      function ($context) {
127        return $context['uid'] === 0 && empty($context['ip']);
128      },
129      NULL,
130      $account_mock,
131    ];
132    // With request but not account.
133    $cases[] = [
134      function ($context) {
135        return $context['ip'] === '127.0.0.1' && empty($context['uid']);
136      },
137      $request_mock,
138    ];
139    // Both request and account.
140    $cases[] = [
141      function ($context) {
142        return $context['ip'] === '127.0.0.1' && $context['uid'] === 1;
143      },
144      $request_mock,
145      $account_mock,
146    ];
147    return $cases;
148  }
149
150}
151
152class NaughtyRecursiveLogger implements LoggerInterface {
153  use LoggerTrait;
154
155  protected $channel;
156  protected $message;
157
158  public function __construct(LoggerChannel $channel) {
159    $this->channel = $channel;
160  }
161
162  public function log($level, $message, array $context = []) {
163    $this->channel->log(rand(0, 7), $message, $context);
164  }
165
166}
167