init commit

This commit is contained in:
Matt Batchelder
2026-02-11 20:55:38 -05:00
commit 6e3929c459
2240 changed files with 467828 additions and 0 deletions

38
tests/Bootstrap.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
define('XIBO', true);
define('PROJECT_ROOT', realpath(__DIR__ . '/..'));
require_once PROJECT_ROOT . '/vendor/autoload.php';
require_once PROJECT_ROOT . '/tests/LocalWebTestCase.php';
require_once PROJECT_ROOT . '/tests/XmdsTestCase.php';
if (!file_exists(PROJECT_ROOT . '/web/settings.php'))
die('Not configured');
\Xibo\Tests\LocalWebTestCase::setEnvironment();
\Xibo\Helper\Translate::InitLocale(null, 'en_GB');

View File

@@ -0,0 +1,148 @@
<?php
/*
* Spring Signage Ltd - http://www.springsignage.com
* Copyright (C) 2017 Spring Signage Ltd
* (DisplayHelperTrait.php)
*/
namespace Xibo\Tests\Helper;
use Carbon\Carbon;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Exception\XiboApiException;
/**
* Trait DisplayHelperTrait
* @package Helper
*/
trait DisplayHelperTrait
{
/**
* @param int $status
* @param string $type
* @return XiboDisplay
* @throws \Exception
*/
protected function createDisplay($status = null, $type = 'windows')
{
// Generate names for display and xmr channel
$hardwareId = Random::generateString(12, 'phpunit');
$xmrChannel = Random::generateString(50);
$this->getLogger()->debug('Creating Display called ' . $hardwareId);
// This is a dummy pubKey and isn't used by anything important
$xmrPubkey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmdnXL4gGg3yJfmqVkU1xsGSQI
3b6YaeAKtWuuknIF1XAHAHtl3vNhQN+SmqcNPOydhK38OOfrdb09gX7OxyDh4+JZ
inxW8YFkqU0zTqWaD+WcOM68wTQ9FCOEqIrbwWxLQzdjSS1euizKy+2GcFXRKoGM
pbBhRgkIdydXoZZdjQIDAQAB
-----END PUBLIC KEY-----';
// Register our display
$this->getXmdsWrapper()->RegisterDisplay($hardwareId,
$hardwareId,
$type,
null,
null,
null,
'00:16:D9:C9:AL:69',
$xmrChannel,
$xmrPubkey
);
// Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1)
$this->fail('Display was not added correctly');
/** @var XiboDisplay $display */
$display = $displays[0];
// Set the initial status
if ($status !== null)
$this->displaySetStatus($display, $status);
return $display;
}
/**
* @param XiboDisplay $display
* @param int $status
*/
protected function displaySetStatus($display, $status)
{
$display->mediaInventoryStatus = $status;
$this->getStore()->update('UPDATE `display` SET MediaInventoryStatus = :status, auditingUntil = :auditingUntil WHERE displayId = :displayId', [
'displayId' => $display->displayId,
'auditingUntil' => Carbon::now()->addSeconds(86400)->format('U'),
'status' => $status
]);
$this->getStore()->commitIfNecessary();
$this->getStore()->close();
}
/**
* @param XiboDisplay $display
*/
protected function displaySetLicensed($display)
{
$display->licensed = 1;
$this->getStore()->update('UPDATE `display` SET licensed = 1, auditingUntil = :auditingUntil WHERE displayId = :displayId', [
'displayId' => $display->displayId,
'auditingUntil' => Carbon::now()->addSeconds(86400)->format('U')
]);
$this->getStore()->commitIfNecessary();
$this->getStore()->close();
}
/**
* @param XiboDisplay $display
* @param string $timeZone
*/
protected function displaySetTimezone($display, $timeZone)
{
$this->getStore()->update('UPDATE `display` SET timeZone = :timeZone WHERE displayId = :displayId', [
'displayId' => $display->displayId,
'timeZone' => $timeZone
]);
$this->getStore()->commitIfNecessary();
$this->getStore()->close();
}
/**
* @param XiboDisplay $display
*/
protected function deleteDisplay($display)
{
$display->delete();
}
/**
* @param XiboDisplay $display
* @param int $status
* @return bool
*/
protected function displayStatusEquals($display, $status)
{
// Requery the Display
try {
$check = (new XiboDisplay($this->getEntityProvider()))->getById($display->displayId);
$this->getLogger()->debug('Tested Display ' . $display->display . '. Status returned is ' . $check->mediaInventoryStatus);
return $check->mediaInventoryStatus === $status;
} catch (XiboApiException $xiboApiException) {
$this->getLogger()->error('API exception for ' . $display->displayId. ': ' . $xiboApiException->getMessage());
return false;
}
}
}

View File

@@ -0,0 +1,287 @@
<?php
/*
* Copyright (C) 2022 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Helper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboResolution;
use Xibo\OAuth2\Client\Entity\XiboWidget;
use Xibo\OAuth2\Client\Exception\XiboApiException;
/**
* Trait LayoutHelperTrait
* @package Helper
*/
trait LayoutHelperTrait
{
/**
* @param int|null $status
* @return XiboLayout
*/
protected function createLayout($status = null)
{
// Create a Layout for us to work with.
$layout = (new XiboLayout($this->getEntityProvider()))
->create(
Random::generateString(),
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->getLogger()->debug('Layout created with name ' . $layout->layout);
if ($status !== null) {
// Set the initial status of this Layout to Built
$this->setLayoutStatus($layout, $status);
}
return $layout;
}
/**
* @param XiboLayout $layout
* @param int $status
* @return $this
*/
protected function setLayoutStatus($layout, $status)
{
$layout->status = $status;
$this->getStore()->update('UPDATE `layout` SET `status` = :status WHERE layoutId = :layoutId', ['layoutId' => $layout->layoutId, 'status' => $status]);
$this->getStore()->commitIfNecessary();
$this->getStore()->close();
return $this;
}
/**
* Build the Layout ready for XMDS
* @param XiboLayout $layout
* @return $this
*/
protected function buildLayout($layout)
{
// Call the status route
$this->getEntityProvider()->get('/layout/status/' . $layout->layoutId);
return $this;
}
/**
* @param XiboLayout $layout
*/
protected function deleteLayout($layout)
{
$layout->delete();
}
/**
* @param XiboLayout $layout
* @param int $status
* @return bool
*/
protected function layoutStatusEquals($layout, $status)
{
// Requery the Display
try {
$check = (new XiboLayout($this->getEntityProvider()))->getById($layout->layoutId);
$this->getLogger()->debug('Tested Layout ' . $layout->layout . '. Status returned is ' . $check->status);
return $check->status === $status;
} catch (XiboApiException $xiboApiException) {
$this->getLogger()->error('API exception for ' . $layout->layoutId . ': ' . $xiboApiException->getMessage());
return false;
}
}
/**
* @param $type
* @return int
*/
protected function getResolutionId($type)
{
if ($type === 'landscape') {
$width = 1920;
$height = 1080;
} else if ($type === 'portrait') {
$width = 1080;
$height = 1920;
} else {
return -10;
}
//$this->getLogger()->debug('Querying for ' . $width . ', ' . $height);
$resolutions = (new XiboResolution($this->getEntityProvider()))->get(['width' => $width, 'height' => $height]);
if (count($resolutions) <= 0)
return -10;
return $resolutions[0]->resolutionId;
}
/**
* @param XiboLayout $layout
* @return XiboLayout
*/
protected function checkout($layout)
{
$this->getLogger()->debug('Checkout ' . $layout->layoutId);
$response = $this->getEntityProvider()->put('/layout/checkout/' . $layout->layoutId);
// Swap the Layout object to use the one returned.
/** @var XiboLayout $layout */
$layout = $this->constructLayoutFromResponse($response);
$this->getLogger()->debug('LayoutId is now: ' . $layout->layoutId);
return $layout;
}
/**
* @param XiboLayout $layout
* @return XiboLayout
*/
protected function publish($layout)
{
$this->getLogger()->debug('Publish ' . $layout->layoutId);
$response = $this->getEntityProvider()->put('/layout/publish/' . $layout->layoutId , [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
// Swap the Layout object to use the one returned.
/** @var XiboLayout $layout */
$layout = $this->constructLayoutFromResponse($response);
$this->getLogger()->debug('LayoutId is now: ' . $layout->layoutId);
return $layout;
}
/**
* @param XiboLayout $layout
* @return XiboLayout
*/
protected function discard($layout)
{
$this->getLogger()->debug('Discard ' . $layout->layoutId);
$response = $this->getEntityProvider()->put('/layout/discard/' . $layout->layoutId);
// Swap the Layout object to use the one returned.
/** @var XiboLayout $layout */
$layout = $this->constructLayoutFromResponse($response);
$this->getLogger()->debug('LayoutId is now: ' . $layout->layoutId);
return $layout;
}
/**
* @param $layout
* @return $this
*/
protected function addSimpleWidget($layout)
{
$this->getEntityProvider()->post('/playlist/widget/clock/' . $layout->regions[0]->regionPlaylist->playlistId, [
'duration' => 100,
'useDuration' => 1
]);
return $this;
}
/**
* @param $layout
* @return $this
*/
protected function addSimpleTextWidget($layout)
{
$this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId, [
'text' => 'PHPUNIT TEST TEXT',
'duration' => 100,
'useDuration' => 1
]);
return $this;
}
/**
* @param $response
* @return \Xibo\OAuth2\Client\Entity\XiboEntity|XiboLayout
*/
private function constructLayoutFromResponse($response)
{
$hydratedRegions = [];
$hydratedWidgets = [];
/** @var XiboLayout $layout */
$layout = new XiboLayout($this->getEntityProvider());
$layout = $layout->hydrate($response);
$this->getLogger()->debug('Constructing Layout from Response: ' . $layout->layoutId);
if (isset($response['regions'])) {
foreach ($response['regions'] as $item) {
/** @var XiboRegion $region */
$region = new XiboRegion($this->getEntityProvider());
$region->hydrate($item);
/** @var XiboPlaylist $playlist */
$playlist = new XiboPlaylist($this->getEntityProvider());
$playlist->hydrate($item['regionPlaylist']);
foreach ($playlist->widgets as $widget) {
/** @var XiboWidget $widgetObject */
$widgetObject = new XiboWidget($this->getEntityProvider());
$widgetObject->hydrate($widget);
$hydratedWidgets[] = $widgetObject;
}
$playlist->widgets = $hydratedWidgets;
$region->regionPlaylist = $playlist;
$hydratedRegions[] = $region;
}
$layout->regions = $hydratedRegions;
} else {
$this->getLogger()->debug('No regions returned with Layout object');
}
return $layout;
}
/**
* @param $layout
* @return XiboLayout
*/
protected function getDraft($layout)
{
$draft = (new XiboLayout($this->getEntityProvider()))->get(['parentId' => $layout->layoutId, 'showDrafts' => 1, 'embed' => 'regions,playlists,widgets']);
return $draft[0];
}
}

View File

@@ -0,0 +1,78 @@
<?php
/*
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Helper;
use Xibo\Service\ConfigServiceInterface;
use Xibo\Service\PlayerActionServiceInterface;
/**
* Class MockPlayerActionService
* @package Helper
*/
class MockPlayerActionService implements PlayerActionServiceInterface
{
/** @var \Xibo\Service\LogServiceInterface */
private $log;
private $displays = [];
/**
* @inheritdoc
*/
public function __construct(ConfigServiceInterface $config, $log, $triggerPlayerActions)
{
$this->log = $log;
}
/**
* @inheritdoc
*/
public function sendAction($displays, $action): void
{
$this->log->debug('MockPlayerActionService: sendAction');
if (!is_array($displays)) {
$displays = [$displays];
}
foreach ($displays as $display) {
$this->displays[] = $display->displayId;
}
}
/**
* @inheritdoc
*/
public function getQueue(): array
{
$this->log->debug('MockPlayerActionService: getQueue');
return $this->displays;
}
/**
* @inheritdoc
*/
public function processQueue(): void
{
$this->log->debug('MockPlayerActionService: processQueue');
}
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Helper;
use Xibo\Service\ConfigServiceInterface;
use Xibo\Service\PlayerActionServiceInterface;
/**
* Class NullPlayerActionService
* @package Helper
*/
class NullPlayerActionService implements PlayerActionServiceInterface
{
/** @var \Xibo\Service\LogServiceInterface */
private $log;
/**
* @inheritdoc
*/
public function __construct(ConfigServiceInterface $config, $log, $triggerPlayerActions)
{
$this->log = $log;
}
/**
* @inheritdoc
*/
public function sendAction($displays, $action): void
{
$this->log->debug('NullPlayerActionService: sendAction');
}
/**
* @inheritdoc
*/
public function getQueue(): array
{
$this->log->debug('NullPlayerActionService: getQueue');
return [];
}
/**
* @inheritdoc
*/
public function processQueue(): void
{
$this->log->debug('NullPlayerActionService: processQueue');
}
}

570
tests/LocalWebTestCase.php Normal file
View File

@@ -0,0 +1,570 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests;
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Nyholm\Psr7\ServerRequest;
use PHPUnit\Framework\TestCase as PHPUnit_TestCase;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Slim\App;
use Slim\Http\ServerRequest as Request;
use Slim\Views\TwigMiddleware;
use Xibo\Entity\Application;
use Xibo\Entity\User;
use Xibo\Factory\ContainerFactory;
use Xibo\Factory\TaskFactory;
use Xibo\Middleware\State;
use Xibo\Middleware\Storage;
use Xibo\OAuth2\Client\Provider\XiboEntityProvider;
use Xibo\Service\DisplayNotifyService;
use Xibo\Service\ReportService;
use Xibo\Storage\PdoStorageService;
use Xibo\Storage\StorageServiceInterface;
use Xibo\Support\Exception\NotFoundException;
use Xibo\Tests\Helper\MockPlayerActionService;
use Xibo\Tests\Middleware\TestAuthMiddleware;
use Xibo\Tests\Xmds\XmdsWrapper;
use Xibo\XTR\TaskInterface;
/**
* Class LocalWebTestCase
* @package Xibo\Tests
*/
class LocalWebTestCase extends PHPUnit_TestCase
{
/** @var ContainerInterface */
public static $container;
/** @var LoggerInterface */
public static $logger;
/** @var TaskInterface */
public static $taskService;
/** @var XiboEntityProvider */
public static $entityProvider;
/** @var XmdsWrapper */
public static $xmds;
/** @var App */
protected $app;
/**
* Get Entity Provider
* @return XiboEntityProvider
*/
public function getEntityProvider()
{
return self::$entityProvider;
}
/**
* Get Xmds Wrapper
* @return XmdsWrapper
*/
public function getXmdsWrapper()
{
return self::$xmds;
}
/**
* Gets the Slim instance configured
* @return App
* @throws \Exception
*/
public function getSlimInstance()
{
// Create the container for dependency injection.
try {
$container = ContainerFactory::create();
} catch (\Exception $e) {
die($e->getMessage());
}
// Create a Slim application
$app = \DI\Bridge\Slim\Bridge::create($container);
$twigMiddleware = TwigMiddleware::createFromContainer($app);
// Create a logger
$handlers = [];
if (isset($_SERVER['PHPUNIT_LOG_TO_FILE']) && $_SERVER['PHPUNIT_LOG_TO_FILE']) {
$handlers[] = new StreamHandler(PROJECT_ROOT . '/library/log.txt', Logger::DEBUG);
}
if (isset($_SERVER['PHPUNIT_LOG_WEB_TO_CONSOLE']) && $_SERVER['PHPUNIT_LOG_WEB_TO_CONSOLE']) {
$handlers[] = new StreamHandler(STDERR, Logger::DEBUG);
}
if (count($handlers) <= 0) {
$handlers[] = new NullHandler();
}
$container->set('logger', function (ContainerInterface $container) use ($handlers) {
$logger = new Logger('PHPUNIT');
$uidProcessor = new UidProcessor();
$logger->pushProcessor($uidProcessor);
foreach ($handlers as $handler) {
$logger->pushHandler($handler);
}
return $logger;
});
// Config
$container->set('name', 'test');
$container->get('configService');
// Set app state
\Xibo\Middleware\State::setState($app, $this->createRequest('GET', '/'));
// Setting Middleware
$app->add(new \Xibo\Middleware\ListenersMiddleware($app));
$app->add(new TestAuthMiddleware($app));
$app->add(new State($app));
$app->add($twigMiddleware);
$app->add(new Storage($app));
$app->add(new Middleware\TestXmr($app));
$app->addRoutingMiddleware();
// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler(\Xibo\Middleware\Handlers::testErrorHandler($container));
// All routes
require PROJECT_ROOT . '/lib/routes-web.php';
require PROJECT_ROOT . '/lib/routes.php';
// Add the route for running a task manually
$app->get('/tasks/{id}', ['\Xibo\Controller\Task','run']);
return $app;
}
/**
* @param string $method
* @param string $path
* @param array $headers
* @param string $requestAttrVal
* @param bool|false $ajaxHeader
* @param null $body
* @return ResponseInterface
*/
protected function sendRequest(string $method, string $path, $body = null, array $headers = ['HTTP_ACCEPT'=>'application/json'], $requestAttrVal = 'test', $ajaxHeader = false): ResponseInterface
{
// Create a request for tests
$request = new Request(new ServerRequest($method, $path, $headers));
$request = $request->withAttribute('_entryPoint', $requestAttrVal);
// If we are using POST or PUT method then we expect to have Body provided, add it to the request
if (in_array($method, ['POST', 'PUT']) && $body != null) {
$request = $request->withParsedBody($body);
// in case we forgot to set Content-Type header for PUT requests
if ($method === 'PUT') {
$request = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded');
}
}
if ($ajaxHeader === true) {
$request = $request->withHeader('X-Requested-With', 'XMLHttpRequest');
}
if ($method == 'GET' && $body != null) {
$request = $request->withQueryParams($body);
}
// send the request and return the response
return $this->app->handle($request);
}
/**
* @param string $method
* @param string $path
* @param null $body
* @param array $headers
* @param array $serverParams
* @return Request
*/
protected function createRequest(string $method, string $path, $body = null, array $headers = ['HTTP_ACCEPT'=>'application/json'], $serverParams = []): Request
{
// Create a request for tests
$request = new Request(new ServerRequest($method, $path, $headers, $body, '', $serverParams));
$request = $request->withAttribute('_entryPoint', 'test');
return $request;
}
/**
* Create a global container for all tests to share.
* @throws \Exception
*/
public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();
// Configure global test state
// We want to ensure there is a
// - global DB object
// - phpunit user who executes the tests through Slim
// - an API application owned by phpunit with client_credentials grant type
if (self::$container == null) {
self::getLogger()->debug('Creating Container');
// Create a new container
$container = ContainerFactory::create();
// Create a logger
$handlers = [];
if (isset($_SERVER['PHPUNIT_LOG_TO_FILE']) && $_SERVER['PHPUNIT_LOG_TO_FILE']) {
$handlers[] = new StreamHandler(PROJECT_ROOT . '/library/log.txt', Logger::INFO);
} else {
$handlers[] = new NullHandler();
}
if (isset($_SERVER['PHPUNIT_LOG_CONTAINER_TO_CONSOLE']) && $_SERVER['PHPUNIT_LOG_CONTAINER_TO_CONSOLE']) {
$handlers[] = new StreamHandler(STDERR, Logger::DEBUG);
}
$container->set('logger', function (ContainerInterface $container) use ($handlers) {
$logger = new Logger('PHPUNIT');
$uidProcessor = new UidProcessor();
$logger->pushProcessor($uidProcessor);
foreach ($handlers as $handler) {
$logger->pushHandler($handler);
}
return $logger;
});
// Initialise config
$container->get('configService');
$container->get('configService')->setDependencies($container->get('store'), '/');
// This is our helper container.
$container->set('name', 'phpunit');
// Configure the container with Player Action and Display Notify
// Player Action Helper
$container->set('playerActionService', function (ContainerInterface $c) {
return new MockPlayerActionService(
$c->get('configService'),
$c->get('logService'),
false
);
});
// Register the display notify service
$container->set('displayNotifyService', function (ContainerInterface $c) {
return new DisplayNotifyService(
$c->get('configService'),
$c->get('logService'),
$c->get('store'),
$c->get('pool'),
$c->get('playerActionService'),
$c->get('scheduleFactory')
);
});
// Register the report service
$container->set('reportService', function (ContainerInterface $c) {
return new ReportService(
$c,
$c->get('store'),
$c->get('timeSeriesStore'),
$c->get('logService'),
$c->get('configService'),
$c->get('sanitizerService'),
$c->get('savedReportFactory')
);
});
// <editor-fold desc="Create PHPUnit container users">
// Find the PHPUnit user and if we don't create it
try {
/** @var User $user */
$user = $container->get('userFactory')->getByName('phpunit');
$user->setChildAclDependencies($container->get('userGroupFactory'));
// Load the user
$user->load(false);
} catch (NotFoundException $e) {
// Create the phpunit user with a random password
/** @var \Xibo\Entity\User $user */
$user = $container->get('userFactory')->create();
$user->setChildAclDependencies($container->get('userGroupFactory'));
$user->userTypeId = 1;
$user->userName = 'phpunit';
$user->libraryQuota = 0;
$user->homePageId = 'statusdashboard.view';
$user->homeFolderId = 1;
$user->isSystemNotification = 1;
$user->setNewPassword(\Xibo\Helper\Random::generateString());
$user->save();
$container->get('store')->commitIfNecessary();
}
// Set on the container
$container->set('user', $user);
// Find the phpunit user and if we don't, complain
try {
/** @var User $admin */
$admin = $container->get('userFactory')->getByName('phpunit');
} catch (NotFoundException $e) {
die ('Cant proceed without the phpunit user');
}
// Check to see if there is an API application we can use
try {
/** @var Application $application */
$application = $container->get('applicationFactory')->getByName('phpunit');
} catch (NotFoundException $e) {
// Add it
$application = $container->get('applicationFactory')->create();
$application->name = ('phpunit');
$application->authCode = 0;
$application->clientCredentials = 1;
$application->userId = $admin->userId;
$application->assignScope($container->get('applicationScopeFactory')->getById('all'));
$application->save();
/** @var PdoStorageService $store */
$store = $container->get('store');
$store->commitIfNecessary();
}
//</editor-fold>
// Register a provider and entity provider to act as our API wrapper
$provider = new \Xibo\OAuth2\Client\Provider\Xibo([
'clientId' => $application->key,
'clientSecret' => $application->secret,
'redirectUri' => null,
'baseUrl' => 'http://localhost'
]);
// Discover the CMS key for XMDS
/** @var PdoStorageService $store */
$store = $container->get('store');
$key = $store->select('SELECT value FROM `setting` WHERE `setting` = \'SERVER_KEY\'', [])[0]['value'];
$store->commitIfNecessary();
// Create an XMDS wrapper for the tests to use
$xmds = new XmdsWrapper('http://localhost/xmds.php', $key);
// Store our entityProvider
self::$entityProvider = new XiboEntityProvider($provider);
// Store our XmdsWrapper
self::$xmds = $xmds;
// Store our container
self::$container = $container;
}
}
/**
* Convenience function to skip a test with a reason and close output buffers nicely.
* @param string $reason
*/
public function skipTest($reason)
{
$this->markTestSkipped($reason);
}
/**
* Get Store
* @return StorageServiceInterface
*/
public function getStore()
{
return self::$container->get('store');
}
/**
* Get a task object
* @param string $task The path of the task class
* @return TaskInterface
* @throws NotFoundException
*/
public function getTask($task)
{
$c = self::$container;
/** @var TaskFactory $taskFactory */
$taskFactory = $c->get('taskFactory');
$task = $taskFactory->getByClass($task);
/** @var TaskInterface $taskClass */
$taskClass = new $task->class();
return $taskClass
->setSanitizer($c->get('sanitizerService'))
->setUser($c->get('user'))
->setConfig($c->get('configService'))
->setLogger($c->get('logService'))
->setPool($c->get('pool'))
->setStore($c->get('store'))
->setTimeSeriesStore($c->get('timeSeriesStore'))
->setDispatcher($c->get('dispatcher'))
->setFactories($c)
->setTask($task);
}
/**
* @return LoggerInterface
* @throws \Exception
*/
public static function getLogger()
{
// Create if necessary
if (self::$logger === null) {
if (isset($_SERVER['PHPUNIT_LOG_TO_CONSOLE']) && $_SERVER['PHPUNIT_LOG_TO_CONSOLE']) {
self::$logger = new Logger('TESTS', [new StreamHandler(STDERR, Logger::DEBUG)]);
} else {
self::$logger = new NullLogger();
}
}
return self::$logger;
}
/**
* Get the queue of actions.
* @return int[]
*/
public function getPlayerActionQueue()
{
/** @var \Xibo\Service\PlayerActionServiceInterface $service */
$service = $this->app->getContainer()->get('playerActionService');
if ($service === null) {
$this->fail('Test has not used the client and therefore cannot determine XMR activity');
}
return $service->getQueue();
}
/**
* @param $name
* @param $class
*/
protected static function installModuleIfNecessary($name, $class)
{
// Make sure the HLS widget is installed
$res = self::$container->get('store')->select('SELECT * FROM `module` WHERE `module` = :module', ['module' => $name]);
if (count($res) <= 0) {
// Install the module
self::$container->get('store')->insert('
INSERT INTO `module` (`Module`, `Name`, `Enabled`, `RegionSpecific`, `Description`,
`SchemaVersion`, `ValidExtensions`, `PreviewEnabled`, `assignable`, `render_as`, `settings`, `viewPath`, `class`, `defaultDuration`, `installName`)
VALUES (:module, :name, :enabled, :region_specific, :description,
:schema_version, :valid_extensions, :preview_enabled, :assignable, :render_as, :settings, :viewPath, :class, :defaultDuration, :installName)
', [
'module' => $name,
'name' => $name,
'enabled' => 1,
'region_specific' => 1,
'description' => $name,
'schema_version' => 1,
'valid_extensions' => null,
'preview_enabled' => 1,
'assignable' => 1,
'render_as' => 'html',
'settings' => json_encode([]),
'viewPath' => '../modules',
'class' => $class,
'defaultDuration' => 10,
'installName' => $name
]);
self::$container->get('store')->commitIfNecessary();
}
}
/**
* @inheritDoc
* @throws \Exception
*/
public function setUp(): void
{
self::getLogger()->debug('LocalWebTestCase: setUp');
parent::setUp();
// Establish a local reference to the Slim app object
$this->app = $this->getSlimInstance();
}
/**
* @inheritDoc
* @throws \Exception
*/
public function tearDown(): void
{
self::getLogger()->debug('LocalWebTestCase: tearDown');
// Close and tidy up the app
$this->app->getContainer()->get('store')->close();
$this->app = null;
parent::tearDown();
}
/**
* Set the _SERVER vars for the suite
* @param array $userSettings
*/
public static function setEnvironment($userSettings = [])
{
$defaults = [
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/',
'SCRIPT_NAME' => '',
'PATH_INFO' => '/',
'QUERY_STRING' => '',
'SERVER_NAME' => 'local.dev',
'SERVER_PORT' => 80,
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'USER_AGENT' => 'Slim Framework',
'REMOTE_ADDR' => '127.0.0.1',
];
$environmentSettings = array_merge($userSettings, $defaults);
foreach ($environmentSettings as $key => $value) {
$_SERVER[$key] = $value;
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface as Middleware;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\App;
use Xibo\Entity\User;
/**
* Class TestAuthMiddleware
* @package Xibo\Tests\Middleware
*
*/
class TestAuthMiddleware implements Middleware
{
/* @var App $app */
private $app;
/**
* Xmr constructor.
* @param $app
*/
public function __construct($app)
{
$this->app = $app;
}
/**
* @param Request $request
* @param RequestHandler $handler
* @return Response
* @throws \Xibo\Support\Exception\GeneralException
*/
public function process(Request $request, RequestHandler $handler): Response
{
$app = $this->app;
$container = $app->getContainer();
/** @var User $user */
$user = $container->get('userFactory')->getByName('phpunit');
$user->setChildAclDependencies($app->getContainer()->get('userGroupFactory'));
// Load the user
$user->load(false);
$container->set('user', $user);
return $handler->handle($request);
}
}

View File

@@ -0,0 +1,96 @@
<?php
/*
* Spring Signage Ltd - http://www.springsignage.com
* Copyright (C) 2017 Spring Signage Ltd
* (TestXmr.php)
*/
namespace Xibo\Tests\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface as Middleware;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\App;
use Xibo\Support\Exception\GeneralException;
use Xibo\Service\DisplayNotifyService;
use Xibo\Service\PlayerActionService;
use Xibo\Tests\Helper\MockPlayerActionService;
/**
* Class TestXmr
* @package Xibo\Tests\Middleware
*/
class TestXmr implements Middleware
{
/* @var App $app */
private $app;
/**
* Xmr constructor.
* @param $app
*/
public function __construct($app)
{
$this->app = $app;
}
/**
* Process
* @param Request $request
* @param RequestHandler $handler
* @return Response
*/
public function process(Request $request, RequestHandler $handler): Response
{
$app = $this->app;
self::setXmr($app);
// Pass along the request
$response = $handler->handle($request);
// Handle display notifications
if ($app->getContainer()->get('displayNotifyService') != null) {
try {
$app->getContainer()->get('displayNotifyService')->processQueue();
} catch (GeneralException $e) {
$app->getContainer()->get('logger')->error('Unable to Process Queue of Display Notifications due to %s', $e->getMessage());
}
}
// Re-terminate any DB connections
$app->getContainer()->get('store')->close();
return $response;
}
/**
* Set XMR
* @param \Slim\App $app
* @param bool $triggerPlayerActions
*/
public static function setXmr($app, $triggerPlayerActions = true)
{
// Player Action Helper
$app->getContainer()->set('playerActionService', function() use ($app, $triggerPlayerActions) {
return new MockPlayerActionService(
$app->getContainer()->get('configService'),
$app->getContainer()->get('logService'),
false
);
});
// Register the display notify service
$app->getContainer()->set('displayNotifyService', function () use ($app) {
return new DisplayNotifyService(
$app->getContainer()->get('configService'),
$app->getContainer()->get('logService'),
$app->getContainer()->get('store'),
$app->getContainer()->get('pool'),
$app->getContainer()->get('playerActionService'),
$app->getContainer()->get('scheduleFactory')
);
});
}
}

193
tests/XMDS.http Normal file

File diff suppressed because one or more lines are too long

105
tests/Xmds/GetDataTest.php Normal file
View File

@@ -0,0 +1,105 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use DOMDocument;
use DOMXPath;
use Xibo\Tests\XmdsTestCase;
/**
* Get data tests for xmds v7
* @property string $dataSetXml
*/
class GetDataTest extends XmdsTestCase
{
// The widgetId of our expected widget (if we change the default layout this ID will change).
const WIDGET_ID = 7;
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
// to make sure Display is logged in, otherwise WidgetSyncTask will not sync data.
$this->sendRequest(
'POST',
$this->register(
'PHPUnit7',
'phpunitv7',
'android'
),
7
);
}
public function testGetData()
{
// Fresh RF
$this->sendRequest('POST', $this->getRf(7), 7);
// Execute Widget Sync task so we can have data for our Widget
exec('cd /var/www/cms; php bin/run.php 9');
// XMDS GetData with our dataSet Widget
$response = $this->sendRequest('POST', $this->getWidgetData(7, self::WIDGET_ID));
$content = $response->getBody()->getContents();
// expect GetDataResponse
$this->assertStringContainsString(
'<ns1:GetDataResponse><data xsi:type="xsd:string">',
$content,
'GetData received incorrect response'
);
$document = new DOMDocument();
$document->loadXML($content);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//data)');
$array = json_decode($result, true);
// go through GetData response and see what we have
foreach ($array as $key => $item) {
// data and meta expected to not be empty
if ($key === 'data' || $key === 'meta') {
$this->assertNotEmpty($item);
$this->assertNotEmpty($key);
}
if ($key === 'data') {
$i = 0;
// go through the expected 2 rows in our dataSet data and see if the column/value matches
foreach ($item as $row) {
$this->assertNotEmpty($row);
if ($i === 0) {
$this->assertSame('Example text value', $row['Text']);
$this->assertSame(1, $row['Number']);
} else if ($i === 1) {
$this->assertSame('PHPUnit text', $row['Text']);
$this->assertSame(2, $row['Number']);
}
$i++;
}
}
}
}
}

View File

@@ -0,0 +1,235 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use DOMDocument;
use DOMXPath;
use PHPUnit\Framework\Attributes\DataProvider;
use Xibo\Tests\XmdsTestCase;
/**
* GetDependency tests, fonts, bundle
*/
class GetDependencyTest extends XmdsTestCase
{
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
}
public static function successCasesBundle(): array
{
return [
[7],
];
}
public static function successCasesFont(): array
{
return [
[7, 'Aileron-Heavy.otf'],
[7, 'fonts.css'],
[6, 'Aileron-Heavy.otf'],
[6, 'fonts.css'],
[5, 'Aileron-Heavy.otf'],
[5, 'fonts.css'],
[4, 'Aileron-Heavy.otf'],
[4, 'fonts.css'],
];
}
public static function successCasesBundleOld(): array
{
return [
[6],
[5],
[4],
];
}
#[DataProvider('successCasesFont')]
public function testGetFont($version, $fileName)
{
$rf = $this->sendRequest('POST', $this->getRf($version), $version);
$response = $rf->getBody()->getContents();
$path = null;
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//RequiredFilesXml)');
$array = json_decode(json_encode(simplexml_load_string($result)), true);
foreach ($array as $item) {
foreach ($item as $file) {
if (!empty($file['@attributes'])
&& !empty($file['@attributes']['saveAs'])
&& $file['@attributes']['saveAs'] === $fileName
) {
if ($version === 7) {
$this->assertSame('dependency', $file['@attributes']['type']);
} else {
$this->assertSame('media', $file['@attributes']['type']);
}
$path = strstr($file['@attributes']['path'], '?');
}
}
}
$this->assertNotEmpty($path);
// Font dependency is still http download, try to get it here
$getFile = $this->getFile($path);
$this->assertSame(200, $getFile->getStatusCode());
$this->assertNotEmpty($getFile->getBody()->getContents());
}
#[DataProvider('successCasesBundle')]
public function testGetBundlev7($version)
{
$rf = $this->sendRequest('POST', $this->getRf($version), $version);
$response = $rf->getBody()->getContents();
$size = null;
$id = null;
$type = null;
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//RequiredFilesXml)');
$array = json_decode(json_encode(simplexml_load_string($result)), true);
foreach ($array as $item) {
foreach ($item as $file) {
if (!empty($file['@attributes'])
&& !empty($file['@attributes']['saveAs'])
&& $file['@attributes']['saveAs'] === 'bundle.min.js'
) {
$size = $file['@attributes']['size'];
$type = $file['@attributes']['fileType'];
$id = $file['@attributes']['id'];
}
}
}
$this->assertNotEmpty($size);
$this->assertNotEmpty($type);
$this->assertNotEmpty($id);
// construct the xml for GetDependency wsdl request
$bundleXml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:GetDependency>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'.$version.'</hardwareKey>
<fileType xsi:type="xsd:string">'. $type .'</fileType>
<id xsi:type="xsd:string">'. $id .'</id>
<chunkOffset xsi:type="xsd:double">0</chunkOffset>
<chunkSize xsi:type="xsd:double">'. $size .'</chunkSize>
</tns:GetDependency>
</soap:Body>
</soap:Envelope>';
// try to call GetDependency with our xml
$getBundle = $this->sendRequest('POST', $bundleXml, $version);
$getBundleResponse = $getBundle->getBody()->getContents();
// expect success
$this->assertSame(200, $getBundle->getStatusCode());
// expect not empty body
$this->assertNotEmpty($getBundleResponse);
// expect response format
$this->assertStringContainsString(
'<ns1:GetDependencyResponse><file xsi:type="xsd:base64Binary">',
$getBundleResponse,
'GetDependency getBundle received incorrect response'
);
}
#[DataProvider('successCasesBundleOld')]
public function testGetBundleOld($version)
{
$rf = $this->sendRequest('POST', $this->getRf($version), $version);
$response = $rf->getBody()->getContents();
$size = null;
$id = null;
$type = null;
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//RequiredFilesXml)');
$array = json_decode(json_encode(simplexml_load_string($result)), true);
foreach ($array as $item) {
foreach ($item as $file) {
if (!empty($file['@attributes'])
&& !empty($file['@attributes']['saveAs'])
&& $file['@attributes']['saveAs'] === 'bundle.min.js'
) {
$size = $file['@attributes']['size'];
$type = $file['@attributes']['type'];
$id = $file['@attributes']['id'];
}
}
}
$this->assertNotEmpty($size);
$this->assertNotEmpty($type);
$this->assertNotEmpty($id);
// construct the xml for GetDependency wsdl request
$bundleXml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:GetFile>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'.$version.'</hardwareKey>
<fileId xsi:type="xsd:string">'. $id .'</fileId>
<fileType xsi:type="xsd:string">'. $type .'</fileType>
<chunkOffset xsi:type="xsd:double">0</chunkOffset>
<chuckSize xsi:type="xsd:double">'. $size .'</chuckSize>
</tns:GetFile>
</soap:Body>
</soap:Envelope>';
// try to call GetFile with our xml
$getBundle = $this->sendRequest('POST', $bundleXml, $version);
$getBundleResponse = $getBundle->getBody()->getContents();
// expect success
$this->assertSame(200, $getBundle->getStatusCode());
// expect not empty body
$this->assertNotEmpty($getBundleResponse);
// expect response format
$this->assertStringContainsString(
'<ns1:GetFileResponse><file xsi:type="xsd:base64Binary">',
$getBundleResponse,
'GetDependency getBundle received incorrect response'
);
}
}

View File

@@ -0,0 +1,215 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use PHPUnit\Framework\Attributes\DataProvider;
use Xibo\Tests\XmdsTestCase;
/**
* Various Notify Status tests
*/
final class NotifyStatusTest extends XmdsTestCase
{
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
}
public static function successCases(): array
{
return [
[7],
[6],
[5],
[4],
];
}
public static function failureCases(): array
{
return [
[3],
];
}
#[DataProvider('successCases')]
public function testCurrentLayout(int $version)
{
$request = $this->sendRequest('POST', $this->notifyStatus($version, '{"currentLayoutId":1}'), $version);
$this->assertStringContainsString(
'<ns1:NotifyStatusResponse><success xsi:type="xsd:boolean">true</success></ns1:NotifyStatusResponse>',
$request->getBody()->getContents(),
'Notify Current Layout received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testCurrentLayoutFailure(int $version)
{
// disable exception on http_error in guzzle, so we can still check the response
$request = $this->sendRequest(
'POST',
$this->notifyStatus($version, '{"currentLayoutId":1}'),
$version,
false
);
$this->assertSame(500, $request->getStatusCode());
// check the fault code
$this->assertStringContainsString(
'<faultcode>SOAP-ENV:Server</faultcode>',
$request->getBody(),
'Notify Current Layout received incorrect response'
);
// check the fault string
$this->assertStringContainsString(
'<faultstring>Procedure \'NotifyStatus\' not present</faultstring>',
$request->getBody(),
'Notify Current Layout received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testCurrentLayoutExceptionFailure(int $version)
{
// we are expecting 500 Server Exception here for xmds 3
$this->expectException('GuzzleHttp\Exception\ServerException');
$this->expectExceptionCode(500);
$request = $this->sendRequest('POST', $this->notifyStatus($version, '{"currentLayoutId":1}'), $version);
}
#[DataProvider('successCases')]
public function testGeoLocation($version)
{
$request = $this->sendRequest(
'POST',
$this->notifyStatus($version, '{"latitude":52.3676, "longitude":4.9041}'),
$version
);
$this->assertStringContainsString(
'<ns1:NotifyStatusResponse><success xsi:type="xsd:boolean">true</success></ns1:NotifyStatusResponse>',
$request->getBody()->getContents(),
'Notify Geo Location received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testGeoLocationFailure(int $version)
{
// disable exception on http_error in guzzle, so we can still check the response
$request = $this->sendRequest(
'POST',
$this->notifyStatus($version, '{"latitude":52.3676, "longitude":4.9041}'),
$version,
false
);
$this->assertSame(500, $request->getStatusCode());
// check the fault code
$this->assertStringContainsString(
'<faultcode>SOAP-ENV:Server</faultcode>',
$request->getBody(),
'Notify Geo Location received incorrect response'
);
// check the fault string
$this->assertStringContainsString(
'<faultstring>Procedure \'NotifyStatus\' not present</faultstring>',
$request->getBody(),
'Notify Geo Location received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testGeoLocationExceptionFailure(int $version)
{
// we are expecting 500 Server Exception here for xmds 3
$this->expectException('GuzzleHttp\Exception\ServerException');
$this->expectExceptionCode(500);
$this->sendRequest(
'POST',
$this->notifyStatus($version, '{"latitude":52.3676, "longitude":4.9041}'),
$version,
);
}
#[DataProvider('successCases')]
public function testOrientation(int $version)
{
$request = $this->sendRequest(
'POST',
$this->notifyStatus($version, '{"width":7680, "height":4320}'),
$version,
);
$this->assertStringContainsString(
'<ns1:NotifyStatusResponse><success xsi:type="xsd:boolean">true</success></ns1:NotifyStatusResponse>',
$request->getBody()->getContents(),
'Notify Orientation received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testOrientationFailure(int $version)
{
// disable exception on http_error in guzzle, so we can still check the response
$request = $this->sendRequest(
'POST',
$this->notifyStatus($version, '{"width":7680, "height":4320}'),
$version,
false
);
$this->assertSame(500, $request->getStatusCode());
// check the fault code
$this->assertStringContainsString(
'<faultcode>SOAP-ENV:Server</faultcode>',
$request->getBody(),
'Notify Orientation received incorrect response'
);
// check the fault string
$this->assertStringContainsString(
'<faultstring>Procedure \'NotifyStatus\' not present</faultstring>',
$request->getBody(),
'Notify Orientation received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testOrientationExceptionFailure(int $version)
{
// we are expecting 500 Server Exception here for xmds 3
$this->expectException('GuzzleHttp\Exception\ServerException');
$this->expectExceptionCode(500);
$this->sendRequest(
'POST',
$this->notifyStatus($version, '{"width":7680, "height":4320}'),
$version,
);
}
}

View File

@@ -0,0 +1,134 @@
<?php
/*
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use DOMDocument;
use DOMXPath;
use Xibo\Tests\XmdsTestCase;
/**
* Register Displays tests
*/
class RegisterDisplayTest extends XmdsTestCase
{
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
}
public function testRegisterDisplayAuthed()
{
$request = $this->sendRequest(
'POST',
$this->register(
'PHPUnit7',
'phpunitv7',
'android'
),
7
);
$response = $request->getBody()->getContents();
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//ActivationMessage)');
$innerDocument = new DOMDocument();
$innerDocument->loadXML($result);
$this->assertSame('READY', $innerDocument->documentElement->getAttribute('code'));
$this->assertSame(
'Display is active and ready to start.',
$innerDocument->documentElement->getAttribute('message')
);
}
public function testRegisterDisplayNoAuth()
{
$request = $this->sendRequest(
'POST',
$this->register(
'PHPUnitWaiting',
'phpunitwaiting',
'android'
),
7
);
$response = $request->getBody()->getContents();
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//ActivationMessage)');
$innerDocument = new DOMDocument();
$innerDocument->loadXML($result);
$this->assertSame('WAITING', $innerDocument->documentElement->getAttribute('code'));
$this->assertSame(
'Display is Registered and awaiting Authorisation from an Administrator in the CMS',
$innerDocument->documentElement->getAttribute('message')
);
$array = json_decode(json_encode(simplexml_load_string($result)), true);
foreach ($array as $key => $value) {
if ($key === 'commercialLicence') {
$this->assertSame('trial', $value);
}
}
}
public function testRegisterNewDisplay()
{
$request = $this->sendRequest(
'POST',
$this->register(
'PHPUnitAddedTest' . mt_rand(1, 10),
'phpunitaddedtest',
'android'
),
7
);
$response = $request->getBody()->getContents();
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//ActivationMessage)');
$innerDocument = new DOMDocument();
$innerDocument->loadXML($result);
$this->assertSame('ADDED', $innerDocument->documentElement->getAttribute('code'));
$this->assertSame(
'Display is now Registered and awaiting Authorisation from an Administrator in the CMS',
$innerDocument->documentElement->getAttribute('message')
);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use PHPUnit\Framework\Attributes\DataProvider;
use Xibo\Tests\XmdsTestCase;
/**
* Report fault tests
*/
final class ReportFaultsTest extends XmdsTestCase
{
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
}
public static function successCases(): array
{
return [
[7],
[6],
];
}
public static function failureCases(): array
{
return [
[5],
[4],
[3],
];
}
#[DataProvider('successCases')]
public function testSendFaultSuccess(int $version)
{
$request = $this->sendRequest('POST', $this->reportFault($version), $version);
$this->assertStringContainsString(
'<ns1:ReportFaultsResponse><success xsi:type="xsd:boolean">true</success>',
$request->getBody()->getContents(),
'Send fault received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testSendFaultFailure(int $version)
{
// disable exception on http_error in guzzle, so we can still check the response
$request = $this->sendRequest('POST', $this->reportFault($version), $version, false);
// check the fault code
$this->assertStringContainsString(
'<faultcode>SOAP-ENV:Server</faultcode>',
$request->getBody(),
'Send fault received incorrect response'
);
// check the fault string
$this->assertStringContainsString(
'<faultstring>Procedure \'ReportFaults\' not present</faultstring>',
$request->getBody(),
'Send fault received incorrect response'
);
}
#[DataProvider('failureCases')]
public function testSendFaultExceptionFailure(int $version)
{
// we are expecting 500 Server Exception here for xmds 3,4 and 5
$this->expectException('GuzzleHttp\Exception\ServerException');
$this->expectExceptionCode(500);
$this->sendRequest('POST', $this->reportFault($version), $version);
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use GuzzleHttp\Exception\GuzzleException;
use Xibo\Tests\xmdsTestCase;
class SubmitLogTest extends XmdsTestCase
{
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
}
/**
* Submit log with category event
* @return void
* @throws GuzzleException
*/
public function testSubmitEventLog()
{
$request = $this->sendRequest(
'POST',
$this->submitEventLog('7'),
7
);
$this->assertStringContainsString(
'<ns1:SubmitLogResponse><success xsi:type="xsd:boolean">true</success>',
$request->getBody()->getContents(),
'Submit Log received incorrect response'
);
}
}

124
tests/Xmds/SyncTest.php Normal file
View File

@@ -0,0 +1,124 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
use DOMDocument;
use DOMXPath;
use PHPUnit\Framework\Attributes\DataProvider;
use Xibo\Tests\xmdsTestCase;
/**
* Sync Schedule and Register tests
*/
class SyncTest extends XmdsTestCase
{
use XmdsHelperTrait;
public function setUp(): void
{
parent::setUp();
}
public static function registerSuccessCases(): array
{
return [
[7],
[6],
];
}
public function testScheduleSyncEvent()
{
$request = $this->sendRequest('POST', $this->getSchedule('PHPUnit7'), 7);
$response = $request->getBody()->getContents();
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//ScheduleXml)');
$innerDocument = new DOMDocument();
$innerDocument->loadXML($result);
$layouts = $innerDocument->documentElement->getElementsByTagName('layout');
$i = 0;
foreach ($layouts as $layout) {
if ($i === 0) {
$this->assertSame('8', $layout->getAttribute('file'));
$this->assertSame('1', $layout->getAttribute('syncEvent'));
$this->assertSame('2', $layout->getAttribute('scheduleid'));
} else if ($i === 1) {
$this->assertSame('6', $layout->getAttribute('file'));
$this->assertSame('0', $layout->getAttribute('syncEvent'));
$this->assertSame('1', $layout->getAttribute('scheduleid'));
}
$i++;
}
}
#[DataProvider('registerSuccessCases')]
public function testRegisterDisplay($version)
{
if ($version === 7) {
$this->sendRequest('POST', $this->notifyStatus($version, '{"lanIpAddress":"192.168.0.3"}'), $version);
$xml = $this->register(
'PHPUnit7',
'phpunitv7',
'android'
);
} else {
$xml = $this->register(
'PHPUnit6',
'phpunitv6',
'android'
);
}
$request = $this->sendRequest('POST', $xml, $version);
$response = $request->getBody()->getContents();
$document = new DOMDocument();
$document->loadXML($response);
$xpath = new DOMXpath($document);
$result = $xpath->evaluate('string(//ActivationMessage)');
$innerDocument = new DOMDocument();
$innerDocument->loadXML($result);
$this->assertSame('READY', $innerDocument->documentElement->getAttribute('code'));
$this->assertSame(
'Display is active and ready to start.',
$innerDocument->documentElement->getAttribute('message')
);
$syncNodes = $innerDocument->getElementsByTagName('syncGroup');
$this->assertSame(1, count($syncNodes));
if ($version === 7) {
$this->assertSame('lead', $syncNodes->item(0)->textContent);
} else {
$this->assertSame('192.168.0.3', $syncNodes->item(0)->textContent);
}
}
}

View File

@@ -0,0 +1,136 @@
<?php
/*
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
trait XmdsHelperTrait
{
public function getRf(string $version)
{
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:RequiredFiles>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'.$version.'</hardwareKey>
</tns:RequiredFiles>
</soap:Body>
</soap:Envelope>';
}
public function notifyStatus(string $version, string $status)
{
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:NotifyStatus>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'.$version.'</hardwareKey>
<status xsi:type-="xsd:string">'.$status.'</status>
</tns:NotifyStatus>
</soap:Body>
</soap:Envelope>';
}
public function register(
$hardwareKey,
$displayName,
$clientType,
$clientVersion = '4',
$clientCode = '400',
$macAddress = 'CC:40:D0:46:3C:A8'
) {
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:RegisterDisplay>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">' . $hardwareKey . '</hardwareKey>
<displayName xsi:type="xsd:string">' . $displayName . '</displayName>
<clientType xsi:type="xsd:string">' . $clientType . '</clientType>
<clientVersion xsi:type="xsd:string">' . $clientVersion . '</clientVersion>
<clientCode xsi:type="xsd:int">' . $clientCode . '</clientCode>
<macAddress xsi:type="xsd:string">' . $macAddress . '</macAddress>
</tns:RegisterDisplay>
</soap:Body>
</soap:Envelope>';
}
public function getSchedule($hardwareKey)
{
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:Schedule>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">' . $hardwareKey . '</hardwareKey>
</tns:Schedule>
</soap:Body>
</soap:Envelope>';
}
public function reportFault($version)
{
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:ReportFaults>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'.$version.'</hardwareKey>
<fault xsi:type="xsd:string">[{"date":"2023-04-20 17:03:52","expires":"2023-04-21 17:03:52","code":"10001","reason":"Test","scheduleId":"0","layoutId":0,"regionId":"0","mediaId":"0","widgetId":"0"}]</fault>
</tns:ReportFaults>
</soap:Body>
</soap:Envelope>';
}
public function getWidgetData($version, $widgetId)
{
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:GetData>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'. $version .'</hardwareKey>
<widgetId xsi:type="xsd:int">'.$widgetId.'</widgetId>
</tns:GetData>
</soap:Body>
</soap:Envelope>';
}
public function submitEventLog($version): string
{
return '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:xmds" xmlns:types="urn:xmds/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:SubmitLog>
<serverKey xsi:type="xsd:string">6v4RduQhaw5Q</serverKey>
<hardwareKey xsi:type="xsd:string">PHPUnit'. $version .'</hardwareKey>
<logXml xsi:type="xsd:string">&lt;log&gt;&lt;event date=&quot;2024-04-10 12:45:55&quot; category=&quot;event&quot;&gt;&lt;eventType&gt;App Start&lt;/eventType&gt;&lt;message&gt;Detailed message about this event&lt;/message&gt;&lt;alertType&gt;both&lt;/alertType&gt;&lt;refId&gt;&lt;/refId&gt;&lt;/event&gt;&lt;/log&gt;</logXml>
</tns:SubmitLog>
</soap:Body>
</soap:Envelope>';
}
}

189
tests/Xmds/XmdsWrapper.php Normal file
View File

@@ -0,0 +1,189 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Xmds;
/**
* Class XmdsWrapper
* @package Xibo\Tests\Xmds
*/
class XmdsWrapper
{
private $URL;
private $KEY;
private $version;
protected $client;
/**
* XmdsWrapper constructor.
* @param string $URL
* @param string $KEY
* @param string $version
* @throws \SoapFault
*/
public function __construct($URL = 'http://localhost/xmds.php', $KEY = 'test', $version = '7')
{
$this->URL = $URL;
$this->KEY = $KEY;
$this->version = $version;
ini_set('soap.wsdl_cache_enabled', 0);
ini_set('soap.wsdl_cache_ttl', 900);
ini_set('default_socket_timeout', 15);
$options = [
'uri'=>'http://schemas.xmlsoap.org/soap/envelope/',
'style'=>SOAP_RPC,
'use'=>SOAP_ENCODED,
'soap_version'=>SOAP_1_1,
'cache_wsdl'=>WSDL_CACHE_NONE,
'connection_timeout'=>15,
'trace'=>true,
'encoding'=>'UTF-8',
'exceptions'=>true,
];
$this->client = new \SoapClient($this->URL . '?wsdl&v=' . $this->version, $options);
}
/**
* @param $hardwareKey
* @param $displayName
* @param string $clientType
* @param string $clientVersion
* @param string $clientCode
* @param string $operatingSystem
* @param string $macAddress
* @param string $xmrChannel
* @param string $xmrPubKey
* @return mixed
* @throws \SoapFault
*/
function RegisterDisplay($hardwareKey, $displayName, $clientType='windows', $clientVersion='', $clientCode='', $operatingSystem='', $macAddress='', $xmrChannel='', $xmrPubKey='')
{
return $this->client->RegisterDisplay($this->KEY,
$hardwareKey,
$displayName,
$clientType,
$clientVersion,
$clientCode,
$operatingSystem,
$macAddress,
$xmrChannel,
$xmrPubKey
);
}
/**
* Request Required Files
* @param $hardwareKey
* @return mixed
* @throws \SoapFault
*/
function RequiredFiles($hardwareKey)
{
return $this->client->RequiredFiles($this->KEY, $hardwareKey);
}
/**
* Request a file
* @param $hardwareKey
* @param $fileId
* @param $fileType
* @param $chunkOffset
* @param $chunkSize
* @return mixed
* @throws \SoapFault
*/
function GetFile($hardwareKey, $fileId, $fileType, $chunkOffset, $chunkSize)
{
return $this->client->GetFile($this->KEY,
$hardwareKey,
$fileId,
$fileType,
$chunkOffset,
$chunkSize
);
}
/**
* Request Schedule
* @param $hardwareKey
* @return mixed
* @throws \SoapFault
*/
function Schedule($hardwareKey)
{
return $this->client->Schedule($this->KEY, $hardwareKey);
}
function BlackList()
{
}
function SubmitLog()
{
}
/**
* Submit Stats
* @param $hardwareKey
* @param $statXml
* @return mixed
* @throws \SoapFault
*/
function SubmitStats($hardwareKey, $statXml)
{
return $this->client->SubmitStats($this->KEY, $hardwareKey, $statXml);
}
function MediaInventory()
{
}
/**
* @param string $hardwareKey
* @param int $layoutId
* @param int $regionId
* @param string $mediaId
* @return string
* @throws \SoapFault
*/
function GetResource($hardwareKey, $layoutId, $regionId, $mediaId)
{
return $this->client->GetResource($this->KEY, $hardwareKey, $layoutId, $regionId, $mediaId);
}
function NotifyStatus()
{
}
function SubmitScreenShot()
{
}
}

180
tests/XmdsTestCase.php Normal file
View File

@@ -0,0 +1,180 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
class xmdsTestCase extends TestCase
{
/** @var ContainerInterface */
public static $container;
/** @var LoggerInterface */
public static $logger;
/** @var Client */
public $client;
/**
* @inheritDoc
*/
public function getGuzzleClient(array $requestOptions = []): Client
{
if ($this->client === null) {
$this->client = new Client($requestOptions);
}
return $this->client;
}
/**
* @param string $method
* @param string $body
* @param string $path
* @param array $headers
* @return ResponseInterface
* @throws GuzzleException
*/
protected function sendRequest(
string $method = 'POST',
string $body = '',
string $version = '7',
bool $httpErrors = true,
string $path = 'http://localhost/xmds.php?v=',
array $headers = ['HTTP_ACCEPT'=>'text/xml']
): ResponseInterface {
// Create a request for tests
return $this->client->request($method, $path . $version, [
'headers' => $headers,
'body' => $body,
'http_errors' => $httpErrors
]);
}
protected function getFile(
string $fileQuery = '',
string $method = 'GET',
string $basePath = 'http://localhost/xmds.php',
): ResponseInterface {
// Create a request for tests
return $this->client->request($method, $basePath . $fileQuery);
}
/**
* Create a global container for all tests to share.
* @throws \Exception
*/
public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();
}
/**
* Convenience function to skip a test with a reason and close output buffers nicely.
* @param string $reason
*/
public function skipTest(string $reason): void
{
$this->markTestSkipped($reason);
}
/**
* @return Logger|NullLogger|LoggerInterface
*/
public static function getLogger(): Logger|NullLogger|LoggerInterface
{
// Create if necessary
if (self::$logger === null) {
if (isset($_SERVER['PHPUNIT_LOG_TO_CONSOLE']) && $_SERVER['PHPUNIT_LOG_TO_CONSOLE']) {
self::$logger = new Logger('TESTS', [new StreamHandler(STDERR, Logger::DEBUG)]);
} else {
self::$logger = new NullLogger();
}
}
return self::$logger;
}
/**
* @inheritDoc
* @throws \Exception
*/
public function setUp(): void
{
self::getLogger()->debug('xmdsTestCase: setUp');
parent::setUp();
// Establish a local reference to the Slim app object
$this->client = $this->getGuzzleClient();
}
/**
* @inheritDoc
* @throws \Exception
*/
public function tearDown(): void
{
self::getLogger()->debug('xmdsTestCase: tearDown');
// Close and tidy up the app
$this->client = null;
parent::tearDown();
}
/**
* Set the _SERVER vars for the suite
* @param array $userSettings
*/
public static function setEnvironment(array $userSettings = []): void
{
$defaults = [
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/',
'SCRIPT_NAME' => '',
'PATH_INFO' => '/',
'QUERY_STRING' => '',
'SERVER_NAME' => 'local.dev',
'SERVER_PORT' => 80,
'HTTP_ACCEPT' => 'text/xml,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'USER_AGENT' => 'Slim Framework',
'REMOTE_ADDR' => '127.0.0.1',
];
$environmentSettings = array_merge($userSettings, $defaults);
foreach ($environmentSettings as $key => $value) {
$_SERVER[$key] = $value;
}
}
}

74
tests/Xmr/playerSub.php Normal file
View File

@@ -0,0 +1,74 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
// This is a simple XMR client which connects to the display added in XMDS.http
// performing actions in the CMS which affect the display should be logged here.
define('PROJECT_ROOT', realpath(__DIR__ . '/../..'));
require PROJECT_ROOT . '/vendor/autoload.php';
// RSA key
$fp = fopen(PROJECT_ROOT . '/library/certs/private.key', 'r');
$privateKey = openssl_get_privatekey(fread($fp, 8192));
fclose($fp);
// Sub
$loop = React\EventLoop\Factory::create();
$context = new React\ZMQ\Context($loop);
$sub = $context->getSocket(ZMQ::SOCKET_SUB);
$sub->connect('tcp://xmr:9505');
$sub->subscribe('H');
$sub->subscribe('XMR_test_channel');
$sub->on('messages', function ($msg) use ($privateKey) {
try {
if ($msg[0] == 'H') {
echo '[' . date('Y-m-d H:i:s') . '] Heartbeat...' . PHP_EOL;
return;
}
// Expect messages to have a length of 3
if (count($msg) != 3) {
throw new InvalidArgumentException('Incorrect Message Length');
}
// Message will be: channel, key, message
if ($msg[0] != 'XMR_test_channel') {
throw new InvalidArgumentException('Channel does not match');
}
// Decrypt
$output = null;
openssl_open(base64_decode($msg[2]), $output, base64_decode($msg[1]), $privateKey, 'RC4');
echo '[' . date('Y-m-d H:i:s') . '] Received: ' . $output . PHP_EOL;
} catch (InvalidArgumentException $e) {
echo '[' . date('Y-m-d H:i:s') . '] E: ' . $e->getMessage() . PHP_EOL;
}
});
$loop->run();
openssl_free_key($privateKey);

View File

@@ -0,0 +1,11 @@
{
"dev": {
"url": "http://localhost",
"serverKey": "test",
"hardwareKey": "phpstorm",
"displayName": "PHPStorm",
"clientType": "windows",
"macAddress": "00:00:00:00:00:00",
"testWidgetId": "1660"
}
}

View File

@@ -0,0 +1,95 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use League\OAuth2\Client\Token\AccessToken;
/**
* Class AboutTest
* @package Xibo\Tests\Integration
*/
class AboutTest extends \Xibo\Tests\LocalWebTestCase
{
/**
* Shows CMS version
* @throws \Exception
*/
public function testVersion()
{
$response = $this->sendRequest('GET', '/about');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(200, $body->status);
$this->assertSame(true, $body->success);
$this->assertSame(false, $body->grid);
$this->assertNotEmpty($body->data, 'Empty Data');
$this->assertNotEmpty($body->data->version, 'Empty Version');
}
/**
* Test that the API is initialised and making authenticated requests.
*/
public function testApiInitialisedTest()
{
$this->assertNotNull($this->getEntityProvider(), 'Entity Provider not set');
$this->assertNotNull($this->getEntityProvider()->getProvider(), 'Provider not set');
}
/**
* @depends testApiInitialisedTest
*/
public function testApiAccessTest()
{
$provider = $this->getEntityProvider()->getProvider();
$token = $provider->getAccessToken('client_credentials');
$this->assertNotNull($token);
$this->assertNotTrue($token->hasExpired(), 'Expired Token');
$this->assertInstanceOf('League\OAuth2\Client\Token\AccessToken', $token);
return $token;
}
/**
* @param AccessToken $token
* @depends testApiAccessTest
*/
public function testApiUserTest(AccessToken $token)
{
$provider = $this->getEntityProvider()->getProvider();
try {
$me = $provider->getResourceOwner($token);
} catch (\Exception $exception) {
$this->fail('API connect not successful: ' . $exception->getMessage());
}
$this->assertNotNull($me);
$this->assertArrayHasKey('userId', $me->toArray());
$this->assertNotEmpty($me->getId());
$this->assertNotEquals(0, $me->getId());
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Helper\DateFormatHelper;
class AuditLogTest extends \Xibo\Tests\LocalWebTestCase
{
public function testSearch()
{
$response = $this->sendRequest('GET','/audit');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertObjectHasAttribute('data', $object->data, $response->getBody());
$this->assertObjectHasAttribute('draw', $object->data, $response->getBody());
$this->assertObjectHasAttribute('recordsTotal', $object->data, $response->getBody());
$this->assertObjectHasAttribute('recordsFiltered', $object->data, $response->getBody());
// Make sure the recordsTotal is not greater than 10 (the default paging)
$this->assertLessThanOrEqual(10, count($object->data->data));
}
public function testExportForm()
{
$response = $this->sendRequest('GET','/audit/form/export');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
}
/**
*/
public function testExport()
{
$response = $this->sendRequest('GET','/audit/export', [
'filterFromDt' => Carbon::now()->subSeconds(86400)->format(DateFormatHelper::getSystemFormat()),
'filterToDt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
]);
$this->assertSame(200, $response->getStatusCode());
}
}

View File

@@ -0,0 +1,147 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class CampaignDeleteTest
* @package Xibo\Tests\integration\Cache
*/
class CampaignDeleteTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboCampaign */
protected $campaign;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboSchedule */
protected $event;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Campaign
$this->campaign = (new XiboCampaign($this->getEntityProvider()))->create(Random::generateString());
// Assign the Layout to the Campaign
$this->getEntityProvider()->post('/campaign/layout/assign/' . $this->campaign->campaignId, [
'layoutId' => $this->layout->layoutId
]);
// Create a Display
$this->display = $this->createDisplay();
// Date
$date = Carbon::now();
// Schedule the Campaign "always" onto our display
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
$date->format(DateFormatHelper::getSystemFormat()),
$date->addHours(3)->format(DateFormatHelper::getSystemFormat()),
$this->campaign->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
parent::tearDown();
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Delete the Campaign
$this->sendRequest('DELETE', '/campaign/' . $this->campaign->campaignId);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* Copyright (C) 2021 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class CampaignLayoutAssignTest
* @package Xibo\Tests\integration\Cache
*/
class CampaignLayoutAssignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboCampaign */
protected $campaign;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboSchedule */
protected $event;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Campaign Layout Unassign Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Campaign
$this->campaign = (new XiboCampaign($this->getEntityProvider()))->create(Random::generateString());
// Create a Display
$this->display = $this->createDisplay();
// Date
$date = Carbon::now();
// Schedule the Campaign "always" onto our display
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
$date->format(DateFormatHelper::getSystemFormat()),
$date->addHours(3)->format(DateFormatHelper::getSystemFormat()),
$this->campaign->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the Campaign
$this->campaign->delete();
parent::tearDown();
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt Done as expected');
// Add the Layout we have prepared to the existing Campaign
$this->sendRequest('POST', '/campaign/layout/assign/' . $this->campaign->campaignId, [
'layoutId' => $this->layout->layoutId
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt Pending as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,155 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class CampaignLayoutUnassignTest
* @package Xibo\Tests\integration\Cache
*/
class CampaignLayoutUnassignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboCampaign */
protected $campaign;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboSchedule */
protected $event;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Campaign
$this->campaign = (new XiboCampaign($this->getEntityProvider()))->create(Random::generateString());
// Assign the Layout to the Campaign
$this->getEntityProvider()->post('/campaign/layout/assign/' . $this->campaign->campaignId, [
'layoutId' => $this->layout->layoutId
]);
// Create a Display
$this->display = $this->createDisplay();
// Date
$date = Carbon::now();
// Schedule the Campaign "always" onto our display
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
$date->format(DateFormatHelper::getSystemFormat()),
$date->addHours(3)->format(DateFormatHelper::getSystemFormat()),
$this->campaign->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the Campaign
$this->campaign->delete();
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Unassign requires edit
$this->sendRequest('PUT', '/campaign/' . $this->campaign->campaignId, [
'name' => $this->campaign->campaign,
'manageLayouts' => 1,
'layoutIds' => [] // empty list
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,159 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\DataSetColumn;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDataSet;
use Xibo\OAuth2\Client\Entity\XiboDataSetColumn;
use Xibo\OAuth2\Client\Entity\XiboDataSetView;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboWidget;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DataSetDataEditTest
* @package Xibo\Tests\integration\Cache
*/
class DataSetDataEditTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboDataSet */
protected $dataSet;
/** @var DataSetColumn */
protected $dataSetColumn;
/** @var XiboLayout */
protected $layout;
/** @var XiboWidget */
protected $widget;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Add a DataSet
$this->dataSet = (new XiboDataSet($this->getEntityProvider()))->create(Random::generateString(), 'Test');
// Add a Column
$this->dataSetColumn = (new XiboDataSetColumn($this->getEntityProvider()))->create($this->dataSet->dataSetId,
Random::generateString(),
'',
1,
1,
1,
'');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a couple of text widgets to the region
$response = $this->getEntityProvider()->post('/playlist/widget/datasetview/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'step' => 1,
'dataSetId' => $this->dataSet->dataSetId
]);
$this->widget = (new XiboDataSetView($this->getEntityProvider()))->hydrate($response);
// Check in
$this->layout = $this->publish($this->layout);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the DataSet
$this->dataSet->deleteWData();
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Add Data to the DataSet
$this->sendRequest('POST','/dataset/data/'. $this->dataSet->dataSetId, [
'dataSetColumnId_' . $this->dataSetColumn->dataSetColumnId => '1'
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,145 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupDisplayAssignTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupDisplayAssignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplayGroup */
protected $displayGroup;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display Group
$this->displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create(Random::generateString(), 'Cache Test', 0, null);
// Schedule the Layout "always" onto our display group
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->displayGroup->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the Display Group
$this->displayGroup->delete();
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Add the Layout we have prepared to the Display Group
$response = $this->sendRequest('POST','/displaygroup/' . $this->displayGroup->displayGroupId . '/display/assign', [
'displayId' => [$this->display->displayId]
]);
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,147 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupDisplayUnassignTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupDisplayUnassignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplayGroup */
protected $displayGroup;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display Group
$this->displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create(Random::generateString(), 'Cache Test', 0, null);
// Schedule the Layout "always" onto our display group
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->displayGroup->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
// Create a Display
$this->display = $this->createDisplay();
// Assign my Display to the Group
$this->getEntityProvider()->post('/displaygroup/' . $this->displayGroup->displayGroupId . '/display/assign', [
'displayId' => [$this->display->displayId]
]);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the Display Group
$this->displayGroup->delete();
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Unassign
$this->sendRequest('POST','/displaygroup/' . $this->displayGroup->displayGroupId . '/display/unassign', [
'displayId' => [$this->display->displayId]
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,181 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupDynamicDisplayTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupDynamicDisplayTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplayGroup */
protected $displayGroup;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Create a Display Group
// this matches all displays created by the test suite
$this->displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create(
Random::generateString(),
'Cache Test',
1,
'phpunit');
$this->getLogger()->debug('DisplayGroup created with ID ' . $this->displayGroup->displayGroupId);
// Schedule the Layout "always" onto our display group
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->displayGroup->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->getLogger()->debug('Schedule created with ID ' . $event->eventId);
// Create a Display
$this->display = $this->createDisplay();
// Run regular maintenance to add the new display to our group.
$this->runRegularMaintenance();
$this->getLogger()->debug('Display created with ID ' . $this->display->displayId);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display Group
$this->displayGroup->delete();
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
$this->getLogger()->debug('Renaming display');
// Rename the display
$response = $this->sendRequest('PUT','/display/' . $this->display->displayId, [
'display' => Random::generateString(10, 'testedited'),
'defaultLayoutId' => $this->display->defaultLayoutId,
'auditingUntil' => null,
'licensed' => $this->display->licensed,
'license' => $this->display->license,
'incSchedule' => $this->display->incSchedule,
'emailAlert' => $this->display->emailAlert,
'wakeOnLanEnabled' => $this->display->wakeOnLanEnabled,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
// There isn't anything directly on the display - so that will NOT trigger anything. The schedule is on the Display Group.
$this->getLogger()->debug('Finished renaming display');
$this->assertLessThan(300, $response->getStatusCode(), 'Non-success status code, body =' . $response->getBody()->getContents());
// Initially we're expecting no change.
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Run regular maintenance
$this->runRegularMaintenance();
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Our player action would have been sent by regular maintenance, not by the edit.
// Make sure we don't have one here.
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
private function runRegularMaintenance()
{
$this->getLogger()->debug('Running Regular Maintenance');
exec('cd /var/www/cms; php bin/run.php 2');
$this->getLogger()->debug('Finished Regular Maintenance');
}
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Xibo\Entity\Display;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupLayoutAssignTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupLayoutAssignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Add the Layout we have prepared to the Display Group
$response = $this->sendRequest('POST','/displaygroup/' . $this->display->displayGroupId . '/layout/assign', [
'layoutId' => [$this->layout->layoutId]
]);
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,120 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Xibo\Entity\Display;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupLayoutUnssignTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupLayoutUnssignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a simple widget
$this->addSimpleWidget($layout);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
// Assign the Layout to the Display
$this->getEntityProvider()->post('/displaygroup/' . $this->display->displayGroupId . '/layout/assign', [
'layoutId' => [$this->layout->layoutId]
]);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Add the Layout we have prepared to the Display Group
$response = $this->sendRequest('POST','/displaygroup/' . $this->display->displayGroupId . '/layout/unassign', [
'layoutId' => [$this->layout->layoutId]
]);
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,102 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Xibo\Entity\Display;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupMediaAssignTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupMediaAssignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLibrary */
protected $media;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Add a media item
$this->media = (new XiboLibrary($this->getEntityProvider()))
->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/HLH264.mp4');
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->media->deleteAssigned();
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Add the Layout we have prepared to the Display Group
$this->sendRequest('POST','/displaygroup/' . $this->display->displayGroupId . '/media/assign', [
'mediaId' => [$this->media->mediaId]
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,107 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Xibo\Entity\Display;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupMediaUnassignTest
* @package Xibo\Tests\integration\Cache
*/
class DisplayGroupMediaUnassignTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLibrary */
protected $media;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Add a media item
$this->media = (new XiboLibrary($this->getEntityProvider()))
->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/HLH264.mp4');
// Create a Display
$this->display = $this->createDisplay();
// Assign the mediaId to the display
$this->getEntityProvider()->post('/displaygroup/' . $this->display->displayGroupId . '/media/assign', [
'mediaId' => [$this->media->mediaId]
]);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->media->deleteAssigned();
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Add the Layout we have prepared to the Display Group
$this->sendRequest('POST','/displaygroup/' . $this->display->displayGroupId . '/media/unassign', [
'mediaId' => [$this->media->mediaId]
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,167 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class GetResourceTest
* @package Xibo\Tests\integration\Cache
*/
class GetResourceTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboTicker */
protected $widget;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a resource heavy module to the Layout (one that will download images)
$response = $this->getEntityProvider()->post('/playlist/widget/ticker/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'uri' => 'http://ceu.xibo.co.uk/mediarss/feed.xml',
'duration' => 100,
'useDuration' => 1,
'sourceId' => 1,
'templateId' => 'media-rss-with-title'
]);
// Edit the Ticker to add the template
$this->widget = (new XiboTicker($this->getEntityProvider()))->hydrate($response);
// Checkin
$this->layout = $this->publish($this->layout);
// Set the Layout status
$this->setLayoutStatus($this->layout, 3);
// Build the Layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Layout is already status 1
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layout->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
$this->assertContains('layoutid="' . $this->layout->layoutId . '"', $rf, 'Layout not in Required Files');
// Call Get Resource
$this->getLogger()->debug('Calling GetResource - for ' . $this->layout->layoutId . ' - ' . $this->layout->regions[0]->regionId . ' - ' . $this->widget->widgetId);
$this->getXmdsWrapper()->GetResource($this->display->license, $this->layout->layoutId, $this->layout->regions[0]->regionId, $this->widget->widgetId);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
}
}

View File

@@ -0,0 +1,151 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutBuildTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutBuildTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboTicker */
protected $widget;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Layout is already status 1
$this->assertTrue($this->layoutStatusEquals($this->layout, 3), 'Pre-Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Pre-Display Status isnt as expected');
// Publish (which builds)
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layout->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$object = json_decode($response->getBody(), true);
$this->layout = $this->constructLayoutFromResponse($object['data']);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Xibo\Entity\Display;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutChangeActionTest
*
* Tests whether a Layout Edit updates the Cache Appropriately
*
* @package integration\Cache
*/
class LayoutChangeActionTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Layout Edit Test');
// Create a Layout
$this->layout = $this->createLayout(1);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure we're in good condition to start
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Edit the Layout
$this->sendRequest('POST','/displaygroup/' . $this->display->displayGroupId . '/action/changeLayout', [
'layoutId' => $this->layout->layoutId
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Exception\XiboApiException;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutDeleteTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutDeleteTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Layout Edit Test');
// Create a Layout
$this->layout = $this->createLayout(1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
if ($this->layout !== null)
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Delete the Layout we've got created for us.
$this->sendRequest('DELETE','/layout/' . $this->layout->layoutId);
// Check its deleted
try {
$this->layoutStatusEquals($this->layout, 0);
} catch (XiboApiException $xiboApiException) {
$this->assertEquals(404, $xiboApiException->getCode(), 'Expecting a 404, got ' . $xiboApiException->getCode());
}
$this->layout = null;
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutEditTest
*
* Tests whether a Layout Edit updates the Cache Appropriately
*
* @package integration\Cache
*/
class LayoutEditTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Layout Edit Test');
// Create a Layout
$this->layout = $this->createLayout(1);
// We need to add a widget to it, so that the Layout tests out as valid
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$this->layout = $this->publish($this->layout);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure we're in good condition to start
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Checkout this Layout
$layout = $this->checkout($this->layout);
// Validate the display status after we've checked out
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected after checkout');
// Edit the Layout
$response = $this->sendRequest('PUT','/layout/background/' . $layout->layoutId, [
'backgroundColor' => $layout->backgroundColor,
'backgroundzIndex' => $layout->backgroundzIndex
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertEquals(200, $response->getStatusCode(), 'Transaction Status Incorrect');
// Check in the Layout
$this->layout = $this->publish($this->layout);
// Validate the layout status afterwards (publish builds the layout)
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected after publish');
// Somehow test that we have issued an XMR request
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,168 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutInCampaignStatusTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutInCampaignStatusTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboCampaign */
protected $campaign;
/** @var XiboLayout */
protected $layout;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboTicker */
protected $widget;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Campaign
$this->campaign = (new XiboCampaign($this->getEntityProvider()))->create(Random::generateString());
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Assign the layout to our campaign
$this->getEntityProvider()->post('/campaign/layout/assign/' . $this->campaign->campaignId, [
'layoutId' => $this->layout->layoutId
]);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Campaign "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->campaign->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the Campaign
$this->campaign->delete();
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Layout is already status 1
$this->assertTrue($this->layoutStatusEquals($this->layout, 3), 'Pre-Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Pre-Display Status isnt as expected');
// Publish (which builds)
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layout->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getStatusCode() . $response->getBody()->getContents());
$response = json_decode($response->getBody(), true);
$this->layout = $this->constructLayoutFromResponse($response['data']);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Xibo\Entity\Display;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutOverlayActionTest
*
* Tests whether a Layout Edit updates the Cache Appropriately
*
* @package integration\Cache
*/
class LayoutOverlayActionTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Layout Edit Test');
// Create a Layout
$this->layout = $this->createLayout(1);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure we're in good condition to start
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Edit the Layout
$this->sendRequest('POST','/displaygroup/' . $this->display->displayGroupId . '/action/overlayLayout', [
'layoutId' => $this->layout->layoutId
]);
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,272 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaInheritWidgetInheritTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaInheritWidgetInheritTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOff
$this->deleteLayout($this->layoutOff);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media Inherit, Widget Inherit, Output => [0, 'Inherit', 'Inherit', 0]
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media Inherit, Widget Inherit, Output => [1, 'Inherit', 'Inherit', 1]
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,282 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaInheritWidgetOffTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaInheritWidgetOffTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Set global widget enable stat set to Off
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Off');
$this->getStore()->commitIfNecessary();
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOn
$this->deleteLayout($this->layoutOff);
// Delete the Display2
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
// Set global widget enable stat set to Inherit
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Inherit');
$this->getStore()->commitIfNecessary();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media Inherit, Widget Off, Output => [0, 'Inherit', 'Off', 0],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media Inherit, Widget Off, Output => [1, 'Off', 'Off', 0],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,280 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaInheritWidgetOnTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaInheritWidgetOnTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Set global widget enable stat set to On
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'On');
$this->getStore()->commitIfNecessary();
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOn
$this->deleteLayout($this->layoutOff);
// Delete the Display2
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
// Set global widget enable stat set to Inherit
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Inherit');
$this->getStore()->commitIfNecessary();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media Inherit, Widget On, Output => [0, 'Inherit', 'On', 1],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media Inherit, Widget On, Output => [1, 'Inherit', 'On', 1],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,285 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaOffWidgetInheritTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaOffWidgetInheritTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Edit the media to set enableStat On
$this->media->edit(
$this->media->name,
$this->media->duration,
$this->media->retired,
$this->media->tags,
$this->media->updateInLayouts,
'Off'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOff
$this->deleteLayout($this->layoutOff);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media Off, Widget Inherit, Output => [0, 'Off', 'Inherit', 0],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media Off, Widget Inherit, Output => [1, 'Off', 'Inherit', 0],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,292 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaOffWidgetOffTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaOffWidgetOffTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Set global widget enable stat set to Off
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Off');
$this->getStore()->commitIfNecessary();
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Edit the media to set enableStat On
$this->media->edit(
$this->media->name,
$this->media->duration,
$this->media->retired,
$this->media->tags,
$this->media->updateInLayouts,
'Off'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOn
$this->deleteLayout($this->layoutOff);
// Delete the Display2
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
// Set global widget enable stat set to Inherit
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Inherit');
$this->getStore()->commitIfNecessary();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media Off, Widget Off, Output => [0, 'Off', 'Off', 0],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media Off, Widget Off, Output => [1, 'Off', 'Off', 0],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,292 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaOffWidgetOnTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaOffWidgetOnTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Set global widget enable stat set to On
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'On');
$this->getStore()->commitIfNecessary();
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Edit the media to set enableStat On
$this->media->edit(
$this->media->name,
$this->media->duration,
$this->media->retired,
$this->media->tags,
$this->media->updateInLayouts,
'Off'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOn
$this->deleteLayout($this->layoutOff);
// Delete the Display2
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
// Set global widget enable stat set to Inherit
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Inherit');
$this->getStore()->commitIfNecessary();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media Off, Widget On, Output => [0, 'Off', 'On', 1],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media Off, Widget On, Output => [1, 'Off', 'On', 1],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,283 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaOnWidgetInheritTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaOnWidgetInheritTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Edit the media to set enableStat On
$this->media->edit(
$this->media->name,
$this->media->duration,
$this->media->retired,
$this->media->tags,
$this->media->updateInLayouts,
'On'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOff
$this->deleteLayout($this->layoutOff);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media On, Widget Inherit, Output => [0, 'On', 'Inherit', 1],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media On, Widget Inherit, Output => [1, 'On', 'Inherit', 1],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,294 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaOnWidgetOffTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaOnWidgetOffTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Set global widget enable stat set to Off
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Off');
$this->getStore()->commitIfNecessary();
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Edit the media to set enableStat On
$this->media->edit(
$this->media->name,
$this->media->duration,
$this->media->retired,
$this->media->tags,
$this->media->updateInLayouts,
'On'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOn
$this->deleteLayout($this->layoutOff);
// Delete the Display2
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
// Set global widget enable stat set to Inherit
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Inherit');
$this->getStore()->commitIfNecessary();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media On, Widget Off, Output => [0, 'On', 'Off', 0],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media On, Widget Off, Output => [1, 'On', 'Off', 0],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="0" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,294 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLMediaOnWidgetOnTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLMediaOnWidgetOnTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $mediaOn;
protected $widgetId;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Set global widget enable stat set to On
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'On');
$this->getStore()->commitIfNecessary();
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
$layoutOff = $this->getDraft($this->layoutOff);
// Upload some media - enableStat is Inherit (from global media stat setting)
$this->media = (new XiboLibrary($this->getEntityProvider()))->create(
Random::generateString(8, 'API Video'),
PROJECT_ROOT . '/tests/resources/HLH264.mp4'
);
// Edit the media to set enableStat On
$this->media->edit(
$this->media->name,
$this->media->duration,
$this->media->retired,
$this->media->tags,
$this->media->updateInLayouts,
'On'
);
// Assign the media we've edited to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOff->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
$layoutOn = $this->getDraft($this->layoutOn);
// Assign the media we've created to our regions playlist- widget with Inherit (from global widget stat setting)
$playlist2 = (new XiboPlaylist($this->getEntityProvider()))
->assign([$this->media->mediaId], 10, $layoutOn->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId2 = $playlist2->widgets[0]->widgetId;
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOn
$this->deleteLayout($this->layoutOff);
// Delete the Display2
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
// Delete the media record
$this->media->deleteAssigned();
// Set global widget enable stat set to Inherit
self::$container->get('configService')->changeSetting('WIDGET_STATS_ENABLED_DEFAULT', 'Inherit');
$this->getStore()->commitIfNecessary();
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget With Media
// LAYOUT MEDIA WIDGET Media stats collected?
// ON ON ON YES Widget takes precedence // Match - 1
// ON OFF ON YES Widget takes precedence // Match - 1
// ON INHERIT ON YES Widget takes precedence // Match - 1
//
// OFF ON ON YES Widget takes precedence // Match - 1
// OFF OFF ON YES Widget takes precedence // Match - 1
// OFF INHERIT ON YES Widget takes precedence // Match - 1
//
// ON ON OFF NO Widget takes precedence // Match - 2
// ON OFF OFF NO Widget takes precedence // Match - 2
// ON INHERIT OFF NO Widget takes precedence // Match - 2
//
// OFF ON OFF NO Widget takes precedence // Match - 2
// OFF OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT OFF NO Widget takes precedence // Match - 2
//
// ON ON INHERIT YES Media takes precedence // Match - 3
// ON OFF INHERIT NO Media takes precedence // Match - 4
// ON INHERIT INHERIT YES Media takes precedence and Inherited from Layout // Match - 5
//
// OFF ON INHERIT YES Media takes precedence // Match - 3
// OFF OFF INHERIT NO Media takes precedence // Match - 4
// OFF INHERIT INHERIT NO Media takes precedence and Inherited from Layout // Match - 6
////
public function testLayoutOff()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
// Layout enable stat 0
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="0">', $xmlString );
// Layout Off, Media On, Widget On, Output => [0, 'On', 'On', 1],
$this->assertContains('<media id="'.$this->widgetId.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
public function testLayoutOn()
{
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
// Layout enable stat 1
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="1">', $xmlString );
// Layout On, Media On, Widget On, Output => [1, 'On', 'On', 1],
$this->assertContains('<media id="'.$this->widgetId2.'" type="video" render="native" duration="10" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="1" fileId="'.$this->media->mediaId.'">', $xmlString );
}
}

View File

@@ -0,0 +1,280 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutProofOfPlayXMLWithoutMediaTest
* @package Xibo\Tests\integration\Cache
*/
class LayoutProofOfPlayXMLWithoutMediaTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layoutOff;
/** @var XiboLayout */
protected $layoutOn;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboTicker */
protected $widget;
protected $media;
protected $widgetId2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout with enableStat Off (by default)
$this->layoutOff = $this->createLayout();
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layoutOff will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOff->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a layout with enableStat On
$this->layoutOn = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit description',
'',
$this->getResolutionId('landscape'),
1
);
// Create a Display2
$this->display2 = $this->createDisplay();
// Schedule the LayoutOn "always" onto our display
// deleting the layoutOn will remove this at the end
$event2 = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->addSeconds(3600)->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layoutOn->campaignId,
[$this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display2, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display2);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the LayoutOff
$this->deleteLayout($this->layoutOff);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the LayoutOn
$this->deleteLayout($this->layoutOn);
// Delete the Display2
$this->deleteDisplay($this->display2);
parent::tearDown();
}
// </editor-fold>
// Logic Table
//
// Widget Without Media
// LAYOUT WIDGET Widget stats collected?
// ON ON YES Widget takes precedence // Match - 1
// ON OFF NO Widget takes precedence // Match - 2
// ON INHERIT YES Inherited from Layout // Match - 7
// OFF ON YES Widget takes precedence // Match - 1
// OFF OFF NO Widget takes precedence // Match - 2
// OFF INHERIT NO Inherited from Layout // Match - 8
/**
* Each array is a test run
* Format (enableStat)
* @return array
*/
public function layoutEnableStatOffCases()
{
return [
// Layout enableStat Off options - for layout and widget and their expected result (Widget stats collected?) in enableStat (media node attribute)
'Layout Off Media On' => [0, 'On', 1],
'Layout Off Media Off' => [0, 'Off', 0],
'Layout Off Media Inherit' => [0, 'Inherit', 0]
];
}
/**
* Each array is a test run
* Format (enableStat)
* @return array
*/
public function layoutEnableStatOnCases()
{
return [
// Layout enableStat On options - for layout and widget and their expected result (Widget stats collected?) in enableStat (media node attribute)
'Layout On Media On' => [1, 'On', 1],
'Layout On Media Off' => [1, 'Off', 0],
'Layout On Media Inherit' => [1, 'Inherit', 1]
];
}
/**
* Edit
* @dataProvider layoutEnableStatOffCases
*/
public function testLayoutOff($layoutEnableStat, $widgetEnableStat, $outputEnableStat)
{
// Checkout
$layoutOff = $this->getDraft($this->layoutOff);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layoutOff->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'] , [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1,
'enableStat' => $widgetEnableStat
]);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOff->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOff = $this->constructLayoutFromResponse($response['data']);
$this->getLogger()->debug($this->layoutOff->enableStat);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display->license);
$this->assertContains('file="' . $this->layoutOff->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display->license, $this->layoutOff->layoutId, 'layout', 0, 0);
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="'.$layoutEnableStat.'">', $xmlString );
$this->assertContains('<media id="'.$this->widget->widgetId.'" type="text" render="native" duration="100" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="'.$outputEnableStat.'">', $xmlString );
}
/**
* Edit
* @dataProvider layoutEnableStatOnCases
*/
public function testLayoutOn($layoutEnableStat, $widgetEnableStat, $outputEnableStat)
{
// Checkout
$layoutOn = $this->getDraft($this->layoutOn);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layoutOn->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'] , [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1,
'enableStat' => $widgetEnableStat
]);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Publish layout
$response = $this->sendRequest('PUT','/layout/publish/' . $this->layoutOn->layoutId, [
'publishNow' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$response = json_decode($response->getBody(), true);
$this->layoutOn = $this->constructLayoutFromResponse($response['data']);
$this->getLogger()->debug($this->layoutOn->enableStat);
// Confirm our Layout is in the Schedule
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layoutOn->layoutId . '"', $schedule, 'Layout not scheduled');
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($this->display2->license);
// Get XML string for player
$xmlString = $this->getXmdsWrapper()->GetFile($this->display2->license, $this->layoutOn->layoutId, 'layout', 0, 0);
$this->assertContains('<layout width="1920" height="1080" bgcolor="#000" schemaVersion="3" enableStat="'.$layoutEnableStat.'">', $xmlString );
$this->assertContains('<media id="'.$this->widget->widgetId.'" type="text" render="native" duration="100" useDuration="1" fromDt="1970-01-01 01:00:00" toDt="2038-01-19 03:14:07" enableStat="'.$outputEnableStat.'">', $xmlString );
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
class LayoutRetireTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Layout Retire Test');
// Create a Layout
$this->layout = $this->createLayout(1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Retire the Layout we've got created for us.
$this->sendRequest('PUT','/layout/retire/' . $this->layout->layoutId, [], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
// Validate the layout status
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LibraryReviseTest
*
* Tests whether a Layout Edit updates the Cache Appropriately
*
* @package integration\Cache
*/
class LibraryReviseTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLibrary */
protected $media;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Layout Edit Test');
// Upload some media
$this->media = (new XiboLibrary($this->getEntityProvider()))
->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/xts-flowers-001.jpg');
// Create a Layout
$this->layout = $this->createLayout(1);
// Checkout
$layout = $this->getDraft($this->layout);
// Add it to the Layout
(new XiboPlaylist($this->getEntityProvider()))->assign([$this->media->mediaId], 10, $layout->regions[0]->regionPlaylist->playlistId);
// Publish
$this->layout = $this->publish($this->layout);
// Set the Layout status (force it)
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->getLogger()->debug('Finished setup');
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the media record
$this->media->deleteAssigned();
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure we're in good condition to start
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout status is not as expected');
// Replace the Media
$this->media = (new XiboLibrary($this->getEntityProvider()))
->create(Random::generateString(), PROJECT_ROOT . '/tests/resources/xts-flowers-002.jpg', $this->media->mediaId, 1, 1);
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 3), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class PlaylistReorderTest
* @package Xibo\Tests\integration\Cache
*/
class PlaylistReorderTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
protected $widget1;
protected $widget2;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Region Edit Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a couple of text widgets to the region
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$this->widget1 = (new XiboText($this->getEntityProvider()))->hydrate($response);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget B',
'duration' => 100,
'useDuration' => 1
]);
$this->widget2 = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Publish
$this->layout = $this->publish($this->layout);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Checkout
$layout = $this->checkout($this->layout);
// Edit region
$this->sendRequest('POST','/playlist/order/' . $layout->regions[0]->regionPlaylist->playlistId, [
'widgets' => [
$this->widget1->widgetId => 2,
$this->widget2->widgetId => 1
]
]);
// This shouldn't effect the display
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Publish
$this->layout = $this->publish($this->layout);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class RegionDeleteTest
* @package Xibo\Tests\integration\Cache
*/
class RegionDeleteTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Region Delete Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a widget to the existing region
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Add a region to the Layout
$this->region = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,300,75,125);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Edit region
$this->sendRequest('DELETE','/region/' . $this->region->regionId);
// This shouldn't effect the display
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Checkin
$this->layout = $this->publish($this->layout);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,144 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class RegionEditTest
* @package Xibo\Tests\integration\Cache
*/
class RegionEditTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboRegion */
protected $region;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Region Edit Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
// Add a widget to the existing region
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Edit region
$this->sendRequest('PUT','/region/' . $this->layout->regions[0]->regionId, [
'width' => 700,
'height' => 500,
'top' => 400,
'left' => 400,
'loop' => 0,
'zIndex' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Checkin
$this->layout = $this->publish($this->layout);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,159 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ScheduleChangeInsideRfTest
* @package Xibo\Tests\integration\Cache
*/
class ScheduleChangeInsideRfTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboTicker */
protected $widget;
/** @var XiboSchedule */
protected $event;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Check us in again
$this->layout = $this->publish($this->layout);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Layout is already status 1
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Change the Schedule
$response = $this->sendRequest('PUT','/schedule/' . $this->event->eventId, [
'fromDt' => Carbon::createFromTimestamp($this->event->fromDt)->format(DateFormatHelper::getSystemFormat()),
'toDt' => Carbon::createFromTimestamp($this->event->toDt)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->event->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertTrue(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,161 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\OAuth2\Client\Entity\XiboTicker;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ScheduleChangeOutsideRfTest
* @package Xibo\Tests\integration\Cache
*/
class ScheduleChangeOutsideRfTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboTicker */
protected $widget;
/** @var XiboSchedule */
protected $event;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Check us in again
$this->layout = $this->publish($this->layout);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
// Dates outside of RF
$date = Carbon::now()->addMonth();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
$date->format(DateFormatHelper::getSystemFormat()),
$date->addHour()->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Make sure our Layout is already status 1
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Change the Schedule
$this->sendRequest('PUT','/schedule/' . $this->event->eventId, [
'fromDt' => date(DateFormatHelper::getSystemFormat(), $this->event->fromDt),
'toDt' => date(DateFormatHelper::getSystemFormat(), $this->event->toDt),
'eventTypeId' => 1,
'campaignId' => $this->event->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Validate that XMR has been called.
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class WidgetDeleteTest
* @package Xibo\Tests\integration\Cache
*/
class WidgetDeleteTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
protected $widget;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Region Delete Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget B',
'duration' => 100,
'useDuration' => 1
]);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Edit region
$response = $this->sendRequest('DELETE','/playlist/widget/' . $this->widget->widgetId);
$this->assertEquals(200, $response->getStatusCode(), 'Transaction Status Incorrect');
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Publish
$this->layout = $this->publish($this->layout);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,142 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration\Cache;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class WidgetEditTest
* @package Xibo\Tests\integration\Cache
*/
class WidgetEditTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
protected $widget;
/** @var XiboDisplay */
protected $display;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache Region Edit Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId, [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$this->widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
// Create a Display
$this->display = $this->createDisplay();
// Schedule the Layout "always" onto our display
// deleting the layout will remove this at the end
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
}
public function tearDown()
{
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
/**
* @group cacheInvalidateTests
*/
public function testInvalidateCache()
{
// Edit region
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widget->widgetId, [
'text' => 'Edited Text',
'duration' => 100,
'useDuration' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertEquals(200, $response->getStatusCode(), 'Transaction Status Incorrect');
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Check us in again
$this->layout = $this->publish($this->layout);
// Check the Layout Status
// Validate the layout status afterwards
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Validate the display status afterwards
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_PENDING), 'Display Status isnt as expected');
// Somehow test that we have issued an XMR request
$this->assertFalse(in_array($this->display->displayId, $this->getPlayerActionQueue()), 'Player action not present');
}
}

View File

@@ -0,0 +1,142 @@
<?php
/*
* Copyright (C) 2021 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Support\Exception\InvalidArgumentException;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class CampaignLayoutManagementTest
* @package Xibo\Tests\integration
*/
class CampaignLayoutManagementTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboCampaign */
protected $campaign;
/** @var XiboLayout */
protected $layout;
public function setup()
{
parent::setup();
// Create a Campaign and Layout
$this->campaign = (new XiboCampaign($this->getEntityProvider()))->create(Random::generateString());
$this->layout = $this->createLayout();
$this->layout = $this->publish($this->layout);
}
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Campaign
$this->campaign->delete();
parent::tearDown();
}
/**
* Assign Layout
*/
public function testAssignOneLayout()
{
// Assign one layout
$response = $this->sendRequest('POST', '/campaign/layout/assign/' . $this->campaign->campaignId, [
'layoutId' => $this->layout->layoutId
]);
$this->assertSame(200, $response->getStatusCode(), 'Request failed: ' . $response->getBody()->getContents());
// Get this campaign and check it has 1 layout assigned
$campaignCheck = (new XiboCampaign($this->getEntityProvider()))->getById($this->campaign->campaignId);
$this->assertSame($this->campaign->campaignId, $campaignCheck->campaignId, $response->getBody());
$this->assertSame(1, $campaignCheck->numberLayouts, $response->getBody());
}
/**
* Assign Layout
*/
public function testAssignTwoLayouts()
{
$response = $this->sendRequest('PUT', '/campaign/' . $this->campaign->campaignId, [
'name' => $this->campaign->campaign,
'manageLayouts' => 1,
'layoutIds' => [$this->layout->layoutId, $this->layout->layoutId]
]);
$this->assertSame(200, $response->getStatusCode(), 'Request failed');
// Get this campaign and check it has 2 layouts assigned
$campaignCheck = (new XiboCampaign($this->getEntityProvider()))->getById($this->campaign->campaignId);
$this->assertSame($this->campaign->campaignId, $campaignCheck->campaignId, $response->getBody());
$this->assertSame(2, $campaignCheck->numberLayouts, $response->getBody());
}
/**
* Unassign Layout
*/
public function testUnassignLayout()
{
$this->getEntityProvider()->post('/campaign/layout/assign/' . $this->campaign->campaignId, [
'layoutId' => $this->layout->layoutId
]);
$response = $this->sendRequest('PUT', '/campaign/' . $this->campaign->campaignId, [
'name' => $this->campaign->campaign,
'manageLayouts' => 1,
'layoutIds' => []
]);
$campaignCheck = (new XiboCampaign($this->getEntityProvider()))->getById($this->campaign->campaignId);
$this->assertSame($this->campaign->campaignId, $campaignCheck->campaignId, $response->getBody());
$this->assertSame(0, $campaignCheck->numberLayouts, $response->getBody());
}
/**
* Assign Layout to layout specific campaignId - expect failure
* @throws \Exception
*/
public function testAssignLayoutFailure()
{
// Call assign on the layout specific campaignId
$request = $this->createRequest('POST', '/campaign/layout/assign/' . $this->layout->campaignId);
$request = $request->withParsedBody([
'layoutId' => $this->layout->layoutId
]);
try {
$this->app->handle($request);
} catch (InvalidArgumentException $exception) {
$this->assertSame(422, $exception->getCode(), 'Expecting failure, received ' . $exception->getMessage());
}
}
}

View File

@@ -0,0 +1,216 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboResolution;
use Xibo\Tests\LocalWebTestCase;
/**
* Class CampaignTest
* @package Xibo\Tests
*/
class CampaignTest extends LocalWebTestCase
{
protected $startCampaigns;
protected $startLayouts;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startCampaigns = (new XiboCampaign($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all campaigns that weren't there initially
$finalCamapigns = (new XiboCampaign($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining campaigns and nuke them
foreach ($finalCamapigns as $campaign) {
/** @var XiboCampaign $campaign */
$flag = true;
foreach ($this->startCampaigns as $startCampaign) {
if ($startCampaign->campaignId == $campaign->campaignId) {
$flag = false;
}
}
if ($flag) {
try {
$campaign->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $campaign->campaignId . '. E:' . $e->getMessage());
}
}
}
// tearDown all layouts that weren't there initially
$finalLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining layouts and nuke them
foreach ($finalLayouts as $layout) {
/** @var XiboLayout $layout */
$flag = true;
foreach ($this->startLayouts as $startLayout) {
if ($startLayout->layoutId == $layout->layoutId) {
$flag = false;
}
}
if ($flag) {
try {
$layout->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $layout->layoutId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* @param $type
* @return int
*/
private function getResolutionId($type)
{
if ($type === 'landscape') {
$width = 1920;
$height = 1080;
} else if ($type === 'portrait') {
$width = 1080;
$height = 1920;
} else {
return -10;
}
//$this->getLogger()->debug('Querying for ' . $width . ', ' . $height);
$resolutions = (new XiboResolution($this->getEntityProvider()))->get(['width' => $width, 'height' => $height]);
if (count($resolutions) <= 0)
return -10;
return $resolutions[0]->resolutionId;
}
/**
* Show Campaigns
*/
public function testListAll()
{
# Get list of all campaigns
$response = $this->sendRequest('GET', '/campaign');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$object = json_decode($response->getBody());
# Check if call was successful
$this->assertObjectHasAttribute('data', $object);
$this->assertNotEmpty($object->data);
}
/**
* Add Campaign
*/
public function testAdd()
{
# Generate random name
$name = Random::generateString(8, 'phpunit');
# Add campaign
$response = $this->sendRequest('POST', '/campaign', ['name' => $name]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if campaign has he name we want it to have
$this->assertSame($name, $object->data->campaign);
}
/**
* Test edit
*/
public function testEdit()
{
# Generate name and add campaign
$name = Random::generateString(8, 'phpunit');
$campaign = (new XiboCampaign($this->getEntityProvider()))->create($name);
# Generate new random name
$newName = Random::generateString(8, 'phpunit');
# Edit the campaign we added and change the name
$response = $this->sendRequest('PUT', '/campaign/' . $campaign->campaignId, ['name' => $newName]);
# check if cal was successful
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
# Check if campaign has the new name now
$this->assertSame($newName, $object->data->campaign);
}
/**
* Test Delete
*/
public function testDelete()
{
# generate two random names
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known campaigns
$camp1 = (new XiboCampaign($this->getEntityProvider()))->create($name1);
$camp2 = (new XiboCampaign($this->getEntityProvider()))->create($name2);
# Delete the one we created last
$response = $this->sendRequest('DELETE', '/campaign/' . $camp2->campaignId);
# This should return 204 for success
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$campaigns = (new XiboCampaign($this->getEntityProvider()))->get();
$this->assertEquals(count($this->startCampaigns) + 1, count($campaigns));
$flag = false;
foreach ($campaigns as $campaign) {
if ($campaign->campaignId == $camp1->campaignId) {
$flag = true;
}
}
# Check if everything is in order
$this->assertTrue($flag, 'Campaign ID ' . $camp1->campaignId . ' was not found after deleting a different campaign');
# Cleanup
$camp1->delete();
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
class ClockTest extends \Xibo\Tests\LocalWebTestCase
{
public function testView()
{
$response = $this->sendRequest('GET','/clock');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$response = json_decode($response->getBody());
$this->assertNotEmpty($response->data);
}
}

View File

@@ -0,0 +1,276 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCommand;
use Xibo\Tests\LocalWebTestCase;
class CommandTest extends LocalWebTestCase
{
protected $startCommands;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startCommands = (new XiboCommand($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all commands that weren't there initially
$finalCommands = (new XiboCommand($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining commands and nuke them
foreach ($finalCommands as $command) {
/** @var XiboCommand $command */
$flag = true;
foreach ($this->startCommands as $startCom) {
if ($startCom->commandId == $command->commandId) {
$flag = false;
}
}
if ($flag) {
try {
$command->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $command->commandId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* Shows this user commands
*/
public function testListAll()
{
# Get the list of all commands
$response = $this->sendRequest('GET','/command');
# Check if call was successful
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* testAddSuccess - test adding various commands that should be valid
* @dataProvider provideSuccessCases
* @group minimal
*/
public function testAddSuccess($commandName, $commandDescription, $commandCode)
{
// Loop through any pre-existing commands to make sure we're not
// going to get a clash
foreach ($this->startCommands as $tmpCom) {
if ($tmpCom->command == $commandName) {
$this->skipTest("There is a pre-existing command with this name");
return;
}
}
# Add new comands with arguments from provideSuccessCases
$response = $this->sendRequest('POST','/command', [
'command' => $commandName,
'description' => $commandDescription,
'code' => $commandCode
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
# Check if commands were added successfully and have correct parameters
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($commandName, $object->data->command);
$this->assertSame($commandDescription, $object->data->description);
$this->assertSame($commandCode, $object->data->code);
# Check again that the command was added correctly
$command = (new XiboCommand($this->getEntityProvider()))->getById($object->id);
$this->assertSame($commandName, $command->command);
$this->assertSame($commandDescription, $command->description);
$this->assertSame($commandCode, $command->code);
# Clean up the commands as we no longer need it
$this->assertTrue($command->delete(), 'Unable to delete ' . $command->commandId);
}
/**
* Each array is a test run
* Format (command name, description, code)
* @return array
*/
public function provideSuccessCases()
{
# Cases we provide to testAddSuccess, you can extend it by simply adding new case here
return [
'reboot' => ['test command', 'test description', 'reboot'],
'binary' => ['test command 2', '|01100100|01100001|01101110|00001101', 'binary'],
'sleep' => ['test command 3', 'test description', 'sleep'],
];
}
/**
* testAddFailure - test adding various commands that should be invalid
* @dataProvider provideFailureCases
*/
public function testAddFailure($commandName, $commandDescription, $commandCode)
{
# Add new commands with arguments from provideFailureCases
$response = $this->sendRequest('POST','/command', [
'command' => $commandName,
'description' => $commandDescription,
'code' => $commandCode
]);
# Check if commands are failing as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Each array is a test run
* Format (command name, description, code)
* @return array
*/
public function provideFailureCases()
{
# Cases we provide to testAddFailure, you can extend it by simply adding new case here
return [
'No code' => ['No code', 'aa', NULL],
'Code with space' => ['Code with space', 'Code with space', 'Code with space'],
'Code with symbol' => ['Code with symbol', 'Code with symbol', 'Codewithsymbol$$'],
'No description' => ['no description', NULL, 'code'],
'No Name' => [NULL, 'Bienvenue à la suite de tests Xibo', 'beep'],
'Only Name' => ['Deutsch Prüfung 1', NULL, NULL],
'Empty' => [NULL, NULL, NULL]
];
}
/**
* List all commands known set
* @group minimal
* @depends testAddSuccess
*/
public function testListKnown()
{
$cases = $this->provideSuccessCases();
$commands = [];
// Check each possible case to ensure it's not pre-existing
// If it is, skip over it
foreach ($cases as $case) {
$flag = true;
foreach ($this->startCommands as $tmpCom) {
if ($case[0] == $tmpCom->command) {
$flag = false;
}
}
if ($flag) {
$commands[] = (new XiboCommand($this->getEntityProvider()))->create($case[0],$case[1],$case[2]);
}
}
$response = $this->sendRequest('GET','/command');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be as many commands as we created plus the number we started with in the system
$this->assertEquals(count($commands) + count($this->startCommands), $object->data->recordsTotal);
# Clean up the groups we created
foreach ($commands as $com) {
$com->delete();
}
}
/**
* Edit an existing command
*/
public function testEdit()
{
# Load in a known command
/** @var XiboCommand $command */
$command = (new XiboCommand($this->getEntityProvider()))->create('phpunit command', 'phpunit description', 'phpunitcode');
# Generate new name and description
$name = Random::generateString(8, 'command');
$description = Random::generateString(8, 'description');
# Change name and description of earlier created command
$response = $this->sendRequest('PUT','/command/' . $command->commandId, [
'command' => $name,
'description' => $description,
'code' => $command->code
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->command);
$this->assertSame($description, $object->data->description);
# Check that the command name and description were actually renamed
$command = (new XiboCommand($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $command->command);
$this->assertSame($description, $command->description);
# Clean up the Layout as we no longer need it
$command->delete();
}
/**
* Test delete
* @group minimal
*/
public function testDelete()
{
# Generate random names
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known commands
$command1 = (new XiboCommand($this->getEntityProvider()))->create($name1, 'phpunit description', 'code');
$command2 = (new XiboCommand($this->getEntityProvider()))->create($name2, 'phpunit description', 'codetwo');
# Delete the one we created last
$response = $this->sendRequest('DELETE','/command/' . $command2->commandId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$commands = (new XiboCommand($this->getEntityProvider()))->get();
$this->assertEquals(count($this->startCommands) + 1, count($commands));
$flag = false;
foreach ($commands as $command) {
if ($command->commandId == $command1->commandId) {
$flag = true;
}
}
$this->assertTrue($flag, 'Command ID ' . $command1->commandId . ' was not found after deleting a different command');
# Clean up the first command as we no longer need it
$command1->delete();
}
}

View File

@@ -0,0 +1,161 @@
<?php
/**
* Copyright (C) 2022 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDataSet;
use Xibo\Tests\LocalWebTestCase;
/**
* Test remote datasets
*/
class DataSetRemoteTest extends LocalWebTestCase
{
/** @var \Xibo\OAuth2\Client\Entity\XiboDataSet */
private $dataSet;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
// copy json file to /web folder
shell_exec('cp -r ' . PROJECT_ROOT . '/tests/resources/RemoteDataSet.json ' . PROJECT_ROOT . '/web');
$this->dataSet = (new XiboDataSet($this->getEntityProvider()))
->create(
Random::generateString(8, 'phpunit'),
'',
'remote',
1,
'GET',
'http://localhost/RemoteDataSet.json',
'',
'',
'',
'',
1,
0,
null,
'data'
);
// Add columns
$this->dataSet->createColumn(
'title',
null,
1,
1,
3,
null,
'title'
);
$this->dataSet->createColumn(
'identifier',
null,
2,
2,
3,
null,
'id'
);
$this->dataSet->createColumn(
'date',
null,
3,
3,
3,
null,
'Date'
);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the dataset
$this->dataSet->deleteWData();
// remove json file from /web folder
shell_exec('rm -r ' . PROJECT_ROOT . '/web/RemoteDataSet.json');
parent::tearDown();
}
public function testRemoteDataSetData()
{
// call the remote dataSet test
$response = $this->sendRequest('POST', '/dataset/remote/test', [
'testDataSetId' => $this->dataSet->dataSetId,
'dataSet' => $this->dataSet->dataSet,
'code' => 'remote',
'isRemote' => 1,
'method' => 'GET',
'uri' => 'http://localhost/RemoteDataSet.json',
'dataRoot' => 'data',
'refreshRate' => 0,
'clearRate' => 1,
'sourceId' => 1,
'limitPolicy' => 'stop'
]);
// HTTP response code
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
// Expect a JSON body
$object = json_decode($response->getBody());
// Data and ID parameters
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
// Make sure we have the same dataset back
$this->assertSame($object->id, $this->dataSet->dataSetId);
// Make sure we parsed out some entries.
$this->assertNotEmpty($object->data->entries);
$this->assertNotEmpty($object->data->processed);
// The entries should match our sample file.
$this->assertSame(3, $object->data->number);
// First record
$this->assertSame(1, $object->data->processed[0][0]->identifier);
$this->assertSame('Title 1', $object->data->processed[0][0]->title);
$this->assertSame('2019-07-29 13:11:00', $object->data->processed[0][0]->date);
// Second record
$this->assertSame(2, $object->data->processed[0][1]->identifier);
$this->assertFalse(property_exists($object->data->processed[0][1], 'title'));
$this->assertSame('2019-07-30 03:04:00', $object->data->processed[0][1]->date);
// Third record
$this->assertSame(3, $object->data->processed[0][2]->identifier);
$this->assertSame('1', $object->data->processed[0][2]->title);
$this->assertFalse(property_exists($object->data->processed[0][2], 'date'));
}
}

View File

@@ -0,0 +1,546 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDataSet;
use Xibo\OAuth2\Client\Entity\XiboDataSetColumn;
use Xibo\OAuth2\Client\Entity\XiboDataSetRow;
use Xibo\Tests\LocalWebTestCase;
class DataSetTest extends LocalWebTestCase
{
protected $startDataSets;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startDataSets = (new XiboDataSet($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all datasets that weren't there initially
$finalDataSets = (new XiboDataSet($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$difference = array_udiff($finalDataSets, $this->startDataSets, function ($a, $b) {
/** @var XiboDataSet $a */
/** @var XiboDataSet $b */
return $a->dataSetId - $b->dataSetId;
});
# Loop over any remaining datasets and nuke them
foreach ($difference as $dataSet) {
/** @var XiboDataSet $dataSet */
try {
$dataSet->deleteWData();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $dataSet->dataSetId . '. E: ' . $e->getMessage() . PHP_EOL);
}
}
parent::tearDown();
}
/*
* List all datasets
*/
public function testListAll()
{
$response = $this->sendRequest('GET','/dataset');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* @group add
*/
public function testAdd()
{
# Generate random name
$name = Random::generateString(8, 'phpunit');
# Add dataset
$response = $this->sendRequest('POST','/dataset', [
'dataSet' => $name,
'description' => 'PHP Unit Test'
]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if dataset has the correct name
$this->assertSame($name, $object->data->dataSet);
}
/**
* Test edit
* @depends testAdd
*/
public function testEdit()
{
# Create a new dataset
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create('phpunit dataset', 'phpunit description');
# Generate new name and description
$name = Random::generateString(8, 'phpunit');
$description = 'New description';
# Edit the name and description
$response = $this->sendRequest('PUT','/dataset/' . $dataSet->dataSetId, [
'dataSet' => $name,
'description' => $description
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
# Check if name and description were correctly changed
$this->assertSame($name, $object->data->dataSet);
$this->assertSame($description, $object->data->description);
# Deeper check by querying for dataset again
$dataSetCheck = (new XiboDataSet($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $dataSetCheck->dataSet);
$this->assertSame($description, $dataSetCheck->description);
# Clean up the dataset as we no longer need it
$dataSet->delete();
}
/**
* @depends testEdit
*/
public function testDelete()
{
# Generate new random names
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known dataSets
$data1 = (new XiboDataSet($this->getEntityProvider()))->create($name1, 'phpunit description');
$data2 = (new XiboDataSet($this->getEntityProvider()))->create($name2, 'phpunit description');
# Delete the one we created last
$response = $this->sendRequest('DELETE','/dataset/' . $data2->dataSetId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$dataSets = (new XiboDataSet($this->getEntityProvider()))->get();
$this->assertEquals(count($this->startDataSets) + 1, count($dataSets));
$flag = false;
foreach ($dataSets as $dataSet) {
if ($dataSet->dataSetId == $data1->dataSetId) {
$flag = true;
}
}
$this->assertTrue($flag, 'dataSet ID ' . $data1->dataSetId . ' was not found after deleting a different dataset');
}
# TO DO /dataset/import/
/**
* @dataProvider provideSuccessCases
*/
public function testAddColumnSuccess($columnName, $columnListContent, $columnOrd, $columnDataTypeId, $columnDataSetColumnTypeId, $columnFormula)
{
# Create radom name and description
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit column add';
# Create new dataset
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Create new columns with arguments from provideSuccessCases
$response = $this->sendRequest('POST','/dataset/' . $dataSet->dataSetId . '/column', [
'heading' => $columnName,
'listContent' => $columnListContent,
'columnOrder' => $columnOrd,
'dataTypeId' => $columnDataTypeId,
'dataSetColumnTypeId' => $columnDataSetColumnTypeId,
'formula' => $columnFormula
]);
# Check that call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
# Check that columns have correct parameters
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($columnName, $object->data->heading);
$this->assertSame($columnListContent, $object->data->listContent);
$this->assertSame($columnOrd, $object->data->columnOrder);
$this->assertSame($columnDataTypeId, $object->data->dataTypeId);
$this->assertSame($columnDataSetColumnTypeId, $object->data->dataSetColumnTypeId);
$this->assertSame($columnFormula, $object->data->formula);
# Check that column was correctly added
$column = (new XiboDataSetColumn($this->getEntityProvider()))->getById($dataSet->dataSetId, $object->id);
$this->assertSame($columnName, $column->heading);
# Clean up the dataset as we no longer need it
$this->assertTrue($dataSet->delete(), 'Unable to delete ' . $dataSet->dataSetId);
}
/**
* Each array is a test run
* Format ($columnName, $columnListContent, $columnOrd, $columnDataTypeId, $columnDataSetColumnTypeId, $columnFormula)
* @return array
*/
public function provideSuccessCases()
{
# Cases we provide to testAddColumnSucess, you can extend it by simply adding new case here
return [
# Value
'Value String' => ['Test Column Value String', NULL, 2, 1, 1, NULL],
'List Content' => ['Test Column list content', 'one,two,three', 2, 1, 1, NULL],
'Value Number' => ['Test Column Value Number', NULL, 2, 2, 1, NULL],
'Value Date' => ['Test Column Value Date', NULL, 2, 3, 1, NULL],
'External Image' => ['Test Column Value External Image', NULL, 2, 4, 1, NULL],
'Library Image' => ['Test Column Value Internal Image', NULL, 2, 5, 1, NULL],
# Formula
'Formula' => ['Test Column Formula', NULL, 2, 5, 1, 'Where Name = Dan'],
];
}
/**
* @dataProvider provideFailureCases
*/
public function testAddColumnFailure($columnName, $columnListContent, $columnOrd, $columnDataTypeId, $columnDataSetColumnTypeId, $columnFormula)
{
# Create random name and description
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit column add failure';
# Create new columns that we expect to fail with arguments from provideFailureCases
/** @var XiboDataSet $dataSet */
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
$response = $this->sendRequest('POST','/dataset/' . $dataSet->dataSetId . '/column', [
'heading' => $columnName,
'listContent' => $columnListContent,
'columnOrder' => $columnOrd,
'dataTypeId' => $columnDataTypeId,
'dataSetColumnTypeId' => $columnDataSetColumnTypeId,
'formula' => $columnFormula
]);
# Check if cases are failing as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Each array is a test run
* Format ($columnName, $columnListContent, $columnOrd, $columnDataTypeId, $columnDataSetColumnTypeId, $columnFormula)
* @return array
*/
public function provideFailureCases()
{
# Cases we provide to testAddColumnFailure, you can extend it by simply adding new case here
return [
// Value
'Incorrect dataType' => ['incorrect data type', NULL, 2, 12, 1, NULL],
'Incorrect columnType' => ['incorrect column type', NULL, 2, 19, 1, NULL],
'Empty Name' => [NULL, NULL, 2, 3, 1, NULL],
'Symbol Name' => ['a.b.c', NULL, 2, 3, 1, NULL],
'Symbol Name 2' => ['$£"', NULL, 2, 3, 1, NULL]
];
}
/**
* Search columns for DataSet
*/
public function testListAllColumns()
{
# Create new dataSet
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit column list';
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Add a new column to our dataset
$nameCol = Random::generateString(8, 'phpunit');
$dataSet->createColumn($nameCol,'', 2, 1, 1, '');
# Search for columns
$response = $this->sendRequest('GET','/dataset/' . $dataSet->dataSetId . '/column');
# Check if call was successful
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# Clean up as we no longer need it
$dataSet->delete();
}
/**
* Test edit column
*/
public function testColumnEdit()
{
# Create dataSet
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit column edit';
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Add new column to our dataset
$nameCol = Random::generateString(8, 'phpunit');
$column = (new XiboDataSetColumn($this->getEntityProvider()))->create($dataSet->dataSetId, $nameCol,'', 2, 1, 1, '');
# Generate new random name
$nameNew = Random::generateString(8, 'phpunit');
# Edit our column and change the name
$response = $this->sendRequest('PUT','/dataset/' . $dataSet->dataSetId . '/column/' . $column->dataSetColumnId, [
'heading' => $nameNew,
'listContent' => '',
'columnOrder' => $column->columnOrder,
'dataTypeId' => $column->dataTypeId,
'dataSetColumnTypeId' => $column->dataSetColumnTypeId,
'formula' => ''
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if our column has updated name
$this->assertSame($nameNew, $object->data->heading);
# Clean up as we no longer need it
$dataSet->delete();
}
/**
* @param $dataSetId
* @depends testAddColumnSuccess
*/
public function testDeleteColumn()
{
# Create dataSet
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit column delete';
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Add new column to our dataset
$nameCol = Random::generateString(8, 'phpunit');
$column = (new XiboDataSetColumn($this->getEntityProvider()))->create($dataSet->dataSetId, $nameCol,'', 2, 1, 1, '');
# delete column
$response = $this->sendRequest('DELETE','/dataset/' . $dataSet->dataSetId . '/column/' . $column->dataSetColumnId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
}
/*
* GET data
*/
public function testGetData()
{
# Create dataSet
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit';
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Call get data
$response = $this->sendRequest('GET','/dataset/data/' . $dataSet->dataSetId);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# Clean up
$dataSet->delete();
}
/**
* Test add row
*/
public function testRowAdd()
{
# Create a new dataset to use
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit row add';
/** @var XiboDataSet $dataSet */
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Create column and add it to our dataset
$nameCol = Random::generateString(8, 'phpunit');
$column = (new XiboDataSetColumn($this->getEntityProvider()))->create($dataSet->dataSetId, $nameCol,'', 2, 1, 1, '');
# Add new row to our dataset and column
$response = $this->sendRequest('POST','/dataset/data/' . $dataSet->dataSetId, [
'dataSetColumnId_' . $column->dataSetColumnId => 'test',
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Get the row id
$row = $dataSet->getData();
$this->getLogger()->debug(json_encode($row));
# Check if data was correctly added to the row
$this->assertArrayHasKey($nameCol, $row[0]);
$this->assertSame($row[0][$nameCol], 'test');
# Clean up as we no longer need it, deleteWData will delete dataset even if it has data assigned to it
$dataSet->deleteWData();
}
/**
* Test edit row
* @dataProvider provideSuccessCasesRow
*/
public function testRowEdit($data)
{
# Create a new dataset to use
/** @var XiboDataSet $dataSet */
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit row edit';
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Generate a new name for the new column
$nameCol = Random::generateString(8, 'phpunit');
# Create new column and add it to our dataset
$column = (new XiboDataSetColumn($this->getEntityProvider()))->create($dataSet->dataSetId, $nameCol,'', 2, 1, 1, '');
# Add new row with data to our dataset
$rowD = 'test';
$row = (new XiboDataSetRow($this->getEntityProvider()))->create($dataSet->dataSetId, $column->dataSetColumnId, $rowD);
# Edit row data
$response = $this->sendRequest('PUT','/dataset/data/' . $dataSet->dataSetId . '/' . $row['id'], [
'dataSetColumnId_' . $column->dataSetColumnId => $data
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# get the row id
$rowCheck = $dataSet->getData();
# Check if data was correctly added to the row
$this->assertArrayHasKey($nameCol, $rowCheck[0]);
if ($data == Null){
$this->assertSame($rowCheck[0][$nameCol], $rowD);
}
else {
$this->assertSame($rowCheck[0][$nameCol], $data);
}
# Clean up as we no longer need it, deleteWData will delete dataset even if it has data assigned to it
$dataSet->deleteWData();
}
/**
* Each array is a test run
* Format ($data)
* @return array
*/
public function provideSuccessCasesRow()
{
# Cases we provide to testRowEdit, you can extend it by simply adding new case here
return [
# Value
'String' => ['API EDITED ROW'],
'Null' => [NULL],
'number as string' => ['1212']
];
}
/*
* delete row data
*/
public function testRowDelete()
{
# Create a new dataset to use
/** @var XiboDataSet $dataSet */
$name = Random::generateString(8, 'phpunit');
$description = 'PHP Unit row delete';
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, $description);
# Generate a new name for the new column
$nameCol = Random::generateString(8, 'phpunit');
# Create new column and add it to our dataset
$column = (new XiboDataSetColumn($this->getEntityProvider()))->create($dataSet->dataSetId, $nameCol,'', 2, 1, 1, '');
# Add new row data
$row = (new XiboDataSetRow($this->getEntityProvider()))->create($dataSet->dataSetId, $column->dataSetColumnId, 'Row Data');
# Delete row
$response = $this->sendRequest('DELETE','/dataset/data/' . $dataSet->dataSetId . '/' . $row['id']);
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up as we no longer need it, deleteWData will delete dataset even if it has data assigned to it
$dataSet->deleteWData();
}
public function testAddRemoteDataSet()
{
$name = Random::generateString(8, 'phpunit');
# Add dataset
$response = $this->sendRequest('POST','/dataset', [
'dataSet' => $name,
'code' => 'remote',
'isRemote' => 1,
'method' => 'GET',
'uri' => 'http://localhost/resources/RemoteDataSet.json',
'dataRoot' => 'data',
'refreshRate' => 0,
'clearRate' => 1,
'sourceId' => 1,
'limitPolicy' => 'stop'
]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check dataSet object
$this->assertSame($name, $object->data->dataSet);
$this->assertSame(1, $object->data->isRemote);
$this->assertSame('http://localhost/resources/RemoteDataSet.json', $object->data->uri);
$this->assertSame(1, $object->data->clearRate);
$this->assertSame(0, $object->data->refreshRate);
$this->assertSame(0, $object->data->lastClear);
$this->assertSame(1, $object->data->sourceId);
}
public function testEditRemoteDataSet()
{
$name = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
// add DataSet with wrapper
$dataSet = (new XiboDataSet($this->getEntityProvider()))->create($name, '', 'remote', 1, 'GET', 'http://localhost/resources/RemoteDataSet.json', '', '', '', '', 1, 0, null, 'data');
// Edit DataSet
$response = $this->sendRequest('PUT','/dataset/' . $dataSet->dataSetId, [
'dataSet' => $name2,
'code' => 'remote',
'isRemote' => 1,
'method' => 'GET',
'uri' => 'http://localhost/resources/RemoteDataSet.json',
'dataRoot' => 'data',
'clearRate' => 3600,
'refreshRate' => 1,
'sourceId' => 1,
'limitPolicy' => 'stop'
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check dataSet object
$this->assertSame($name2, $object->data->dataSet);
$this->assertSame(1, $object->data->isRemote);
$this->assertSame('http://localhost/resources/RemoteDataSet.json', $object->data->uri);
$this->assertSame(3600, $object->data->clearRate);
$this->assertSame(1, $object->data->refreshRate);
$this->assertSame(1, $object->data->sourceId);
}
}

View File

@@ -0,0 +1,223 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDaypart;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DaypartTest
* @package Xibo\Tests\Integration
*/
class DaypartTest extends LocalWebTestCase
{
/** @var XiboDaypart[] */
protected $startDayparts;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startDayparts = (new XiboDaypart($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->getLogger()->debug('There are ' . count($this->startDayparts) . ' dayparts at the start of the test');
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all dayparts that weren't there initially
$finalDayparts = (new XiboDaypart($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining dayparts and nuke them
foreach ($finalDayparts as $daypart) {
/** @var XiboDaypart $daypart */
$flag = true;
foreach ($this->startDayparts as $startDaypart) {
if ($startDaypart->dayPartId == $daypart->dayPartId) {
$flag = false;
}
}
if ($flag) {
try {
$daypart->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $daypart->dayPartId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* testAddSuccess - test adding various daypart that should be valid
* @dataProvider provideSuccessCases
*/
public function testAddSuccess($name, $description, $startTime, $endTime, $exceptionDays, $exceptionStartTimes, $exceptionEndTimes)
{
# Create daypart with arguments from provideSuccessCases
$response = $this->sendRequest('POST','/daypart', [
'name' => $name,
'description' => $description,
'startTime' => $startTime,
'endTime' => $endTime,
'exceptionDays' => $exceptionDays,
'exceptionStartTimes' => $exceptionStartTimes,
'exceptionEndTimes' => $exceptionEndTimes
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame($description, $object->data->description);
# Check that the daypart was really added
$dayparts = (new XiboDaypart($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->assertEquals(count($this->startDayparts) + 1, count($dayparts));
# Check that the daypart was added correctly
$daypart = (new XiboDaypart($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $daypart->name);
$this->assertSame($description, $daypart->description);
# Clean up the daypart as we no longer need it
$this->assertTrue($daypart->delete(), 'Unable to delete ' . $daypart->dayPartId);
}
/**
* testAddFailure - test adding various daypart that should be invalid
* @dataProvider provideFailureCases
*/
public function testAddFailure($name, $description, $startTime, $endTime, $exceptionDays, $exceptionStartTimes, $exceptionEndTimes)
{
# Create daypart with arguments from provideFailureCases
$response = $this->sendRequest('POST','/daypart', [
'name' => $name,
'description' => $description,
'startTime' => $startTime,
'endTime' => $endTime,
'exceptionDays' => $exceptionDays,
'exceptionStartTimes' => $exceptionStartTimes,
'exceptionEndTimes' => $exceptionEndTimes
]);
# check if they fail as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Each array is a test run
* Format ($name, $description, $startTime, $endTime, $exceptionDays, $exceptionStartTimes, $exceptionEndTimes)
* @return array
*/
public function provideSuccessCases()
{
# Data for testAddSuccess, easily expandable - just add another set of data below
return [
'No exceptions' => ['phpunit daypart', 'API', '02:00', '06:00', NULL, NULL, NULL],
'Except Monday' => ['phpunit daypart exception', NULL, '02:00', '06:00', ['Monday'], ['00:01'], ['23:59']]
];
}
/**
* Each array is a test run
* Format ($name, $description, $startTime, $endTime, $exceptionDays, $exceptionStartTimes, $exceptionEndTimes)
* @return array
*/
public function provideFailureCases()
{
# Data for testAddfailure, easily expandable - just add another set of data below
// TODO we should probably validate description and day names in daypart Controller.
return [
'Empty title' => [NULL, 'should be invalid', '07:00', '10:00', NULL, NULL, NULL],
//'Description over 254 characters' => ['Too long description', Random::generateString(258), '07:00', '10:00', NULL, NULL, NULL],
'Wrong time data type' => ['Time as integer','should be incorrect', 21, 22, NULL, NULL, NULL],
//'Wrong day name' => ['phpunit daypart exception', NULL, '02:00', '06:00', ['Cabbage'], ['00:01'], ['23:59']]
];
}
/**
* Edit an existing daypart
*/
public function testEdit()
{
#Create new daypart
$daypart = (new XiboDaypart($this->getEntityProvider()))->create('phpunit daypart', 'API', '02:00', '06:00', NULL, NULL, NULL);
# Change the daypart name and description
$name = Random::generateString(8, 'phpunit');
$description = Random::generateString(8, 'description');
$response = $this->sendRequest('PUT','/daypart/' . $daypart->dayPartId, [
'name' => $name,
'description' => $description,
'startTime' => '02:00',
'endTime' => '06:00',
'exceptionDays' => $daypart->exceptionDays,
'exceptionStartTimes' => $daypart->exceptionStartTimes,
'exceptionEndTimes' => $daypart->exceptionEndTimes
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame($description, $object->data->description);
# Check that the daypart was actually renamed
$daypart = (new XiboDaypart($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $daypart->name);
$this->assertSame($description, $daypart->description);
# Clean up the Daypart as we no longer need it
$daypart->delete();
}
/**
* Test delete
* @group minimal
*/
public function testDelete()
{
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known Dayparts
$daypart1 = (new XiboDaypart($this->getEntityProvider()))->create($name1, 'API', '02:00', '06:00', NULL, NULL, NULL);
$daypart2 = (new XiboDaypart($this->getEntityProvider()))->create($name2, 'API', '12:00', '16:00', NULL, NULL, NULL);
# Delete the one we created last
$response = $this->sendRequest('DELETE','/daypart/' . $daypart2->dayPartId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$dayparts = (new XiboDaypart($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->assertEquals(count($this->startDayparts) + 1, count($dayparts));
$flag = false;
foreach ($dayparts as $daypart) {
if ($daypart->dayPartId == $daypart1->dayPartId) {
$flag = true;
}
}
$this->assertTrue($flag, 'Daypart ID ' . $daypart1->dayPartId . ' was not found after deleting a different daypart');
$daypart1->delete();
}
}

View File

@@ -0,0 +1,138 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Tests copying a display group.
*/
class DisplayGroupCopyTest extends LocalWebTestCase
{
use DisplayHelperTrait;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboDisplayGroup */
protected $displayGroup;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for Cache ' . get_class($this) . ' Test');
// Create a couple of displays to use in the test
$this->display = $this->createDisplay();
$this->display2 = $this->createDisplay();
// Create a display group and assign both displays
$this->displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create(
'phpunit_' . bin2hex(random_bytes(4)),
'',
0,
null
);
// Assign our two displays
$this->displayGroup->assignDisplay($this->display->displayId);
$this->displayGroup->assignDisplay($this->display2->displayId);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Display
$this->deleteDisplay($this->display);
$this->deleteDisplay($this->display2);
$this->displayGroup->delete();
}
// </editor-fold>
public function testCopyPlain()
{
$response = $this->sendRequest('POST', '/displaygroup/' . $this->displayGroup->displayGroupId . '/copy', [
'displayGroup' => 'phpunit_' . bin2hex(random_bytes(4)),
'description' => 'copied',
'copyMembers' => 0,
'copyAssignments' => 0,
'copyTags' => 0,
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame('copied', $object->data->description);
// Check there aren't any displays assigned.
$results = $this->getStore()->select('SELECT COUNT(*) AS cnt FROM lkdisplaydg WHERE displayGroupId = :displayGroupId', [
'displayGroupId' => $object->id
]);
$this->assertEquals(0, intval($results[0]['cnt']));
(new XiboDisplayGroup($this->getEntityProvider()))->getById($object->id)->delete();
}
public function testCopyMembers()
{
$response = $this->sendRequest('POST', '/displaygroup/' . $this->displayGroup->displayGroupId . '/copy', [
'displayGroup' => 'phpunit_' . bin2hex(random_bytes(4)),
'description' => 'copied',
'copyMembers' => 1,
'copyAssignments' => 0,
'copyTags' => 0,
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame('copied', $object->data->description);
// Check there aren't any displays assigned.
$results = $this->getStore()->select('SELECT COUNT(*) AS cnt FROM lkdisplaydg WHERE displayGroupId = :displayGroupId', [
'displayGroupId' => $object->id
]);
$this->assertEquals(2, intval($results[0]['cnt']));
(new XiboDisplayGroup($this->getEntityProvider()))->getById($object->id)->delete();
}
}

View File

@@ -0,0 +1,899 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCommand;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class DisplayGroupTest
* @package Xibo\Tests\Integration
*/
class DisplayGroupTest extends LocalWebTestCase
{
use LayoutHelperTrait;
protected $startDisplayGroups;
protected $startDisplays;
protected $startLayouts;
protected $startCommands;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startDisplayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startDisplays = (new XiboDisplay($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startCommands = (new XiboCommand($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all display groups that weren't there initially
$finalDisplayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining display groups and nuke them
foreach ($finalDisplayGroups as $displayGroup) {
/** @var XiboDisplayGroup $displayGroup */
$flag = true;
foreach ($this->startDisplayGroups as $startGroup) {
if ($startGroup->displayGroupId == $displayGroup->displayGroupId) {
$flag = false;
}
}
if ($flag) {
try {
$displayGroup->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $displayGroup->displayGroupId . '. E:' . $e->getMessage());
}
}
}
// Tear down any displays that weren't there before
$finalDisplays = (new XiboDisplay($this->getEntityProvider()))->get();
# Loop over any remaining displays and nuke them
foreach ($finalDisplays as $display) {
/** @var XiboDisplay $display */
$flag = true;
foreach ($this->startDisplays as $startDisplay) {
if ($startDisplay->displayId == $display->displayId) {
$flag = false;
}
}
if ($flag) {
try {
$display->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $display->displayId . '. E:' . $e->getMessage());
}
}
}
// tearDown all layouts that weren't there initially
$finalLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining layouts and nuke them
foreach ($finalLayouts as $layout) {
/** @var XiboLayout $layout */
$flag = true;
foreach ($this->startLayouts as $startLayout) {
if ($startLayout->layoutId == $layout->layoutId) {
$flag = false;
}
}
if ($flag) {
try {
$layout->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $layout->layoutId . '. E:' . $e->getMessage());
}
}
}
// tearDown all commands that weren't there initially
$finalCommands = (new XiboCommand($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining commands and nuke them
foreach ($finalCommands as $command) {
/** @var XiboCommand $command */
$flag = true;
foreach ($this->startCommands as $startCom) {
if ($startCom->commandId == $command->commandId) {
$flag = false;
}
}
if ($flag) {
try {
$command->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $command->commandId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* List all display groups known empty
* @group minimal
* @group destructive
*/
public function testListEmpty()
{
if (count($this->startDisplayGroups) > 0) {
$this->skipTest("There are pre-existing DisplayGroups");
return;
}
$response = $this->sendRequest('GET','/displaygroup');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be no DisplayGroups in the system
$this->assertEquals(0, $object->data->recordsTotal);
}
/**
* testAddSuccess - test adding various Display Groups that should be valid
* @dataProvider provideSuccessCases
* @group minimal
*/
public function testAddSuccess($groupName, $groupDescription, $isDynamic, $expectedDynamic, $dynamicCriteria, $expectedDynamicCriteria)
{
// Loop through any pre-existing DisplayGroups to make sure we're not
// going to get a clash
foreach ($this->startDisplayGroups as $tmpGroup) {
if ($tmpGroup->displayGroup == $groupName) {
$this->skipTest("There is a pre-existing DisplayGroup with this name");
return;
}
}
$response = $this->sendRequest('POST','/displaygroup', [
'displayGroup' => $groupName,
'description' => $groupDescription,
'isDynamic' => $isDynamic,
'dynamicCriteria' => $dynamicCriteria
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($groupName, $object->data->displayGroup);
$this->assertSame($groupDescription, $object->data->description);
$this->assertSame($expectedDynamic, $object->data->isDynamic);
$this->assertSame($expectedDynamicCriteria, $object->data->dynamicCriteria);
# Check that the group was really added
$displayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['length' => 1000]);
$this->assertEquals(count($this->startDisplayGroups) + 1, count($displayGroups));
# Check that the group was added correctly
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->getById($object->id);
$this->assertSame($groupName, $displayGroup->displayGroup);
$this->assertSame($groupDescription, $displayGroup->description);
$this->assertSame($expectedDynamic, $displayGroup->isDynamic);
$this->assertSame($expectedDynamicCriteria, $displayGroup->dynamicCriteria);
# Clean up the DisplayGroup as we no longer need it
$this->assertTrue($displayGroup->delete(), 'Unable to delete ' . $displayGroup->displayGroupId);
}
/**
* testAddFailure - test adding various Display Groups that should be invalid
* @dataProvider provideFailureCases
* @group minimal
*/
public function testAddFailure($groupName, $groupDescription, $isDynamic, $dynamicCriteria)
{
$response = $this->sendRequest('POST','/displaygroup', [
'displayGroup' => $groupName,
'description' => $groupDescription,
'isDynamic' => $isDynamic,
'dynamicCriteria' => $dynamicCriteria
]);
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* List all display groups known set
* @group minimal
* @depends testAddSuccess
*/
public function testListKnown()
{
# Load in a known set of display groups
# We can assume this works since we depend upon the test which
# has previously added and removed these without issue:
$cases = $this->provideSuccessCases();
$displayGroups = [];
// Check each possible case to ensure it's not pre-existing
// If it is, skip over it
foreach ($cases as $case) {
$flag = true;
foreach ($this->startDisplayGroups as $tmpGroup) {
if ($case[0] == $tmpGroup->displayGroup) {
$flag = false;
}
}
if ($flag) {
$displayGroups[] = (new XiboDisplayGroup($this->getEntityProvider()))->create($case[0],$case[1],$case[2],$case[3]);
}
}
$response = $this->sendRequest('GET','/displaygroup');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be as many groups as we created plus the number we started with in the system
$this->assertEquals(count($displayGroups) + count($this->startDisplayGroups), $object->data->recordsTotal);
# Clean up the groups we created
foreach ($displayGroups as $group) {
$group->delete();
}
}
/**
* List specific display groups
* @group minimal
* @group destructive
* @depends testListKnown
* @depends testAddSuccess
* @dataProvider provideSuccessCases
*/
public function testListFilter($groupName, $groupDescription, $isDynamic, $expectedDynamic, $dynamicCriteria, $expectedDynamicCriteria)
{
if (count($this->startDisplayGroups) > 0) {
$this->skipTest("There are pre-existing DisplayGroups");
return;
}
# Load in a known set of display groups
# We can assume this works since we depend upon the test which
# has previously added and removed these without issue:
$cases = $this->provideSuccessCases();
$displayGroups = [];
foreach ($cases as $case) {
$displayGroups[] = (new XiboDisplayGroup($this->getEntityProvider()))->create($case[0], $case[1], $case[2], $case[3]);
}
$response = $this->sendRequest('GET','/displaygroup', [
'displayGroup' => $groupName
]);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be at least one match
$this->assertGreaterThanOrEqual(1, $object->data->recordsTotal);
$flag = false;
# Check that for the records returned, $groupName is in the groups names
foreach ($object->data->data as $group) {
if (strpos($groupName, $group->displayGroup) == 0) {
$flag = true;
}
else {
// The object we got wasn't the exact one we searched for
// Make sure all the words we searched for are in the result
foreach (array_map('trim',explode(",",$groupName)) as $word) {
assertTrue((strpos($word, $group->displayGroup) !== false), 'Group returned did not match the query string: ' . $group->displayGroup);
}
}
}
$this->assertTrue($flag, 'Search term not found');
foreach ($displayGroups as $group) {
$group->delete();
}
}
/**
* Each array is a test run
* Format
* (Group Name, Group Description, isDynamic, Returned isDynamic (0 or 1),
* Criteria for Dynamic group, Returned Criteria for Dynamic group)
* For example, if you set isDynamic to 0 and send criteria, it will come back
* with criteria = null
* These are reused in other tests so please ensure Group Name is unique
* through the dataset
* @return array
*/
public function provideSuccessCases()
{
return [
// Multi-language non-dynamic groups
'English 1' => ['phpunit test group', 'Api', 0, 0, null, null],
'English 2' => ['another phpunit test group', 'Api', 0, 0, null, null],
'French 1' => ['Test de Français 1', 'Bienvenue à la suite de tests Xibo', 0, 0, null, null],
'German 1' => ['Deutsch Prüfung 1', 'Weiß mit schwarzem Text', 0, 0, null, null],
'Simplified Chinese 1' => ['试验组', '测试组描述', 0, 0, null, null],
// Multi-language dynamic groups
'English Dynamic 1' => ['phpunit test dynamic group', 'Api', 1, 1, 'test', 'test'],
'French Dynamic 1' => ['Test de Français 2', 'Bienvenue à la suite de tests Xibo', 1, 1, 'test', 'test'],
'German Dynamic 1' => ['Deutsch Prüfung 2', 'Weiß mit schwarzem Text', 1, 1, 'test', 'test'],
// Tests for the various allowed values for isDynamic = 1
'isDynamic on' => ['phpunit group dynamic is on', 'Api', 'on', 1, 'test', 'test'],
'isDynamic true' => ['phpunit group dynamic is true', 'Api', 'true', 1, 'test', 'test'],
// Invalid isDynamic flag (the CMS sanitises these for us to false)
'isDynamic is 7 null criteria' => ['Invalid isDynamic flag 1', 'Invalid isDynamic flag', 7, 0, null, null],
'isDynamic is 7 with criteria' => ['Invalid isDynamic flag 2 ', 'Invalid isDynamic flag', 7, 0, 'criteria', 'criteria'],
'isDynamic is invalid null criteria' => ['Invalid isDynamic flag alpha 1', 'Invalid isDynamic flag alpha', 'invalid', 0, null, null],
'isDynamic is invalid with criteria' => ['Invalid isDynamic flag alpha 2', 'Invalid isDynamic flag alpha', 'invalid', 0, 'criteria', 'criteria']
];
}
/**
* Each array is a test run
* Format
* (Group Name, Group Description, isDynamic, Criteria for Dynamic group)
* @return array
*/
public function provideFailureCases()
{
return [
// Description is limited to 255 characters
'Description over 254 characters' => ['Too long description', Random::generateString(255), 0, null],
// If isDynamic = 1 then criteria must be set
'No dynamicCriteria on dynamic group' => ['No dynamic criteria', 'No dynamic criteria', 1, null],
// Missing group names
'Group name empty' => ['', 'Group name is empty', 0, null],
'Group name null' => [null, 'Group name is null', 0, null]
];
}
/**
* Try and add two display groups with the same name
* @group minimal
* @depends testAddSuccess
*/
public function testAddDuplicate()
{
$flag = true;
foreach ($this->startDisplayGroups as $group) {
if ($group->displayGroup == 'phpunit displaygroup') {
$flag = false;
}
}
# Load in a known display group if it's not there already
if ($flag) {
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit displaygroup', 'phpunit displaygroup', 0, '');
}
$response = $this->sendRequest('POST','/displaygroup', [
'displayGroup' => 'phpunit displaygroup',
'description' => 'phpunit displaygroup',
'isDynamic' => 0,
'dynamicCriteria' => ''
]);
$this->assertSame(409, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
$displayGroup->delete();
}
/**
* Edit an existing display group
* @depends testAddSuccess
* @group minimal
*/
public function testEdit()
{
foreach ($this->startDisplayGroups as $group) {
if ($group->displayGroup == 'phpunit displaygroup') {
$this->skipTest('displayGroup already exists with that name');
return;
}
}
# Load in a known display group
/** @var XiboDisplayGroup $displayGroup */
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit displaygroup', 'phpunit displaygroup', 0, '');
# Change the group name and description
# Change it to a dynamic group with a fixed criteria
$name = Random::generateString(8, 'phpunit');
$description = Random::generateString(8, 'description');
$criteria = 'test';
$response = $this->sendRequest('PUT','/displaygroup/' . $displayGroup->displayGroupId, [
'displayGroup' => $name,
'description' => $description,
'isDynamic' => 1,
'dynamicCriteria' => $criteria
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->displayGroup);
$this->assertSame($description, $object->data->description);
$this->assertSame(1, $object->data->isDynamic);
$this->assertSame($criteria, $object->data->dynamicCriteria);
# Check that the group was actually renamed
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $displayGroup->displayGroup);
$this->assertSame($description, $displayGroup->description);
$this->assertSame(1, $displayGroup->isDynamic);
$this->assertSame($criteria, $displayGroup->dynamicCriteria);
# Clean up the DisplayGroup as we no longer need it
$displayGroup->delete();
}
/**
* Test delete
* @depends testAddSuccess
* @group minimal
*/
public function testDelete()
{
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known display groups
$displayGroup1 = (new XiboDisplayGroup($this->getEntityProvider()))->create($name1, 'phpunit description', 0, '');
$displayGroup2 = (new XiboDisplayGroup($this->getEntityProvider()))->create($name2, 'phpunit description', 0, '');
# Delete the one we created last
$response = $this->sendRequest('DELETE','/displaygroup/' . $displayGroup2->displayGroupId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$groups = (new XiboDisplayGroup($this->getEntityProvider()))->get();
$this->assertEquals(count($this->startDisplayGroups) + 1, count($groups));
$flag = false;
foreach ($groups as $group) {
if ($group->displayGroupId == $displayGroup1->displayGroupId) {
$flag = true;
}
}
$this->assertTrue($flag, 'DisplayGroup ID ' . $displayGroup1->displayGroupId . ' was not found after deleting a different DisplayGroup');
# Clean up
$displayGroup1->delete();
}
/**
* Assign new displays Test
*/
public function testAssignDisplay()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get();
$display = null;
foreach ($displays as $disp) {
if ($disp->license == $hardwareId) {
$display = $disp;
}
}
if ($display === null) {
$this->fail('Display was not added correctly');
}
# Create a DisplayGroup to add the display to
$name = Random::generateString(8, 'phpunit');
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Call assign display to display group
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/display/assign', [
'displayId' => [$display->displayId]
]);
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Get a list of all Displays in the group
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['displayGroupId' => $displayGroup->displayGroupId]);
# Check that there's only us in that group
$this->assertEquals(1, count($displays));
$this->assertEquals($display->displayId, $displays[0]->displayId);
# Clean up
$displayGroup->delete();
$display->delete();
}
/**
* Try to assign display to isDisplaySpecific displayGroupId
*/
public function testAssignDisplayFailure()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get();
$display = null;
foreach ($displays as $disp) {
if ($disp->license == $hardwareId) {
$display = $disp;
}
}
if ($display === null) {
$this->fail('Display was not added correctly');
}
# Call assign display to display specific display group
$response = $this->sendRequest('POST','/displaygroup/' . $display->displayGroupId . '/display/assign', [
'displayId' => [$display->displayId]
]);
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Unassign displays Test
*/
public function testUnassignDisplay()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get();
$display = null;
foreach ($displays as $disp) {
if ($disp->license == $hardwareId) {
$display = $disp;
}
}
if ($display === null) {
$this->fail('Display was not added correctly');
}
# Create display group
$name = Random::generateString(8, 'phpunit');
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Assign display to display group
$displayGroup->assignDisplay([$display->displayId]);
# Unassign display from display group
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/display/unassign', [
'displayId' => [$display->displayId]
]);
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Clean up
$displayGroup->delete();
$display->delete();
}
/**
* Try to unassign display from isDisplaySpecific displayGroupId
*/
public function testUnassignDisplayFailure()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get();
$display = null;
foreach ($displays as $disp) {
if ($disp->license == $hardwareId) {
$display = $disp;
}
}
if ($display === null) {
$this->fail('Display was not added correctly');
}
# Create display group
$name = Random::generateString(8, 'phpunit');
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Assign display to display group - should be successful
$displayGroup->assignDisplay([$display->displayId]);
# Unassign display from isDisplaySpecific display group - should fail
$response = $this->sendRequest('POST','/displaygroup/' . $display->displayGroupId . '/display/unassign', [
'displayId' => [$display->displayId]
]);
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Assign new display group Test
*/
public function testAssignGroup()
{
# Generate new random names
$name = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
$displayGroup2 = (new XiboDisplayGroup($this->getEntityProvider()))->create($name2, 'phpunit description', 0, '');
# Assign second display group to the first one
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/displayGroup/assign', [
'displayGroupId' => [$displayGroup2->displayGroupId]
]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$displayGroup2->delete();
}
/**
* Unassign displays group Test
*/
public function testUnassignGroup()
{
# Generate new random names
$name = Random::generateString(8, 'PARENT');
$name2 = Random::generateString(8, 'CHILD');
# Create new display groups
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
$displayGroup2 = (new XiboDisplayGroup($this->getEntityProvider()))->create($name2, 'phpunit description', 0, '');
# Assign second display group to the first one
$displayGroup->assignDisplayGroup([$displayGroup2->displayGroupId]);
# Unassign second display group from the first one
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/displayGroup/unassign', [
'displayGroupId' => [$displayGroup2->displayGroupId]
]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$displayGroup2->delete();
}
/**
* Assign new media file to a group Test
*/
public function testAssignMedia()
{
# Generate new random name
$name = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Upload a known files
$media = (new XiboLibrary($this->getEntityProvider()))->create('API video 12', PROJECT_ROOT . '/tests/resources/HLH264.mp4');
$media2 = (new XiboLibrary($this->getEntityProvider()))->create('API image 12', PROJECT_ROOT . '/tests/resources/xts-night-001.jpg');
# Assign two files o the display group and unassign one of them
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/media/assign', [
'mediaId' => [$media->mediaId, $media2->mediaId],
'unassignMediaId' => [$media2->mediaId]
]);
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$media->delete();
$media2->delete();
}
/**
* Unassign media files from a group Test
*/
public function testUnassignMedia()
{
# Generate new random name
$name = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Upload a known file
$media = (new XiboLibrary($this->getEntityProvider()))->create('API image 29', PROJECT_ROOT . '/tests/resources/xts-night-001.jpg');
# Assign media to display Group
$displayGroup->assignMedia([$media->mediaId]);
# Unassign the media from the display group
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/media/unassign', [
'mediaId' => [$media->mediaId]
]);
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$media->delete();
}
/**
* Assign new layouts to a group Test
*/
public function testAssignLayout()
{
# Create new random name
$name = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Create new layouts
$layout = $this->createLayout();
$layout2 = $this->createLayout();
# Assign both layouts to display group then unassign the second layout from it
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/layout/assign', [
'layoutId' => [$layout->layoutId, $layout2->layoutId],
'unassignLayoutsId' => [$layout2->layoutId]
]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$layout->delete();
$layout2->delete();
}
/**
* Unassign layouts from a group Test
*/
public function testUnassignLayout()
{
# Create new random name
$name = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Create new layout
$layout = $this->createLayout();
# assign layout to display group
$displayGroup->assignLayout([$layout->layoutId]);
# unassign layout from display group
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/layout/unassign', [
'layoutId' => [$layout->layoutId]
]);
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$layout->delete();
}
/**
* Collect now action test
*/
public function testCollectNow()
{
# Generate random name
$name = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Call callectNow
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/action/collectNow');
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
}
/**
* Change Layout action test
*/
public function testChangeLayout()
{
# Generate random name
$name = Random::generateString(8, 'phpunit');
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Create new layout
$layout = $this->createLayout();
# Call changeLayout
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/action/changeLayout', [
'layoutId' => $layout->layoutId,
'duration' => 900,
'downloadRequired' => 1,
'changeMode' => 'queue'
]);
# Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$layout->delete();
}
/**
* Revert to Schedule action test
*/
public function testRevertToSchedule()
{
# Generate random name and create new display group
$name = Random::generateString(8, 'phpunit');
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Call RevertToSchedule
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/action/revertToSchedule');
# Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
}
/**
* Send command action test
*/
public function testCommand()
{
# Generate random name and create new display group
$name = Random::generateString(8, 'phpunit');
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create($name, 'phpunit description', 0, '');
# Create new command
$command = (new XiboCommand($this->getEntityProvider()))->create('phpunit command', 'phpunit description', 'phpunitcode');
# Send command to display group
$response = $this->sendRequest('POST','/displaygroup/' . $displayGroup->displayGroupId . '/action/command' , [
'commandId' => $command->commandId
]);
# Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Clean up
$displayGroup->delete();
$command->delete();
}
}

View File

@@ -0,0 +1,171 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\OAuth2\Client\Entity\XiboDisplayProfile;
/**
* Class DisplayProfileTest
* @package Xibo\Tests\Integration
*/
class DisplayProfileTest extends \Xibo\Tests\LocalWebTestCase
{
protected $startProfiles;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startProfiles = (new XiboDisplayProfile($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all profiles that weren't there initially
$finalProfiles = (new XiboDisplayProfile($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
// Loop over any remaining profiles and nuke them
foreach ($finalProfiles as $displayProfile) {
/** @var XiboDisplayProfile $displayProfile */
$flag = true;
foreach ($this->startProfiles as $startProfile) {
if ($startProfile->displayProfileId == $displayProfile->displayProfileId) {
$flag = false;
}
}
if ($flag) {
try {
$displayProfile->delete();
} catch (\Exception $e) {
$this->getLogger()->error('Unable to delete ' . $displayProfile->displayProfileId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* Shows all display profiles
*/
public function testListAll()
{
# Get list of all display profiles
$response = $this->sendRequest('GET','/displayprofile');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* testAddSuccess - test adding various display profiles that should be valid
* @dataProvider provideSuccessCases
* @group minimal
*/
public function testAddSuccess($profileName, $profileType, $profileIsDefault)
{
// Loop through any pre-existing profiles to make sure we're not
// going to get a clash
foreach ($this->startProfiles as $tmpProfile) {
if ($tmpProfile->name == $profileName) {
$this->skipTest("There is a pre-existing profiles with this name");
return;
}
}
$response = $this->sendRequest('POST','/displayprofile', [
'name' => $profileName,
'type' => $profileType,
'isDefault' => $profileIsDefault
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($profileName, $object->data->name);
$this->assertSame($profileType, $object->data->type);
$this->assertSame($profileIsDefault, $object->data->isDefault);
# Check that the profile was added correctly
$profile = (new XiboDisplayProfile($this->getEntityProvider()))->getById($object->id);
$this->assertSame($profileName, $profile->name);
$this->assertSame($profileType, $profile->type);
# Clean up the Profiles as we no longer need it
$this->assertTrue($profile->delete(), 'Unable to delete ' . $profile->displayProfileId);
}
/**
* Each array is a test run
* Format (profile name, type(windows/android), isDefault flag)
* @return array
*/
public function provideSuccessCases()
{
// Cases we provide to testAddSuccess, you can extend it by simply adding new case here
return [
'Android notDefault' => ['test profile', 'android', 0],
'Windows notDefault' => ['different test profile', 'windows', 0],
'French Android' => ['Test de Français 1', 'android', 0],
'Linux' => ['Test de Français 1', 'linux', 0],
'Tizen' => ['Test de Français 1', 'sssp', 0],
'webOS' => ['Test de Français 1', 'lg', 0]
];
}
/**
* testAddFailure - test adding various profiles that should be invalid
* @dataProvider provideFailureCases
*/
public function testAddFailure($profileName, $profileType, $profileIsDefault)
{
# Add new display profile with arguments from provideFailureCases
$response = $this->sendRequest('POST','/displayprofile', [
'name' => $profileName,
'type' => $profileType,
'isDefault' => $profileIsDefault
]);
# Check if it fails as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Each array is a test run
* Format (profile name, type(windows/android), isDefault flag)
* @return array
*/
public function provideFailureCases()
{
# Cases we provide to testAddFailure, you can extend it by simply adding new case here
return [
'NULL Type' => ['no type', NULL, 0],
'NULL name' => [NULL, 'android', 1],
'is Default 1' => ['TEST PHP', 'android', 1]
];
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Copyright (C) 2019 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplayProfile;
use Xibo\OAuth2\Client\Exception\XiboApiException;
/**
* Class DisplayProfileTest
* @package Xibo\Tests\Integration
*/
class DisplayProfileTestDelete extends \Xibo\Tests\LocalWebTestCase
{
/** @var XiboDisplayProfile */
private $displayProfile;
public function setup()
{
parent::setup();
$this->displayProfile = (new XiboDisplayProfile($this->getEntityProvider()))->create(Random::generateString(), 'android', 0);
}
protected function tearDown()
{
if ($this->displayProfile !== null) {
$this->displayProfile->delete();
}
parent::tearDown();
}
/**
* Test delete
*/
public function testDelete()
{
// Delete the one we created last
$response = $this->sendRequest('DELETE','/displayprofile/' . $this->displayProfile->displayProfileId);
// This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
// Check only one remains
try {
$displayProfile = (new XiboDisplayProfile($this->getEntityProvider()))->getById($this->displayProfile->displayProfileId);
$this->fail('Display profile ID ' . $this->displayProfile->displayProfileId . ' was not found after deleting a different Display Profile');
} catch (XiboApiException $exception) {
// We know we've deleted it, so no clear for tearDown
$this->displayProfile = null;
}
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplayProfile;
/**
* Class DisplayProfileTest
* @package Xibo\Tests\Integration
*/
class DisplayProfileTestEdit extends \Xibo\Tests\LocalWebTestCase
{
/** @var XiboDisplayProfile */
private $displayProfile;
public function setup()
{
parent::setup();
$this->displayProfile = (new XiboDisplayProfile($this->getEntityProvider()))->create(Random::generateString(), 'android', 0);
}
protected function tearDown()
{
$this->displayProfile->delete();
parent::tearDown();
}
/**
* Edit an existing profile
*/
public function testEdit()
{
// Call edit on the profile.
$name = Random::generateString(8, 'phpunit');
$response = $this->sendRequest('PUT','/displayprofile/' . $this->displayProfile->displayProfileId, [
'name' => $name,
'type' => $this->displayProfile->type,
'isDefault' => $this->displayProfile->isDefault
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
// Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame('android', $object->data->type);
// Check that the profile was actually renamed
$displayProfile = (new XiboDisplayProfile($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $displayProfile->name);
}
/**
* Edit an existing profile
*/
public function testEditConfig()
{
// Call edit on the profile.
$name = Random::generateString(8, 'phpunit');
$response = $this->sendRequest('PUT','/displayprofile/' . $this->displayProfile->displayProfileId, [
'name' => $name,
'type' => $this->displayProfile->type,
'isDefault' => $this->displayProfile->isDefault,
'emailAddress' => 'phpunit@xibo.org.uk'
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getStatusCode());
$object = json_decode($response->getBody());
// Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame('android', $object->data->type);
foreach ($object->data->config as $config) {
if ($config->name === 'emailAddress') {
$this->assertSame('phpunit@xibo.org.uk', $config->value, json_encode($object->data->config));
}
}
// Check that the profile was actually renamed
$displayProfile = (new XiboDisplayProfile($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $displayProfile->name);
}
}

View File

@@ -0,0 +1,293 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
class DisplayTest extends \Xibo\Tests\LocalWebTestCase
{
protected $startDisplays;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startDisplays = (new XiboDisplay($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Tear down any displays that weren't there before
$finalDisplays = (new XiboDisplay($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining displays and nuke them
foreach ($finalDisplays as $display) {
/** @var XiboDisplay $display */
$flag = true;
foreach ($this->startDisplays as $startDisplay) {
if ($startDisplay->displayId == $display->displayId) {
$flag = false;
}
}
if ($flag) {
try {
$display->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $display->displayId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* Shows list of all displays Test
*/
public function testListAll()
{
# Get all displays
$response = $this->sendRequest('GET','/display');
# Check if successful
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* Delete Display Test
*/
public function testDelete()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1)
$this->fail('Display was not added correctly');
/** @var XiboDisplay $display */
$display = $displays[0];
$response = $this->sendRequest('DELETE','/display/' . $display->displayId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
}
/**
* Edit Display test, expecting success
*/
public function testEditSuccess()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1) {
$this->fail('Display was not added correctly');
}
/** @var XiboDisplay $display */
$display = $displays[0];
$auditingTime = time()+3600;
# Edit display and change its name
$response = $this->sendRequest('PUT','/display/' . $display->displayId, [
'display' => 'API EDITED',
'defaultLayoutId' => $display->defaultLayoutId,
'auditingUntil' => Carbon::createFromTimestamp($auditingTime)->format(DateFormatHelper::getSystemFormat()),
'licensed' => $display->licensed,
'license' => $display->license,
'incSchedule' => $display->incSchedule,
'emailAlert' => $display->emailAlert,
'wakeOnLanEnabled' => $display->wakeOnLanEnabled,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Check if display has new edited name
$this->assertSame('API EDITED', $object->data->display);
}
/**
* Edit Display Type and reference, expecting success
*/
public function testEditDisplayType()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display Type');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1) {
$this->fail('Display was not added correctly');
}
/** @var XiboDisplay $display */
$display = $displays[0];
$auditingTime = time()+3600;
# Edit display and change its name
$response = $this->sendRequest('PUT', '/display/' . $display->displayId, [
'display' => 'PHPUnit Test Display Type - EDITED',
'defaultLayoutId' => $display->defaultLayoutId,
'auditingUntil' => Carbon::createFromTimestamp($auditingTime)->format(DateFormatHelper::getSystemFormat()),
'licensed' => $display->licensed,
'license' => $display->license,
'incSchedule' => $display->incSchedule,
'emailAlert' => $display->emailAlert,
'wakeOnLanEnabled' => $display->wakeOnLanEnabled,
'displayTypeId' => 1,
'ref1' => 'Lorem ipsum',
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Check if display has new edited name
$this->assertSame(1, $object->data->displayTypeId);
$this->assertSame('Lorem ipsum', $object->data->ref1);
}
/**
* Edit Display test, expecting failure
*/
public function testEditFailure()
{
# Create a Display in the system
$hardwareId = Random::generateString(12, 'phpunit');
$this->getXmdsWrapper()->RegisterDisplay($hardwareId, 'PHPUnit Test Display');
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1)
$this->fail('Display was not added correctly');
/** @var XiboDisplay $display */
$display = $displays[0];
# Edit display and change its hardwareKey
$response = $this->sendRequest('PUT','/display/' . $display->displayId, [
'display' => 'API EDITED',
'defaultLayoutId' => $display->defaultLayoutId,
'licensed' => $display->licensed,
'license' => null,
'incSchedule' => $display->incSchedule,
'emailAlert' => $display->emailAlert,
'wakeOnLanEnabled' => $display->wakeOnLanEnabled,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if call failed as expected (license cannot be null)
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getBody());
}
/**
* Request screenshot Test
*/
public function testScreenshot()
{
# Generate names for display and xmr channel
$hardwareId = Random::generateString(12, 'phpunit');
$xmrChannel = Random::generateString(50);
# This is a dummy pubKey and isn't used by anything important
$xmrPubkey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmdnXL4gGg3yJfmqVkU1xsGSQI
3b6YaeAKtWuuknIF1XAHAHtl3vNhQN+SmqcNPOydhK38OOfrdb09gX7OxyDh4+JZ
inxW8YFkqU0zTqWaD+WcOM68wTQ9FCOEqIrbwWxLQzdjSS1euizKy+2GcFXRKoGM
pbBhRgkIdydXoZZdjQIDAQAB
-----END PUBLIC KEY-----';
# Register our display
$this->getXmdsWrapper()->RegisterDisplay($hardwareId,
'PHPUnit Test Display',
'windows',
null,
null,
null,
'00:16:D9:C9:AL:69',
$xmrChannel,
$xmrPubkey
);
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1)
$this->fail('Display was not added correctly');
/** @var XiboDisplay $display */
$display = $displays[0];
# Check if xmr channel and pubkey were registered correctly
$this->assertSame($xmrChannel, $display->xmrChannel, 'XMR Channel not set correctly by XMDS Register Display');
$this->assertSame($xmrPubkey, $display->xmrPubKey, 'XMR PubKey not set correctly by XMDS Register Display');
# Call request screenshot
$response = $this->sendRequest('PUT','/display/requestscreenshot/' . $display->displayId);
# Check if successful
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
}
/**
* Wake On Lan Test
*/
public function testWoL()
{
# Create dummy hardware key and mac address
$hardwareId = Random::generateString(12, 'phpunit');
$macAddress = '00-16-D9-C9-AE-69';
# Register our display
$this->getXmdsWrapper()->RegisterDisplay($hardwareId,
'PHPUnit Test Display',
'windows',
null,
null,
null,
$macAddress,
Random::generateString(50),
Random::generateString(50)
);
# Now find the Id of that Display
$displays = (new XiboDisplay($this->getEntityProvider()))->get(['hardwareKey' => $hardwareId]);
if (count($displays) != 1)
$this->fail('Display was not added correctly');
/** @var XiboDisplay $display */
$display = $displays[0];
# Check if mac address was added correctly
$this->assertSame($macAddress, $display->macAddress, 'Mac Address not set correctly by XMDS Register Display');
$auditingTime = time()+3600;
# Edit display and add broadcast channel
$display->edit(
$display->display,
$display->description,
$display->tags,
Carbon::createFromTimestamp($auditingTime)->format(DateFormatHelper::getSystemFormat()),
$display->defaultLayoutId,
$display->licensed,
$display->license,
$display->incSchedule,
$display->emailAlert,
$display->alertTimeout,
$display->wakeOnLanEnabled,
null,
'127.0.0.1');
# Call WOL
$response = $this->sendRequest('POST','/display/wol/' . $display->displayId);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
/**
* Class FaultTest
* @package Xibo\Tests\Integration
*/
class FaultTest extends \Xibo\Tests\LocalWebTestCase
{
/**
* Collect data
* This test modifies headers and we therefore need to run in a separate process
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testCollect()
{
$response = $this->sendRequest('GET','/fault/collect', ['outputLog' => 'on']);
$this->assertSame(200, $response->getStatusCode());
}
/**
* test turning debug on
*/
public function testDebugOn()
{
$response = $this->sendRequest('PUT','/fault/debug/on');
$this->assertSame(200, $response->getStatusCode());
}
/**
* test turning debug off
*/
public function testDebugOff()
{
$response = $this->sendRequest('PUT','/fault/debug/off');
$this->assertSame(200, $response->getStatusCode());
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* Copyright (C) 2022 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Entity\Display;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\LocalWebTestCase;
class FontTest extends LocalWebTestCase
{
use DisplayHelperTrait;
private $testFileName = 'PHPUNIT FONT TEST';
private $testFilePath = PROJECT_ROOT . '/tests/resources/UglyTypist.ttf';
protected $startFonts;
// TODO create api wrapper for fonts :)
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startFonts = $this->getEntityProvider()->get('/fonts', ['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all media files that weren't there initially
$finalFonts = $this->getEntityProvider()->get('/fonts', ['start' => 0, 'length' => 10000]);
# Loop over any remaining font files and nuke them
foreach ($finalFonts as $font) {
$flag = true;
foreach ($this->startFonts as $startFont) {
if ($startFont['id'] == $font['id']) {
$flag = false;
}
}
if ($flag) {
try {
$this->getEntityProvider()->delete('/fonts/'.$font['id'].'/delete');
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete font ' . $font['id'] . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* List all file in library
*/
public function testListAll()
{
# Get all library items
$response = $this->sendRequest('GET', '/fonts');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
// we expect fonts distributed with CMS to be there.
$this->assertNotEmpty($object->data->data);
}
public function testUpload()
{
$uploadResponse = $this->uploadFontFile();
$uploadedFileObject = $uploadResponse['files'][0];
$this->assertNotEmpty($uploadedFileObject);
$this->assertSame(filesize($this->testFilePath), $uploadedFileObject['size']);
$this->assertSame(basename($this->testFilePath), $uploadedFileObject['fileName']);
$this->getLogger()->debug(
'Uploaded font ' . $uploadedFileObject['name'] .
' with ID ' . $uploadedFileObject['id'] .
' Stored as ' . $uploadedFileObject['fileName']
);
$fontRecord = $this->getEntityProvider()->get('/fonts', ['name' => $this->testFileName])[0];
$this->assertNotEmpty($fontRecord);
$this->assertSame(filesize($this->testFilePath), $fontRecord['size']);
$this->assertSame(basename($this->testFilePath), $fontRecord['fileName']);
}
public function testDelete()
{
$upload = $this->uploadFontFile();
$response = $this->sendRequest('DELETE', '/fonts/' . $upload['files'][0]['id']. '/delete');
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
}
public function testFontDependencies()
{
$upload = $this->uploadFontFile();
$size = $upload['files'][0]['size'];
$md5 = $upload['files'][0]['md5'];
// Create a Display
$display = $this->createDisplay();
$this->displaySetStatus($display, Display::$STATUS_DONE);
$this->displaySetLicensed($display);
// Call Required Files
$rf = $this->getXmdsWrapper()->RequiredFiles($display->license);
$this->assertContains('file download="http" size="'.$size.'" md5="'.$md5.'" saveAs="'.basename($this->testFilePath).'" type="dependency" fileType="font" ', $rf, 'Font not in Required Files');
// Delete the Display
$this->deleteDisplay($display);
}
public function testFontCss()
{
$fontCssPath = PROJECT_ROOT . '/library/fonts/fonts.css';
// upload file, this should also update fonts.css file
$this->uploadFontFile();
// read css file
$fontsCss = file_get_contents($fontCssPath);
// get the record
$fontRecord = $this->getEntityProvider()->get('/fonts', ['name' => $this->testFileName])[0];
// check if the uploaded font was added to player fonts.css file.
$this->assertContains('font-family: \''.$fontRecord['familyName'].'\';', $fontsCss, 'Font not in fonts.css');
$this->assertContains('src: url(\''.basename($this->testFilePath).'\');', $fontsCss, 'Font not in fonts.css');
}
private function uploadFontFile()
{
$payload = [
[
'name' => 'name',
'contents' => $this->testFileName
],
[
'name' => 'files',
'contents' => fopen($this->testFilePath, 'r')
]
];
return $this->getEntityProvider()->post('/fonts', ['multipart' => $payload]);
}
}

View File

@@ -0,0 +1,434 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboText;
use Xibo\Tests\Helper\LayoutHelperTrait;
/**
* Class AboutTest
* @package Xibo\Tests\Integration
*/
class InteractiveFeaturesTest extends \Xibo\Tests\LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout */
protected $layout;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this));
// Create a Layout
$this->layout = $this->createLayout();
// Get Draft
$layout = $this->getDraft($this->layout);
$this->addSimpleTextWidget($layout);
$this->layout = $this->publish($this->layout);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
parent::tearDown();
}
// </editor-fold>
/**
* Test Add Region Drawer
*/
public function testAddDrawer()
{
$layout = $this->checkout($this->layout);
// add Drawer Region
$response = $this->sendRequest('POST', '/region/drawer/' . $layout->layoutId);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(201, $body->status);
$this->assertSame(true, $body->success);
$this->assertSame(false, $body->grid);
$this->assertNotEmpty($body->data, 'Empty Data');
$this->assertSame(1, $body->data->isDrawer);
$this->assertContains('drawer', $body->data->name);
// get the layout
$layout = $this->getEntityProvider()->get('/layout', ['layoutId' => $layout->layoutId, 'embed' => 'regions'])[0];
// check if regions and drawers arrays are not empty
$this->assertNotEmpty($layout['drawers']);
$this->assertNotEmpty($layout['regions']);
}
/**
* Test Add Region Drawer
*/
public function testDeleteDrawer()
{
$layout = $this->checkout($this->layout);
// add Drawer Region
$drawer = $this->getEntityProvider()->post('/region/drawer/' . $layout->layoutId);
// delete Drawer Region
$response = $this->sendRequest('DELETE', '/region/' . $drawer['regionId']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(204, $body->status);
$this->assertSame(true, $body->success);
}
public function testListAll()
{
$response = $this->sendRequest('GET','/action');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* Add Action
* @dataProvider AddActionSuccessCases
* @param string $source
* @param string $triggerType
* @param string|null $triggerCode
* @param string $actionType
* @param string $target
* @param string|null $layoutCode
*/
public function testAddActionSuccess(?string $source, ?string $triggerType, ?string $triggerCode, string $actionType, string $target, ?string $layoutCode)
{
$layout = $this->checkout($this->layout);
$sourceId = null;
$targetId = null;
$widgetId = null;
// Add a couple of text widgets to the region
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
$widget = (new XiboText($this->getEntityProvider()))->hydrate($response);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget B',
'duration' => 100,
'useDuration' => 1
]);
$widget2 = (new XiboText($this->getEntityProvider()))->hydrate($response);
// depending on the source from AddActionsCases, the sourceId will be different
if ($source === 'layout') {
$sourceId = $layout->layoutId;
} elseif ($source === 'region') {
$sourceId = $layout->regions[0]->regionId;
} else {
$sourceId = $widget->widgetId;
}
// depending on the target screen|region we may need targetId
if ($target === 'region') {
$targetId = $layout->regions[0]->regionId;
}
if ($actionType == 'navWidget') {
$widgetId = $widget2->widgetId;
}
$response = $this->sendRequest('POST', '/action', [
'triggerType' => $triggerType,
'triggerCode' => $triggerCode,
'actionType' => $actionType,
'target' => $target,
'targetId' => $targetId,
'widgetId' => $widgetId,
'layoutCode' => $layoutCode,
'source' => $source,
'sourceId' => $sourceId,
'layoutId' => $layout->layoutId
]);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(201, $body->status);
$this->assertSame(true, $body->success);
$this->assertSame(false, $body->grid);
$this->assertNotEmpty($body->data, 'Empty Data');
$this->assertSame($layout->layoutId, $body->data->layoutId);
$this->assertSame($sourceId, $body->data->sourceId);
$this->assertSame($triggerType, $body->data->triggerType);
$this->assertSame($triggerCode, $body->data->triggerCode);
$this->assertSame($actionType, $body->data->actionType);
$this->assertSame($target, $body->data->target);
$this->assertSame($targetId, $body->data->targetId);
}
/**
* Each array is a test run
* Format (string $source, string $triggerType, string|null $triggerCode, string $actionType, string $target, string LayoutCode)
* @return array
*/
public function AddActionSuccessCases()
{
return [
'Layout' => ['layout', 'touch', 'trigger code', 'next', 'screen', null],
'Layout with region target' => ['layout', 'touch', null, 'previous', 'region', null],
'Region' => ['region', 'webhook', 'test', 'previous', 'screen', null],
'Region with region target' => ['region', 'touch', null, 'previous', 'region', null],
'Widget' => ['widget', 'touch', null, 'next', 'screen', null],
'Widget with region target' => ['widget', 'touch', null, 'next', 'region', null],
'Navigate to Widget' => ['layout', 'touch', null, 'navWidget', 'screen', null],
'Navigate to Layout with code' => ['layout', 'touch', null, 'navLayout', 'screen', 'CodeIdentifier'],
'Web UI' => [null, null, null, 'next', 'screen', null]
];
}
public function testEditAction()
{
$layout = $this->checkout($this->layout);
$action = $this->getEntityProvider()->post('/action', [
'actionType' => 'previous',
'target' => 'screen',
'layoutId' => $layout->layoutId
]);
$response = $this->sendRequest('PUT', '/action/' . $action['actionId'], [
'source' => 'layout',
'sourceId' => $layout->layoutId,
'triggerType' => 'webhook',
'triggerCode' => 'new code',
'actionType' => 'next',
'target' => 'region',
'targetId' => $layout->regions[0]->regionId
], ['Content-Type' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(200, $body->status);
$this->assertSame(true, $body->success);
$this->assertSame(false, $body->grid);
$this->assertNotEmpty($body->data, 'Empty Data');
$this->assertSame($layout->layoutId, $body->data->sourceId);
$this->assertSame($layout->layoutId, $body->data->layoutId);
$this->assertSame('webhook', $body->data->triggerType);
$this->assertSame('new code', $body->data->triggerCode);
$this->assertSame('next', $body->data->actionType);
$this->assertSame('region', $body->data->target);
$this->assertSame($layout->regions[0]->regionId, $body->data->targetId);
}
public function testDeleteAction()
{
$layout = $this->checkout($this->layout);
$action = $this->getEntityProvider()->post('/action', [
'triggerType' => 'webhook',
'triggerCode' => 'test',
'actionType' => 'previous',
'target' => 'screen',
'layoutId' => $layout->layoutId
]);
$response = $this->sendRequest('DELETE', '/action/' . $action['actionId']);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(204, $body->status);
$this->assertSame(true, $body->success);
$this->assertSame(false, $body->grid);
// check if one action remains with our Layout Id.
$actions = $this->getEntityProvider()->get('/action', ['sourceId' => $layout->layoutId]);
$this->assertSame(0, count($actions));
}
/**
* Add Action
* @dataProvider editActionFailureCases
* @param string $source
* @param string $triggerType
* @param string|null $triggerCode
* @param string $actionType
* @param string $target
*/
public function testEditActionFailure(string $source, string $triggerType, ?string $triggerCode, string $actionType, string $target)
{
$layout = $this->checkout($this->layout);
$action = $this->getEntityProvider()->post('/action', [
'actionType' => 'previous',
'target' => 'screen',
'layoutId' => $layout->layoutId
]);
$targetId = null;
$widgetId = null;
$layoutCode = null;
if ($source === 'layout') {
$sourceId = $layout->layoutId;
} elseif ($source === 'region') {
$sourceId = $layout->regions[0]->regionId;
} else {
$sourceId = null;
}
$response = $this->sendRequest('PUT', '/action/' . $action['actionId'], [
'triggerType' => $triggerType,
'triggerCode' => $triggerCode,
'actionType' => $actionType,
'target' => $target,
'targetId' => $targetId,
'source' => $source,
'sourceId' => $sourceId
]);
$body = json_decode($response->getBody());
// in other failure cases, we expect to get invalidArgument exception.
$this->assertSame(422, $response->getStatusCode());
// get the error message for cases and make sure we return correct one.
if ($source === 'playlist') {
$this->assertSame('Invalid source', $body->error);
}
// wrong trigger type case
if ($triggerType === 'notExistingType') {
$this->assertSame('Invalid trigger type', $body->error);
}
// wrong trigger type case
if ($actionType === 'wrongAction') {
$this->assertSame('Invalid action type', $body->error);
}
// wrong target case
if ($target === 'world') {
$this->assertSame('Invalid target', $body->error);
}
// test case when we have target set to region, but we don't set targetId to any regionId
if ($target === 'region') {
$this->assertSame('Please select a Region', $body->error);
}
// trigger code in non layout
if ($triggerType === 'webhook' && $triggerCode === null) {
$this->assertSame('Please provide trigger code', $body->error);
}
// navWidget without widgetId
if ($actionType === 'navWidget' && $widgetId == null) {
$this->assertSame('Please select a Widget', $body->error);
}
// navLayout without layoutCode
if ($actionType === 'navLayout' && $layoutCode == null) {
$this->assertSame('Please enter Layout code', $body->error);
}
}
/**
* Each array is a test run
* Format (string $source, string $triggerType, string|null $triggerCode, string $actionType, string $target)
* @return array
*/
public function editActionFailureCases()
{
return [
'Wrong source' => ['playlist', 'touch', null, 'next', 'screen'],
'Wrong trigger type' => ['layout', 'notExistingType', null, 'previous', 'screen'],
'Wrong action type' => ['layout', 'touch', null, 'wrongAction', 'screen'],
'Wrong target' => ['layout', 'touch', null, 'next', 'world'],
'Target region without targetId' => ['layout', 'touch', 'trigger code', 'next', 'region'],
'Missing trigger code for webhook' => ['region', 'webhook', null, 'next', 'screen'],
'Navigate to Widget without widgetId' => ['layout', 'touch', null, 'navWidget', 'screen'],
'Navigate to Layout without layoutCode' => ['layout', 'touch', null, 'navLayout', 'screen']
];
}
public function testCopyLayoutWithActions()
{
$layout = $this->checkout($this->layout);
$this->getEntityProvider()->post('/action', [
'triggerType' => 'touch',
'actionType' => 'previous',
'target' => 'screen',
'layout' => $layout->layoutId
]);
$this->layout = $this->publish($this->layout);
$response = $this->sendRequest('POST', '/layout/copy/' . $this->layout->layoutId, ['copyMediaFiles' => 0, 'name' => Random::generateString()]);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response);
$body = json_decode($response->getBody());
$this->assertSame(201, $body->status);
$newLayoutId = $body->id;
$newLayout = $this->getEntityProvider()->get('/layout', ['layoutId' => $newLayoutId, 'embed' => 'regions,actions'])[0];
$this->assertNotEmpty($newLayout['actions']);
// delete the copied layout
(new XiboLayout($this->getEntityProvider()))->getById($newLayoutId)->delete();
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutDraftTest
* @package Xibo\Tests\integration
*/
class LayoutDraftTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout */
private $layout;
public function setup()
{
parent::setup();
$this->layout = $this->createLayout();
}
public function tearDown()
{
parent::tearDown();
// This should always be the original, regardless of whether we checkout/discard/etc
$this->layout->delete();
}
/**
* Test adding a region to a Layout that has been checked out, but use the parent
*/
public function testAddRegionCheckoutParent()
{
// Add region to our layout with data from regionSuccessCases
$response = $this->sendRequest('POST','/region/' . $this->layout->layoutId, [
'width' => 100,
'height' => 100,
'top' => 10,
'left' => 10
]);
$this->assertSame(422, $response->getStatusCode(), 'Status Incorrect');
$object = json_decode($response->getBody());
$this->assertSame(false, $object->success);
$this->assertSame(422, $object->httpStatus);
}
/**
* Test adding a region to a Layout that has been checked out, using the draft
*/
public function testAddRegionCheckout()
{
// Checkout the Parent, but add a Region to the Original
$layout = $this->getDraft($this->layout);
// Add region to our layout with data from regionSuccessCases
$response = $this->sendRequest('POST','/region/' . $layout->layoutId, [
'width' => 100,
'height' => 100,
'top' => 10,
'left' => 10
]);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
}
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Exception\XiboApiException;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
class LayoutLockTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout */
private $layout;
public function setup()
{
parent::setup();
$this->layout = $this->createLayout();
// Get Draft
$layout = $this->getDraft($this->layout);
$this->addSimpleWidget($layout);
$this->layout = $this->publish($this->layout);
// Set the Layout status
$this->setLayoutStatus($this->layout, 1);
}
public function tearDown()
{
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
parent::tearDown();
}
public function testIsLockedObject()
{
// draft
$layout = $this->checkout($this->layout);
// add simple Widget via API
$this->addSimpleWidget($layout);
// Get the Layout object via web request
$response = $this->sendRequest('GET', '/layout', ['layoutId' => $layout->layoutId], [], 'layoutLock');
$body = json_decode($response->getBody());
$layoutObject = $body[0];
// check if the isLocked dynamic object is there and is not empty, then check the values inside of it.
// we expect it to be locked with our LayoutId and API entryPoint
$this->assertNotEmpty($layoutObject->isLocked);
$this->assertSame($layout->layoutId, $layoutObject->isLocked->layoutId);
$this->assertSame('API', $layoutObject->isLocked->entryPoint);
}
public function testApiToWeb()
{
// draft
$layout = $this->checkout($this->layout);
// add simple Widget via API
$this->addSimpleWidget($layout);
// layout should be locked for our User with API entry point for 5 min.
// attempt to add another Widget via web request
$response = $this->sendRequest('POST', '/playlist/widget/clock/' . $layout->regions[0]->regionPlaylist->playlistId, [
'duration' => 100,
'useDuration' => 1
], [], 'layoutLock');
$this->assertSame(403, $response->getStatusCode());
$body = json_decode($response->getBody());
$this->assertSame(403, $body->httpStatus);
$this->assertContains('Layout ID ' . $layout->layoutId . ' is locked by another User! Lock expires on:', $body->error);
}
public function testWebToApi()
{
// draft
$layout = $this->checkout($this->layout);
// call Layout status via web request, this will trigger the Layout Lock Middleware as well
$this->sendRequest('GET', '/layout/status/' . $layout->layoutId, [], [], 'layoutLock');
// attempt to add Widget via API
try {
$this->addSimpleWidget($layout);
} catch (XiboApiException $exception) {
$this->assertContains('Layout ID ' . $layout->layoutId . ' is locked by another User! Lock expires on:', $exception->getMessage());
}
}
}

View File

@@ -0,0 +1,903 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboRegion;
use Xibo\OAuth2\Client\Entity\XiboResolution;
use Xibo\Support\Exception\InvalidArgumentException;
use Xibo\Support\Exception\NotFoundException;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class LayoutTest
* @package Xibo\Tests\Integration
*/
class LayoutTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout[] */
protected $startLayouts;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all layouts that weren't there initially
$finalLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
// Loop over any remaining layouts and nuke them
foreach ($finalLayouts as $layout) {
/** @var XiboLayout $layout */
$flag = true;
foreach ($this->startLayouts as $startLayout) {
if ($startLayout->layoutId == $layout->layoutId) {
$flag = false;
}
}
if ($flag) {
try {
$layout->delete();
} catch (\Exception $e) {
$this->getLogger()->error('Unable to delete ' . $layout->layoutId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* @param $type
* @return int
*/
private function getResolutionId($type)
{
if ($type === 'landscape') {
$width = 1920;
$height = 1080;
} else if ($type === 'portrait') {
$width = 1080;
$height = 1920;
} else {
return -10;
}
//$this->getLogger()->debug('Querying for ' . $width . ', ' . $height);
$resolutions = (new XiboResolution($this->getEntityProvider()))->get(['width' => $width, 'height' => $height]);
if (count($resolutions) <= 0)
return -10;
return $resolutions[0]->resolutionId;
}
/**
* List all layouts known empty
*/
public function testListEmpty()
{
# Check that there is one layout in the database (the 'default layout')
if (count($this->startLayouts) > 1) {
$this->skipTest("There are pre-existing Layouts");
return;
}
$response = $this->sendRequest('GET','/layout');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be one default layout in the system
$this->assertEquals(1, $object->data->recordsTotal);
}
/**
* testAddSuccess - test adding various Layouts that should be valid
* @dataProvider provideSuccessCases
*/
public function testAddSuccess($layoutName, $layoutDescription, $layoutTemplateId, $layoutResolutionType)
{
$layoutResolutionId = $this->getResolutionId($layoutResolutionType);
# Create layouts with arguments from provideSuccessCases
$response = $this->sendRequest('POST','/layout', [
'name' => $layoutName,
'description' => $layoutDescription,
'layoutId' => $layoutTemplateId,
'resolutionId' => $layoutResolutionId
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($layoutName, $object->data->layout);
$this->assertSame($layoutDescription, $object->data->description);
# Check that the layout was really added
$layouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->assertEquals(count($this->startLayouts) + 1, count($layouts));
# Check that the layout was added correctly
$layout = (new XiboLayout($this->getEntityProvider()))->getById($object->id);
$this->assertSame($layoutName, $layout->layout);
$this->assertSame($layoutDescription, $layout->description);
# Clean up the Layout as we no longer need it
$this->assertTrue($layout->delete(), 'Unable to delete ' . $layout->layoutId);
}
/**
* testAddFailure - test adding various Layouts that should be invalid
* @dataProvider provideFailureCases
*/
public function testAddFailure($layoutName, $layoutDescription, $layoutTemplateId, $layoutResolutionType)
{
$layoutResolutionId = $this->getResolutionId($layoutResolutionType);
# Create layouts with arguments from provideFailureCases
$request = $this->createRequest('POST','/layout');
$request->withParsedBody([
'name' => $layoutName,
'description' => $layoutDescription,
'layoutId' => $layoutTemplateId,
'resolutionId' => $layoutResolutionId
]);
try {
$this->app->handle($request);
} catch (InvalidArgumentException $e) {
# check if they fail as expected
$this->assertSame(422, $e->getCode(), 'Expecting failure, received ' . $e->getMessage());
} catch (NotFoundException $e ) {
$this->assertSame(404, $e->getCode(), 'Expecting failure, received ' . $e->getMessage());
}
}
/**
* List all layouts known set
* @group minimal
*/
public function testListKnown()
{
$cases = $this->provideSuccessCases();
$layouts = [];
// Check each possible case to ensure it's not pre-existing
// If it is, skip over it
foreach ($cases as $case) {
$flag = true;
foreach ($this->startLayouts as $tmpLayout) {
if ($case[0] == $tmpLayout->layout) {
$flag = false;
}
}
if ($flag) {
$layouts[] = (new XiboLayout($this->getEntityProvider()))->create($case[0],$case[1],$case[2],$this->getResolutionId($case[3]));
}
}
$response = $this->sendRequest('GET','/layout');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be as many layouts as we created plus the number we started with in the system
$this->assertEquals(count($layouts) + count($this->startLayouts), $object->data->recordsTotal);
# Clean up the Layouts we created
foreach ($layouts as $lay) {
$lay->delete();
}
}
/**
* List specific layouts
* @group minimal
* @group destructive
* @depends testListKnown
* @depends testAddSuccess
* @dataProvider provideSuccessCases
*/
public function testListFilter($layoutName, $layoutDescription, $layoutTemplateId, $layoutResolutionType)
{
if (count($this->startLayouts) > 1) {
$this->skipTest("There are pre-existing Layouts");
return;
}
# Load in a known set of layouts
# We can assume this works since we depend upon the test which
# has previously added and removed these without issue:
$cases = $this->provideSuccessCases();
$layouts = [];
foreach ($cases as $case) {
$layouts[] = (new XiboLayout($this->getEntityProvider()))->create($case[0], $case[1], $case[2], $this->getResolutionId($case[3]));
}
// Fitler for our specific layout
$response = $this->sendRequest('GET','/layout', ['name' => $layoutName]);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# There should be at least one match
$this->assertGreaterThanOrEqual(1, $object->data->recordsTotal);
$flag = false;
# Check that for the records returned, $layoutName is in the groups names
foreach ($object->data->data as $lay) {
if (strpos($layoutName, $lay->layout) == 0) {
$flag = true;
}
else {
// The object we got wasn't the exact one we searched for
// Make sure all the words we searched for are in the result
foreach (array_map('trim',explode(",",$layoutName)) as $word) {
assertTrue((strpos($word, $lay->layout) !== false), 'Layout returned did not match the query string: ' . $lay->layout);
}
}
}
$this->assertTrue($flag, 'Search term not found');
// Remove the Layouts we've created
foreach ($layouts as $lay) {
$lay->delete();
}
}
/**
* Each array is a test run
* Format (LayoutName, description, layoutID (template), resolution ID)
* @return array
*/
public function provideSuccessCases()
{
# Data for testAddSuccess, easily expandable - just add another set of data below
return [
// Multi-language layouts
'English 1' => ['phpunit test Layout', 'Api', NULL, 'landscape'],
'French 1' => ['Test de Français 1', 'Bienvenue à la suite de tests Xibo', NULL, 'landscape'],
'German 1' => ['Deutsch Prüfung 1', 'Weiß mit schwarzem Text', NULL, 'landscape'],
'Simplified Chinese 1' => ['试验组', '测试组描述', NULL, 'landscape'],
'Portrait layout' => ['Portrait layout', '1080x1920', '', 'portrait'],
'No Description' => ['Just the title and resolution', NULL, '', 'portrait'],
'Just title' => ['Just the name', NULL, NULL, 'portrait']
];
}
/**
* Each array is a test run
* Format (LayoutName, description, layoutID (template), resolution ID)
* @return array
*/
public function provideFailureCases()
{
# Data for testAddfailure, easily expandable - just add another set of data below
return [
// Description is limited to 255 characters
'Description over 254 characters' => ['Too long description', Random::generateString(255), '', 'landscape'],
// Missing layout names
'layout name empty' => ['', 'Layout name is empty', '', 'landscape'],
'Layout name null' => [null, 'Layout name is null', '', 'landscape'],
'Wrong resolution ID' => ['id not found', 'not found exception', '', 'invalid']
];
}
/**
* Try and add two layouts with the same name
*/
public function testAddDuplicate()
{
# Check if there are layouts with that name already in the system
$flag = true;
foreach ($this->startLayouts as $layout) {
if ($layout->layout == 'phpunit layout') {
$flag = false;
}
}
# Load in a known layout if it's not there already
$landscapeId = $this->getResolutionId('landscape');
if ($flag) {
(new XiboLayout($this->getEntityProvider()))->create(
'phpunit layout',
'phpunit layout',
'',
$landscapeId
);
}
$response = $this->sendRequest('POST','/layout', [
'name' => 'phpunit layout',
'description' => 'phpunit layout',
'resolutionId' => $landscapeId
]);
$this->assertSame(409, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode() . '. Body = ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(false, $object->success);
$this->assertContains('You already own a Layout called ', $object->error);
}
/**
* Edit an existing layout
*/
public function testEdit()
{
// Create a known layout with a random name for us to work with.
// it will automatically get deleted in tearDown()
$layout = $this->createLayout();
// We do not need to checkout the Layout to perform an edit of its top level data.
// Change the layout name and description
$name = Random::generateString(8, 'phpunit');
$description = Random::generateString(8, 'description');
$response = $this->sendRequest('PUT','/layout/' . $layout->layoutId, [
'name' => $name,
'description' => $description
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->layout);
$this->assertSame($description, $object->data->description);
# Check that the layout was actually renamed
$layout = (new XiboLayout($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $layout->layout);
$this->assertSame($description, $layout->description);
# Clean up the Layout as we no longer need it
$layout->delete();
}
/**
* Edit an existing layout that should fail because of negative value in the backgroundzIndex
*/
public function testEditFailure()
{
// Create a known layout with a random name for us to work with.
// it will automatically get deleted in tearDown()
$layout = $this->createLayout();
// Set a background z-index that is outside parameters
$response = $this->sendRequest('PUT','/layout/' . $layout->layoutId, [
'backgroundColor' => $layout->backgroundColor,
'backgroundzIndex' => -1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getBody());
}
/**
* Test delete
* @group minimal
*/
public function testDelete()
{
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known layouts
$layout1 = (new XiboLayout($this->getEntityProvider()))->create($name1, 'phpunit description', '', $this->getResolutionId('landscape'));
$layout2 = (new XiboLayout($this->getEntityProvider()))->create($name2, 'phpunit description', '', $this->getResolutionId('landscape'));
# Delete the one we created last
$response = $this->sendRequest('DELETE','/layout/' . $layout2->layoutId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$layouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->assertEquals(count($this->startLayouts) + 1, count($layouts));
$flag = false;
foreach ($layouts as $layout) {
if ($layout->layoutId == $layout1->layoutId) {
$flag = true;
}
}
$this->assertTrue($flag, 'Layout ID ' . $layout1->layoutId . ' was not found after deleting a different layout');
$layout1->delete();
}
/**
* Try to delete a layout that is assigned to a campaign
*/
public function testDeleteAssigned()
{
# Load in a known layout
/** @var XiboLayout $layout */
$layout = (new XiboLayout($this->getEntityProvider()))->create('phpunit layout assigned', 'phpunit layout', '', $this->getResolutionId('landscape'));
// Make a campaign with a known name
$name = Random::generateString(8, 'phpunit');
$campaign = (new XiboCampaign($this->getEntityProvider()))->create($name);
// Assign layout to campaign
$this->getEntityProvider()->post('/campaign/layout/assign/' . $campaign->campaignId, [
'layoutId' => $layout->layoutId
]);
# Check if it's assigned
$campaignCheck = (new XiboCampaign($this->getEntityProvider()))->getById($campaign->campaignId);
$this->assertSame(1, $campaignCheck->numberLayouts);
# Try to Delete the layout assigned to the campaign
$response = $this->sendRequest('DELETE','/layout/' . $layout->layoutId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
}
/**
* Test Layout Retire
*/
public function testRetire()
{
// Get known layout
$layout = $this->createLayout();
// Call retire
$response = $this->sendRequest('PUT','/layout/retire/' . $layout->layoutId, [], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
// Get the same layout again and make sure its retired = 1
$layout = (new XiboLayout($this->getEntityProvider()))->getById($layout->layoutId);
$this->assertSame(1, $layout->retired, 'Retired flag not updated');
}
/**
* Test Unretire
*/
public function testUnretire()
{
// Get known layout
/** @var XiboLayout $layout */
$layout = $this->createLayout();
// Retire it
$this->getEntityProvider()->put('/layout/retire/' . $layout->layoutId);
// Call layout edit with this Layout
$response = $this->sendRequest('PUT','/layout/unretire/' . $layout->layoutId, [], [
'CONTENT_TYPE' => 'application/x-www-form-urlencoded'
]);
// Make sure that was successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
// Get the same layout again and make sure its retired = 0
$layout = (new XiboLayout($this->getEntityProvider()))->getById($layout->layoutId);
$this->assertSame(0, $layout->retired, 'Retired flag not updated. ' . $response->getBody());
}
/**
* Add new region to a specific layout
* @dataProvider regionSuccessCases
*/
public function testAddRegionSuccess($regionWidth, $regionHeight, $regionTop, $regionLeft)
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
// Add region to our layout with data from regionSuccessCases
$response = $this->sendRequest('POST','/region/' . $layout->layoutId, [
'width' => $regionWidth,
'height' => $regionHeight,
'top' => $regionTop,
'left' => $regionLeft
]);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if region has intended values
$this->assertSame($regionWidth, $object->data->width);
$this->assertSame($regionHeight, $object->data->height);
$this->assertSame($regionTop, $object->data->top);
$this->assertSame($regionLeft, $object->data->left);
}
/**
* Each array is a test run
* Format (width, height, top, left)
* @return array
*/
public function regionSuccessCases()
{
return [
// various correct regions
'region 1' => [500, 350, 100, 150],
'region 2' => [350, 200, 50, 50],
'region 3' => [69, 69, 20, 420],
'region 4 no offsets' => [69, 69, 0, 0]
];
}
/**
* testAddFailure - test adding various regions that should be invalid
* @dataProvider regionFailureCases
*/
public function testAddRegionFailure($regionWidth, $regionHeight, $regionTop, $regionLeft, $expectedHttpCode, $expectedWidth, $expectedHeight)
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
# Add region to our layout with datafrom regionFailureCases
$response = $this->sendRequest('POST','/region/' . $layout->layoutId, [
'width' => $regionWidth,
'height' => $regionHeight,
'top' => $regionTop,
'left' => $regionLeft
]);
# Check if we receive failure as expected
$this->assertSame($expectedHttpCode, $response->getStatusCode(), 'Expecting failure, received ' . $response->getBody());
if ($expectedHttpCode == 200) {
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($expectedWidth, $object->data->width);
$this->assertSame($expectedHeight, $object->data->height);
}
}
/**
* Each array is a test run
* Format (width, height, top, left)
* @return array
*/
public function regionFailureCases()
{
return [
// various incorrect regions
'region no size' => [NULL, NULL, 20, 420, 200, 250, 250],
'region negative dimensions' => [-69, -420, 20, 420, 422, null, null]
];
}
/**
* Edit known region
*/
public function testEditRegion()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
# Add region to our layout
$region = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,300,75,125);
# Edit region
$response = $this->sendRequest('PUT','/region/' . $region->regionId, [
'name' => $layout->layout . ' edited',
'width' => 700,
'height' => 500,
'top' => 400,
'left' => 400,
'loop' => 0,
'zIndex' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if region has updated values
$this->assertSame(700, $object->data->width);
$this->assertSame(500, $object->data->height);
$this->assertSame(400, $object->data->top);
$this->assertSame(400, $object->data->left);
}
/**
* Edit known region that should fail because of negative z-index value
*/
public function testEditRegionFailure()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
# Add region to our layout
$region = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,300,75,125);
# Edit region
$response = $this->sendRequest('PUT','/region/' . $region->regionId, [
'width' => 700,
'height' => 500,
'top' => 400,
'left' => 400,
'loop' => 0,
'zIndex' => -1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if it failed
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getBody());
}
/**
* delete region test
*/
public function testDeleteRegion()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
$region = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200, 670, 100, 100);
# Delete region
$response = $this->sendRequest('DELETE','/region/' . $region->regionId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
}
/**
* Add tag to a layout
*/
public function testAddTag()
{
# Create layout
$name = Random::generateString(8, 'phpunit');
$layout = (new XiboLayout($this->getEntityProvider()))->create($name, 'phpunit description', '', $this->getResolutionId('landscape'));
# Assign new tag to our layout
$response = $this->sendRequest('POST','/layout/' . $layout->layoutId . '/tag' , [
'tag' => ['API']
]);
$layout = (new XiboLayout($this->getEntityProvider()))->getById($layout->layoutId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
foreach ($layout->tags as $tag) {
$this->assertSame('API', $tag['tag']);
}
}
/**
* Delete tags from layout
*/
public function testDeleteTag()
{
$name = Random::generateString(8, 'phpunit');
$layout = (new XiboLayout($this->getEntityProvider()))->create($name, 'phpunit description', '', $this->getResolutionId('landscape'));
$tag = 'API';
$layout->addTag($tag);
$layout = (new XiboLayout($this->getEntityProvider()))->getById($layout->layoutId);
$response = $this->sendRequest('POST','/layout/' . $layout->layoutId . '/untag', [
'tag' => [$tag]
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$layout->delete();
}
/**
* Calculate layout status
*/
public function testStatus()
{
# Create layout
$name = Random::generateString(8, 'phpunit');
$layout = (new XiboLayout($this->getEntityProvider()))->create($name, 'phpunit description', '', $this->getResolutionId('landscape'));
# Calculate layouts status
$response = $this->sendRequest('GET','/layout/status/' . $layout->layoutId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
}
/**
* Copy Layout Test
*/
public function testCopy()
{
# Load in a known layout
/** @var XiboLayout $layout */
$layout = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit layout',
'',
$this->getResolutionId('landscape')
);
// Generate new random name
$nameCopy = Random::generateString(8, 'phpunit');
// Call copy
$response = $this->sendRequest('POST','/layout/copy/' . $layout->layoutId, [
'name' => $nameCopy,
'description' => 'Copy',
'copyMediaFiles' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if copied layout has correct name
$this->assertSame($nameCopy, $object->data->layout);
# Clean up the Layout as we no longer need it
$this->assertTrue($layout->delete(), 'Unable to delete ' . $layout->layoutId);
}
/**
* Position Test
*/
public function testPosition()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
# Create Two known regions and add them to that layout
$region1 = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,670,75,125);
$region2 = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,300,475,625);
# Reposition regions on that layout
$regionJson = json_encode([
[
'regionid' => $region1->regionId,
'width' => 700,
'height' => 500,
'top' => 400,
'left' => 400
],
[
'regionid' => $region2->regionId,
'width' => 100,
'height' => 100,
'top' => 40,
'left' => 40
]
]);
$response = $this->sendRequest('PUT','/region/position/all/' . $layout->layoutId, [
'regions' => $regionJson
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(true, $object->success);
$this->assertSame(200, $object->status);
}
/**
* Position Test with incorrect parameters (missing height and incorrect spelling)
*/
public function testPositionFailure()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
# Create Two known regions and add them to that layout
$region1 = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,670,75,125);
$region2 = (new XiboRegion($this->getEntityProvider()))->create($layout->layoutId, 200,300,475,625);
# Reposition regions on that layout with incorrect/missing parameters
$regionJson = json_encode([
[
'regionid' => $region1->regionId,
'width' => 700,
'top' => 400,
'left' => 400
],
[
'regionid' => $region2->regionId,
'heigTH' => 100,
'top' => 40,
'left' => 40
]
]);
$response = $this->sendRequest('PUT','/region/position/all/' . $layout->layoutId, [
'regions' => $regionJson
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
# Check if it fails as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(false, $object->success);
$this->assertSame(422, $object->httpStatus);
}
/**
* Add a Drawer to the Layout
*/
public function testAddDrawer()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
// Add Drawer
$response = $this->sendRequest('POST', '/region/drawer/' . $layout->layoutId);
// Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
// Check if drawer has the right values
$this->assertSame($layout->width, $object->data->width);
$this->assertSame($layout->height, $object->data->height);
$this->assertSame(0, $object->data->top);
$this->assertSame(0, $object->data->left);
}
/**
* Edit a Drawer to the Layout
*/
public function testSaveDrawer()
{
// Create a Layout and Checkout
$layout = $this->createLayout();
$layout = $this->getDraft($layout);
// Add Drawer
$drawer = $this->getEntityProvider()->post('/region/drawer/' . $layout->layoutId);
// Save drawer
$response = $this->sendRequest('PUT', '/region/drawer/' . $drawer['regionId'], [
'width' => 1280,
'height' => 720
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
// Check if successful
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
// Check if drawer has the right values
$this->assertSame(1280, $object->data->width);
$this->assertSame(720, $object->data->height);
$this->assertSame(0, $object->data->top);
$this->assertSame(0, $object->data->left);
}
}

View File

@@ -0,0 +1,280 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\LocalWebTestCase;
class LibraryTest extends LocalWebTestCase
{
protected $startMedias;
protected $mediaName;
protected $mediaType;
protected $mediaId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startMedias = (new XiboLibrary($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all media files that weren't there initially
$finalMedias = (new XiboLibrary($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining media files and nuke them
foreach ($finalMedias as $media) {
/** @var XiboLibrary $media */
$flag = true;
foreach ($this->startMedias as $startMedia) {
if ($startMedia->mediaId == $media->mediaId) {
$flag = false;
}
}
if ($flag) {
try {
$media->deleteAssigned();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $media->mediaId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* List all file in library
*/
public function testListAll()
{
# Get all library items
$response = $this->sendRequest('GET','/library');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* Add new file to library
*/
public function testAdd()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$media = (new XiboLibrary($this->getEntityProvider()))->create('API video test', PROJECT_ROOT . '/tests/resources/HLH264.mp4');
$media->delete();
}
/**
* Add new file to library and replace old one in all layouts
*/
public function testReplace()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$media = (new XiboLibrary($this->getEntityProvider()))->create('flowers', PROJECT_ROOT . '/tests/resources/xts-flowers-001.jpg');
# Replace the image and update it in all layouts (name, file location, old media id, replace in all layouts flag, delete old revision flag)
$media2 = (new XiboLibrary($this->getEntityProvider()))->create('API replace image', PROJECT_ROOT . '/tests/resources/xts-flowers-002.jpg', $media->mediaId, 1, 1);
}
/**
* try to add not allowed filetype
*/
public function testAddEmpty()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$this->expectException('\Xibo\OAuth2\Client\Exception\XiboApiException');
$media = (new XiboLibrary($this->getEntityProvider()))->create('API incorrect file 2', PROJECT_ROOT . '/tests/resources/empty.txt');
}
/**
* Add tags to media
*/
public function testAddTag()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$media = (new XiboLibrary($this->getEntityProvider()))->create('flowers 2', PROJECT_ROOT . '/tests/resources/xts-flowers-001.jpg');
$response = $this->sendRequest('POST','/library/' . $media->mediaId . '/tag', [
'tag' => ['API']
]);
$media = (new XiboLibrary($this->getEntityProvider()))->getById($media->mediaId);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
foreach ($media->tags as $tag) {
$this->assertSame('API', $tag['tag']);
}
$media->delete();
}
/**
* Delete tags from media
* @group broken
*/
public function testDeleteTag()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$media = (new XiboLibrary($this->getEntityProvider()))->create('flowers', PROJECT_ROOT . '/tests/resources/xts-flowers-001.jpg');
$media->AddTag('API');
$media = (new XiboLibrary($this->getEntityProvider()))->getById($media->mediaId);
$this->assertSame('API', $media->tags);
$response = $this->sendRequest('POST','/library/' . $media->mediaId . '/untag', [
'tag' => ['API']
]);
$media = (new XiboLibrary($this->getEntityProvider()))->getById($media->mediaId);
print_r($media->tags);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$media->delete();
}
/**
* Edit media file
*/
public function testEdit()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$media = (new XiboLibrary($this->getEntityProvider()))->create('API video 4', PROJECT_ROOT . '/tests/resources/HLH264.mp4');
# Generate new random name
$name = Random::generateString(8, 'phpunit');
# Edit media file, change the name
$response = $this->sendRequest('PUT','/library/' . $media->mediaId, [
'name' => $name,
'duration' => 50,
'retired' => $media->retired,
'tags' => $media->tags,
'updateInLayouts' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertSame($name, $object->data->name);
$media = (new XiboLibrary($this->getEntityProvider()))->getById($media->mediaId);
$this->assertSame($name, $media->name);
$media->delete();
}
/**
* Test delete added media
*/
public function testDelete()
{
# Using XiboLibrary wrapper to upload new file to the CMS, need to provide (name, file location)
$media = (new XiboLibrary($this->getEntityProvider()))->create('API video 4', PROJECT_ROOT . '/tests/resources/HLH264.mp4');
# Delete added media file
$response = $this->sendRequest('DELETE','/library/' . $media->mediaId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
}
/**
* Library tidy
*/
public function testTidy()
{
$response = $this->sendRequest('DELETE','/library/tidy');
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
}
public function testUploadFromUrl()
{
shell_exec('cp -r ' . PROJECT_ROOT . '/tests/resources/rss/image1.jpg ' . PROJECT_ROOT . '/web');
$response = $this->sendRequest('POST','/library/uploadUrl', [
'url' => 'http://localhost/image1.jpg'
]);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertNotEmpty($object->data, 'Empty Response');
$this->assertSame('image', $object->data->mediaType);
$this->assertSame(0, $object->data->expires);
$this->assertSame('image1', $object->data->name);
$this->assertNotEmpty($object->data->mediaId, 'Not successful, MediaId is empty');
$module = $this->getEntityProvider()->get('/module', ['name' => 'Image']);
$moduleDefaultDuration = $module[0]['defaultDuration'];
$this->assertSame($object->data->duration, $moduleDefaultDuration);
shell_exec('rm -r ' . PROJECT_ROOT . '/web/image1.jpg');
}
public function testUploadFromUrlWithType()
{
shell_exec('cp -r ' . PROJECT_ROOT . '/tests/resources/rss/image2.jpg ' . PROJECT_ROOT . '/web');
$response = $this->getEntityProvider()->post('/library/uploadUrl?envelope=1', [
'url' => 'http://localhost/image2.jpg',
'type' => 'image'
]);
$this->assertSame(201, $response['status'], json_encode($response));
$this->assertNotEmpty($response['data'], 'Empty Response');
$this->assertSame('image', $response['data']['mediaType']);
$this->assertSame(0, $response['data']['expires']);
$this->assertSame('image2', $response['data']['name']);
$this->assertNotEmpty($response['data']['mediaId'], 'Not successful, MediaId is empty');
$module = $this->getEntityProvider()->get('/module', ['name' => 'Image']);
$moduleDefaultDuration = $module[0]['defaultDuration'];
$this->assertSame($response['data']['duration'], $moduleDefaultDuration);
shell_exec('rm -r ' . PROJECT_ROOT . '/web/image2.jpg');
}
public function testUploadFromUrlWithTypeAndName()
{
shell_exec('cp -r ' . PROJECT_ROOT . '/tests/resources/HLH264.mp4 ' . PROJECT_ROOT . '/web');
$response = $this->getEntityProvider()->post('/library/uploadUrl?envelope=1', [
'url' => 'http://localhost/HLH264.mp4',
'type' => 'video',
'optionalName' => 'PHPUNIT URL upload video'
]);
$this->assertSame(201, $response['status'], json_encode($response));
$this->assertNotEmpty($response['data'], 'Empty Response');
$this->assertSame('video', $response['data']['mediaType']);
$this->assertSame(0, $response['data']['expires']);
$this->assertSame('PHPUNIT URL upload video', $response['data']['name']);
$this->assertNotEmpty($response['data']['mediaId'], 'Not successful, MediaId is empty');
// for videos we expect the Media duration to be the actual video duration.
$this->assertSame($response['data']['duration'], 78);
shell_exec('rm -r ' . PROJECT_ROOT . '/web/HLH264.mp4');
}
}

View File

@@ -0,0 +1,118 @@
<?php
/**
* Copyright (C) 2021 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\LocalWebTestCase;
class MenuBoardCategoryTest extends LocalWebTestCase
{
private $menuBoard;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->menuBoard = $this->getEntityProvider()->post('/menuboard', [
'name' => Random::generateString(10, 'phpunit'),
'description' => 'Description for test Menu Board'
]);
}
public function tearDown()
{
parent::tearDown();
if ($this->menuBoard['menuId'] !== null) {
$this->getEntityProvider()->delete('/menuboard/' . $this->menuBoard['menuId']);
}
}
public function testListEmpty()
{
$response = $this->sendRequest('GET', '/menuboard/' . $this->menuBoard['menuId'] . '/categories');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertEquals(0, $object->data->recordsTotal);
}
public function testAdd()
{
$media = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(10, 'API Image'), PROJECT_ROOT . '/tests/resources/xts-night-001.jpg');
$name = Random::generateString(10, 'Category Add');
$response = $this->sendRequest('POST', '/menuboard/' . $this->menuBoard['menuId'] . '/category', [
'name' => $name,
'mediaId' => $media->mediaId
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame($media->mediaId, $object->data->mediaId);
$media->delete();
}
public function testEdit()
{
$menuBoardCategory = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoard['menuId'] . '/category', [
'name' => 'Test Menu Board Category Edit'
]);
$name = Random::generateString(10, 'Category Edit');
$response = $this->sendRequest('PUT', '/menuboard/' . $menuBoardCategory['menuCategoryId'] . '/category', [
'name' => $name,
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
}
public function testDelete()
{
$menuBoardCategory = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoard['menuId'] . '/category', [
'name' => 'Test Menu Board Category Delete'
]);
$response = $this->sendRequest('DELETE', '/menuboard/' . $menuBoardCategory['menuCategoryId'] . '/category');
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
}
}

View File

@@ -0,0 +1,198 @@
<?php
/**
* Copyright (C) 2021 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\LocalWebTestCase;
class MenuBoardProductTest extends LocalWebTestCase
{
private $menuBoard;
private $menuBoardCategory;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->menuBoard = $this->getEntityProvider()->post('/menuboard', [
'name' => Random::generateString(10, 'phpunit'),
'description' => 'Description for test Menu Board'
]);
$this->menuBoardCategory = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoard['menuId'] . '/category', [
'name' => 'Test Menu Board Category Edit'
]);
}
public function tearDown()
{
parent::tearDown();
if ($this->menuBoard['menuId'] !== null) {
$this->getEntityProvider()->delete('/menuboard/' . $this->menuBoard['menuId']);
}
}
public function testListEmpty()
{
$response = $this->sendRequest('GET', '/menuboard/' . $this->menuBoardCategory['menuCategoryId'] . '/products');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertEquals(0, $object->data->recordsTotal);
}
public function testAdd()
{
$media = (new XiboLibrary($this->getEntityProvider()))->create(Random::generateString(10, 'API Image'), PROJECT_ROOT . '/tests/resources/xts-night-001.jpg');
$name = Random::generateString(10, 'Product Add');
$response = $this->sendRequest('POST', '/menuboard/' . $this->menuBoardCategory['menuCategoryId'] . '/product', [
'name' => $name,
'mediaId' => $media->mediaId,
'price' => '$12.40',
'description' => 'Product Description',
'allergyInfo' => 'N/A',
'availability' => 1,
'productOptions' => ['small', 'medium', 'large'],
'productValues' => ['$10.40', '$15.40', '$20.20']
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame($media->mediaId, $object->data->mediaId);
$this->assertSame('$12.40', $object->data->price);
$this->assertSame('Product Description', $object->data->description);
$this->assertSame('N/A', $object->data->allergyInfo);
$this->assertSame(1, $object->data->availability);
// product options are ordered by option name
$this->assertSame($object->id, $object->data->productOptions[2]->menuProductId);
$this->assertSame('small', $object->data->productOptions[2]->option);
$this->assertSame('$10.40', $object->data->productOptions[2]->value);
$this->assertSame($object->id, $object->data->productOptions[1]->menuProductId);
$this->assertSame('medium', $object->data->productOptions[1]->option);
$this->assertSame('$15.40', $object->data->productOptions[1]->value);
$this->assertSame($object->id, $object->data->productOptions[0]->menuProductId);
$this->assertSame('large', $object->data->productOptions[0]->option);
$this->assertSame('$20.20', $object->data->productOptions[0]->value);
$media->delete();
}
/**
* @dataProvider provideFailureCases
*/
public function testAddFailure($name, $price)
{
$response = $this->sendRequest('POST', '/menuboard/' . $this->menuBoardCategory['menuCategoryId'] . '/product', [
'name' => $name,
'price' => $price
]);
# check if they fail as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Each array is a test run
* Format (name, price, productOptions, productValues)
* @return array
*/
public function provideFailureCases()
{
return [
'empty name' => ['', '$11', [], []],
'empty price' => ['Test Product', null, [], []]
];
}
public function testEdit()
{
$menuBoardProduct = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoardCategory['menuCategoryId'] . '/product', [
'name' => 'Test Menu Board Product Edit',
'price' => '$11.11',
'productOptions' => ['small', 'medium', 'large'],
'productValues' => ['$10.40', '$15.40', '$20.20']
]);
$name = Random::generateString(10, 'Product Edit');
$response = $this->sendRequest('PUT', '/menuboard/' . $menuBoardProduct['menuProductId'] . '/product', [
'name' => $name,
'price' => '$9.99',
'description' => 'Product Description Edited',
'allergyInfo' => '',
'availability' => 1,
'productOptions' => ['small', 'medium', 'large'],
'productValues' => ['$8.40', '$12.40', '$15.20']
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame('$9.99', $object->data->price);
$this->assertSame('Product Description Edited', $object->data->description);
$this->assertSame('', $object->data->allergyInfo);
$this->assertSame(1, $object->data->availability);
// product options are ordered by option name
$this->assertSame($object->id, $object->data->productOptions[2]->menuProductId);
$this->assertSame('small', $object->data->productOptions[2]->option);
$this->assertSame('$8.40', $object->data->productOptions[2]->value);
$this->assertSame($object->id, $object->data->productOptions[1]->menuProductId);
$this->assertSame('medium', $object->data->productOptions[1]->option);
$this->assertSame('$12.40', $object->data->productOptions[1]->value);
$this->assertSame($object->id, $object->data->productOptions[0]->menuProductId);
$this->assertSame('large', $object->data->productOptions[0]->option);
$this->assertSame('$15.20', $object->data->productOptions[0]->value);
}
public function testDelete()
{
$menuBoardProduct = $this->getEntityProvider()->post('/menuboard/' . $this->menuBoardCategory['menuCategoryId'] . '/product', [
'name' => 'Test Menu Board Category Delete',
'price' => '$11.11'
]);
$response = $this->sendRequest('DELETE', '/menuboard/' . $menuBoardProduct['menuProductId'] . '/product');
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* Copyright (C) 2021 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\Tests\LocalWebTestCase;
class MenuBoardTest extends LocalWebTestCase
{
private $menuBoardId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
}
public function tearDown()
{
parent::tearDown();
if ($this->menuBoardId !== null) {
$this->getEntityProvider()->delete('/menuboard/' . $this->menuBoardId);
}
}
public function testListAll()
{
$response = $this->sendRequest('GET', '/menuboards');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
public function testAdd()
{
$name = Random::generateString(10, 'MenuBoard Add');
$response = $this->sendRequest('POST', '/menuboard', [
'name' => $name,
'description' => 'Description for test Menu Board Add'
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame('Description for test Menu Board Add', $object->data->description);
$this->menuBoardId = $object->id;
}
public function testEdit()
{
$menuBoard = $this->getEntityProvider()->post('/menuboard', [
'name' => 'Test Menu Board Edit',
'description' => 'Description for test Menu Board Edit'
]);
$name = Random::generateString(10, 'MenuBoard Edit');
$response = $this->sendRequest('PUT', '/menuboard/' . $menuBoard['menuId'], [
'name' => $name,
'description' => 'Test Menu Board Edited description'
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->name);
$this->assertSame('Test Menu Board Edited description', $object->data->description);
$this->menuBoardId = $object->id;
}
public function testDelete()
{
$menuBoard = $this->getEntityProvider()->post('/menuboard', [
'name' => 'Test Menu Board Delete',
'description' => 'Description for test Menu Board Delete'
]);
$response = $this->sendRequest('DELETE', '/menuboard/' . $menuBoard['menuId']);
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
}
}

View File

@@ -0,0 +1,196 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\OAuth2\Client\Entity\XiboNotification;
use Xibo\Tests\LocalWebTestCase;
class NotificationTest extends LocalWebTestCase
{
/** @var XiboDisplayGroup[] */
protected $startDisplayGroups;
/** @var XiboNotification[] */
protected $startNotifications;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startDisplayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startNotifications = (new XiboNotification($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all display groups that weren't there initially
$finalDisplayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining display groups and nuke them
foreach ($finalDisplayGroups as $displayGroup) {
/** @var XiboDisplayGroup $displayGroup */
$flag = true;
foreach ($this->startDisplayGroups as $startGroup) {
if ($startGroup->displayGroupId == $displayGroup->displayGroupId) {
$flag = false;
}
}
if ($flag) {
try {
$displayGroup->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $displayGroup->displayGroupId . '. E:' . $e->getMessage());
}
}
}
// tearDown all notifications that weren't there initially
$finalNotifications = (new XiboNotification($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining notifications and nuke them
foreach ($finalNotifications as $notification) {
/** @var XiboNotification $notification */
$flag = true;
foreach ($this->startNotifications as $startNotf) {
if ($startNotf->notificationId == $notification->notificationId) {
$flag = false;
}
}
if ($flag) {
try {
$notification->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $notification->notificationId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* List notifications
*/
public function testListAll()
{
# Get all notifications
$response = $this->sendRequest('GET','/notification');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* Create new Notification
*/
public function testAdd()
{
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'notification description', 0, '');
# Add new notification and assign it to our display group
$subject = 'API Notification';
$response = $this->sendRequest('POST','/notification', [
'subject' => $subject,
'body' => 'Notification body text',
'releaseDt' => '2016-09-01 00:00:00',
'isEmail' => 0,
'isInterrupt' => 0,
'displayGroupIds' => [$displayGroup->displayGroupId]
// 'userGroupId' =>
]);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
# Check if the subject is correctly set
$this->assertSame($subject, $object->data->subject);
}
/**
* Delete notification
*/
public function testDelete()
{
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
# Create new notification
$notification = (new XiboNotification($this->getEntityProvider()))->create('API subject', 'API body', '2016-09-01 00:00:00', 0, 0, [$displayGroup->displayGroupId]);
# Delete notification
$response = $this->sendRequest('DELETE','/notification/' . $notification->notificationId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status);
# Clean up
$displayGroup->delete();
}
/**
* Edit notification
*/
public function testEdit()
{
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
# Create new notification
$notification = (new XiboNotification($this->getEntityProvider()))->create('API subject', 'API body', '2016-09-01 00:00:00', 0, 0, [$displayGroup->displayGroupId]);
$notification->releaseDt = Carbon::createFromTimestamp($notification->releaseDt)->format(DateFormatHelper::getSystemFormat());
# Create new subject
$subjectNew = 'Subject edited via API';
# Edit our notification
$response = $this->sendRequest('PUT','/notification/' . $notification->notificationId, [
'subject' => $subjectNew,
'body' => $notification->body,
'releaseDt' => $notification->releaseDt,
'isEmail' => $notification->isEmail,
'isInterrupt' => $notification->isInterrupt,
'displayGroupIds' => [$displayGroup->displayGroupId]
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($subjectNew, $object->data->subject);
# Clean up
$displayGroup->delete();
$notification->delete();
}
}

View File

@@ -0,0 +1,204 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\Entity\Display;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboDisplayProfile;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class PlayerSoftwareTest
* @package Xibo\Tests\integration
*/
class PlayerSoftwareTest extends LocalWebTestCase
{
use DisplayHelperTrait;
/** @var XiboLibrary */
protected $media;
/** @var XiboLibrary */
protected $media2;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplayProfile */
protected $displayProfile;
protected $version;
protected $version2;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) . ' Test');
// Upload version files
$uploadVersion = $this->uploadVersionFile(Random::generateString(), PROJECT_ROOT . '/tests/resources/Xibo_for_Android_v1.7_R61.apk');
$this->version = $uploadVersion['files'][0];
$uploadVersion2 = $this->uploadVersionFile(Random::generateString(), PROJECT_ROOT . '/tests/resources/Xibo_for_Android_v1.8_R108.apk');
$this->version2 = $uploadVersion2['files'][0];
// Create a Display
$this->display = $this->createDisplay(null, 'android');
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Create a display profile
$this->displayProfile = (new XiboDisplayProfile($this->getEntityProvider()))->create(Random::generateString(), 'android', 0);
// Edit display profile to add the uploaded apk to the config
$this->getEntityProvider()->put('/displayprofile/' . $this->displayProfile->displayProfileId, [
'name' => $this->displayProfile->name,
'type' => $this->displayProfile->type,
'isDefault' => $this->displayProfile->isDefault,
'versionMediaId' => $this->version['id']
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
// Delete the version files we've been working with
$this->getEntityProvider()->delete('/playersoftware/' . $this->version['id']);
$this->getEntityProvider()->delete('/playersoftware/' . $this->version2['id']);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the Display profile
$this->displayProfile->delete();
parent::tearDown();
}
// </editor-fold>
public function testVersionFromProfile()
{
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Edit display, assign it to the created display profile
$response = $this->sendRequest('PUT','/display/' . $this->display->displayId, [
'display' => $this->display->display,
'licensed' => $this->display->licensed,
'license' => $this->display->license,
'defaultLayoutId' => $this->display->defaultLayoutId,
'displayProfileId' => $this->displayProfile->displayProfileId,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded'] );
// Check response
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame($this->displayProfile->displayProfileId, $object->data->displayProfileId, $response->getBody());
// Ensure the Version Instructions are present on the Register Display call and that
// Register our display
$register = $this->getXmdsWrapper()->RegisterDisplay($this->display->license,
$this->display->license,
'android',
null,
null,
null,
'00:16:D9:C9:AL:69',
$this->display->xmrChannel,
$this->display->xmrPubKey
);
$this->getLogger()->debug($register);
$this->assertContains($this->version['fileName'], $register, 'Version information not in Register');
$this->assertContains('61', $register, 'Version information Code not in Register');
}
public function testVersionOverride()
{
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Edit display, set the versionMediaId
$response = $this->sendRequest('PUT','/display/' . $this->display->displayId, [
'display' => $this->display->display,
'licensed' => $this->display->licensed,
'license' => $this->display->license,
'versionMediaId' => $this->version2['id'],
'defaultLayoutId' => $this->display->defaultLayoutId,
'displayProfileId' => $this->displayProfile->displayProfileId
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded'] );
// Check response
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame($this->displayProfile->displayProfileId, $object->data->displayProfileId, $response->getBody());
$this->assertNotEmpty($object->data->overrideConfig);
foreach ($object->data->overrideConfig as $override) {
if ($override->name === 'versionMediaId') {
$this->assertSame($this->version2['id'], $override->value, json_encode($object->data->overrideConfig));
}
}
// call register
$register = $this->getXmdsWrapper()->RegisterDisplay($this->display->license,
$this->display->license,
'android',
null,
null,
null,
'00:16:D9:C9:AL:69',
$this->display->xmrChannel,
$this->display->xmrPubKey
);
// make sure the media ID set on the display itself is in the register
$this->getLogger()->debug($register);
$this->assertContains($this->version2['fileName'], $register, 'Version information not in Register');
$this->assertContains('108', $register, 'Version information Code not in Register');
}
private function uploadVersionFile($fileName, $filePath)
{
$payload = [
[
'name' => 'name',
'contents' => $fileName
],
[
'name' => 'files',
'contents' => fopen($filePath, 'r')
]
];
return $this->getEntityProvider()->post('/playersoftware', ['multipart' => $payload]);
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\Tests\LocalWebTestCase;
/**
* Class PlaylistTest
* @package Xibo\Tests\integration
*/
class PlaylistTest extends LocalWebTestCase
{
/** @var XiboPlaylist[] */
private $playlists;
/** @var XiboPlaylist */
private $duplicateName;
public function setup()
{
parent::setup();
$this->duplicateName = (new XiboPlaylist($this->getEntityProvider()))->hydrate($this->getEntityProvider()->post('/playlist', [
'name' => Random::generateString(5, 'playlist')
]));
// Add a Playlist to use for the duplicate name test
$this->playlists[] = $this->duplicateName;
}
public function tearDown()
{
$this->getLogger()->debug('Tearing down, removing ' . count($this->playlists));
// Delete any Playlists we've added
foreach ($this->playlists as $playlist) {
$this->getEntityProvider()->delete('/playlist/' . $playlist->playlistId);
}
parent::tearDown();
}
/**
* @return array
*/
public function addPlaylistCases()
{
return [
'Normal add' => [200, Random::generateString(5, 'playlist'), null, 0, null, null],
'Tags add' => [200, Random::generateString(5, 'playlist'), 'test', 0, null, null],
'Dynamic add' => [200, Random::generateString(5, 'playlist'), null, 1, 'test', null]
];
}
/**
* @return array
*/
public function addPlaylistFailureCases()
{
return [
'Dynamic without filter' => [422, Random::generateString(5, 'playlist'), null, 1, null, null],
'Without a name' => [422, null, null, 1, null, null]
];
}
/**
* @dataProvider addPlaylistCases
*/
public function testAddPlaylist($statusCode, $name, $tags, $isDynamic, $nameFilter, $tagFilter)
{
// Add this Playlist
$response = $this->sendRequest('POST', '/playlist', [
'name' => $name,
'tags' => $tags,
'isDynamic' => $isDynamic,
'filterMediaName' => $nameFilter,
'filterMediaTag' => $tagFilter
]);
// Check the response headers
$this->assertSame($statusCode, $response->getStatusCode(), 'Not successful: ' . $response->getStatusCode() . $response->getBody());
// Make sure we have a useful body
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, 'Missing data');
$this->assertObjectHasAttribute('id', $object, 'Missing id');
// Add to the list of playlists to clean up
if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) {
$this->playlists[] = (new XiboPlaylist($this->getEntityProvider()))->hydrate((array)$object->data);
}
// Get the Playlists back out from the API, to double check it has been created as we expected
/** @var XiboPlaylist $playlistCheck */
$playlistCheck = (new XiboPlaylist($this->getEntityProvider()))->hydrate($this->getEntityProvider()->get('/playlist', ['playlistId' => $object->id])[0]);
$this->assertEquals($name, $playlistCheck->name, 'Names are not identical');
}
/**
* @dataProvider addPlaylistFailureCases
*/
public function testAddPlaylistFailure($statusCode, $name, $tags, $isDynamic, $nameFilter, $tagFilter)
{
$response = $this->sendRequest('POST', '/playlist', [
'name' => $name,
'tags' => $tags,
'isDynamic' => $isDynamic,
'filterMediaName' => $nameFilter,
'filterMediaTag' => $tagFilter
]);
// Check the response headers
$this->assertSame($statusCode, $response->getStatusCode(), 'Not successful: ' . $response->getStatusCode() . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, 'Missing data');
$this->assertSame([], $object->data);
$this->assertObjectHasAttribute('error', $object, 'Missing error');
$this->assertObjectNotHasAttribute('id', $object);
}
/**
* Edit test
*/
public function testEditPlaylist()
{
// New name
$newName = Random::generateString(5, 'playlist');
// Take the duplicate name playlist, and edit it
$response = $this->sendRequest('PUT','/playlist/' . $this->duplicateName->playlistId, [
'name' => $newName,
'tags' => null,
'isDynamic' => 0,
'nameFilter' => null,
'tagFilter' => null
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
// Check the response headers
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getStatusCode() . $response->getBody());
/** @var XiboPlaylist $playlistCheck */
$playlistCheck = (new XiboPlaylist($this->getEntityProvider()))->hydrate($this->getEntityProvider()->get('/playlist', ['playlistId' => $this->duplicateName->playlistId])[0]);
$this->assertEquals($newName, $playlistCheck->name, 'Names are not identical');
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\Tests\LocalWebTestCase;
/**
* Class PlaylistTest
* @package Xibo\Tests\Integration
*/
class PlaylistWidgetListTest extends LocalWebTestCase
{
/** @var XiboPlaylist */
private $playlist;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
// Create a Playlist
$this->playlist = (new XiboPlaylist($this->getEntityProvider()))->hydrate($this->getEntityProvider()->post('/playlist', [
'name' => Random::generateString(5, 'playlist')
]));
// Assign some Widgets
$this->getEntityProvider()->post('/playlist/widget/clock/' . $this->playlist->playlistId, [
'duration' => 100,
'useDuration' => 1
]);
$text = $this->getEntityProvider()->post('/playlist/widget/text/' . $this->playlist->playlistId);
$this->getEntityProvider()->put('/playlist/widget/' . $text['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Playlist
parent::tearDown();
}
/**
* List all items in playlist
*/
public function testGetWidget()
{
// Search widgets on our playlist
$response = $this->sendRequest('GET','/playlist/widget', [
'playlistId' => $this->playlist->playlistId
]);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(2, $object->data->recordsTotal);
}
}

View File

@@ -0,0 +1,295 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboImage;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboLibrary;
use Xibo\OAuth2\Client\Entity\XiboPlaylist;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ProofOfPlayOnOff
* @package Xibo\Tests\Integration
*/
class ProofOfPlayOnOff extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboLayout */
protected $layout2;
/** @var XiboLibrary */
protected $media;
/** @var int */
protected $widgetId;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// get draft Layout
$layout = $this->getDraft($this->layout);
// Create another Layout with stat enabled
$this->layout2 = (new XiboLayout($this->getEntityProvider()))->create(
Random::generateString(8, 'phpunit'),
'phpunit layout',
'',
$this->getResolutionId('landscape'),
1
);
// Upload some media
$this->media = (new XiboLibrary($this->getEntityProvider()))->create('API video '.rand(1,400), PROJECT_ROOT . '/tests/resources/HLH264.mp4');
// Assign the media we've created to our regions playlist.
$playlist = (new XiboPlaylist($this->getEntityProvider()))->assign([$this->media->mediaId], 10, $layout->regions[0]->regionPlaylist->playlistId);
// Store the widgetId
$this->widgetId = $playlist->widgets[0]->widgetId;
$this->getLogger()->debug('Finished Setup');
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
$this->getLogger()->debug('Tear down for ' . get_class($this) . ' Test');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the second Layout we've been working with
$this->deleteLayout($this->layout2);
// Delete the media record
$this->media->deleteAssigned();
}
/**
* Each array is a test run
* Format (enableStat)
* @return array
*/
public function enableStatLayoutCases()
{
return [
// various correct enableStat flag
'Layout enableStat Off' => [0],
'Layout enableStat On' => [1]
];
}
/**
* Each array is a test run
* Format (enableStat)
* @return array
*/
public function enableStatMediaAndWidgetCases()
{
return [
// various correct enableStat options - for both media and widget are same
'enableStat Off' => ['Off'],
'enableStat On' => ['On'],
'enableStat Inherit' => ['Inherit']
];
}
/**
* Add enableStat flag was set to 0 when creating the layout
*/
public function testAddLayoutEnableStatOff()
{
// Check that the layout enable stat sets to off
$layout = (new XiboLayout($this->getEntityProvider()))->getById($this->layout->layoutId);
$this->assertSame(0, $layout->enableStat);
}
/**
* Add enableStat flag was set to 1 when creating the layout
*/
public function testAddLayoutEnableStatOn()
{
// Check that the layout enable stat sets to on
$layout = (new XiboLayout($this->getEntityProvider()))->getById($this->layout2->layoutId);
$this->assertSame(1, $layout->enableStat);
}
/**
* Edit enableStat flag of an existing layout
* @dataProvider enableStatLayoutCases
*/
public function testEditLayoutEnableStat($enableStat)
{
$name = Random::generateString(8, 'phpunit');
$description = Random::generateString(8, 'description');
$response = $this->sendRequest('PUT','/layout/' . $this->layout->layoutId, [
'name' => $name,
'description' => $description,
'enableStat' => $enableStat
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame($enableStat, $object->data->enableStat);
// Check that the layout enable stat sets to on/off
$layout = (new XiboLayout($this->getEntityProvider()))->getById($object->id);
$this->assertSame($enableStat, $layout->enableStat);
}
/**
* Edit enableStat flag of an existing media file
* @dataProvider enableStatMediaAndWidgetCases
*/
public function testEditMediaEnableStat($enableStat)
{
$name = Random::generateString(8, 'phpunit');
// Edit media file
$response = $this->sendRequest('PUT','/library/' . $this->media->mediaId, [
'name' => $name,
'duration' => 50,
'updateInLayouts' => 1,
'enableStat' => $enableStat
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame($enableStat, $object->data->enableStat);
$media = (new XiboLibrary($this->getEntityProvider()))->getById($this->media->mediaId);
$this->assertSame($enableStat, $media->enableStat);
}
/**
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException
* @dataProvider enableStatMediaAndWidgetCases
*/
public function testEditWidgetEnableStat($enableStat)
{
// Now try to edit our assigned Media Item.
$response = $this->sendRequest('PUT','/playlist/widget/' . $this->widgetId, [
'enableStat' => $enableStat,
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']
);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
/** @var XiboImage $widgetOptions */
$response = $this->getEntityProvider()->get('/playlist/widget', ['widgetId' => $this->widgetId]);
$widgetOptions = (new XiboImage($this->getEntityProvider()))->hydrate($response[0]);
foreach ($widgetOptions->widgetOptions as $option) {
if ($option['option'] == 'enableStat') {
$this->assertSame($enableStat, $option['value']);
}
}
}
/**
* Copy Layout - enableStat flag copied from an existing layout
*/
public function testCopyLayoutCheckEnableStat()
{
// Generate new random name
$nameCopy = Random::generateString(8, 'phpunit');
// Call copy
$response = $this->sendRequest('POST','/layout/copy/' . $this->layout2->layoutId, [
'name' => $nameCopy,
'description' => 'Copy',
'copyMediaFiles' => 1
], [
'CONTENT_TYPE' => 'application/x-www-form-urlencoded'
]);
$this->assertSame(200, $response->getStatusCode());
// Check if copied layout has enableStat flag of copying layout
$object = json_decode($response->getBody());
$this->assertSame($this->layout2->enableStat, $object->data->enableStat);
}
/**
* Bulk On/Off Layout enableStat
* @dataProvider enableStatLayoutCases
*/
public function testLayoutBulkEnableStat($enableStat)
{
// Call Set enable stat
$response = $this->sendRequest('PUT','/layout/setenablestat/' . $this->layout->layoutId, [
'enableStat' => $enableStat
], [
'CONTENT_TYPE' => 'application/x-www-form-urlencoded'
]);
$this->assertSame(200, $response->getStatusCode());
$layout = (new XiboLayout($this->getEntityProvider()))->getById($this->layout->layoutId);
$this->assertSame($enableStat, $layout->enableStat);
}
/**
* Bulk On/Off/Inherit Media enableStat
* @dataProvider enableStatMediaAndWidgetCases
*/
public function testMediaBulkEnableStat($enableStat)
{
// Call Set enable stat
$response = $this->sendRequest('PUT','/library/setenablestat/' . $this->media->mediaId, [
'enableStat' => $enableStat
], [
'CONTENT_TYPE' => 'application/x-www-form-urlencoded'
]);
$this->assertSame(200, $response->getStatusCode());
$media = (new XiboLibrary($this->getEntityProvider()))->getById($this->media->mediaId);
$this->assertSame($enableStat, $media->enableStat);
}
}

View File

@@ -0,0 +1,188 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ReportScheduleDataTest
* @package Xibo\Tests\Integration
*/
class ReportScheduleDataTest extends LocalWebTestCase
{
use LayoutHelperTrait, DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
// Stat type
private $type;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
// Create a Layout
$this->layout = $this->createLayout();
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetLicensed($this->display);
$this->type = 'layout';
$hardwareId = $this->display->license;
// Record some stats
$this->getXmdsWrapper()->SubmitStats(
$hardwareId,
'<stats>
<stat fromdt="'. Carbon::now()->startOfDay()->subDays(4)->format(DateFormatHelper::getSystemFormat()) . '"
todt="'.Carbon::now()->startOfDay()->subDays(3)->format(DateFormatHelper::getSystemFormat()) .'"
type="'.$this->type.'"
scheduleid="0"
layoutid="'.$this->layout->layoutId.'" />
<stat fromdt="'. Carbon::now()->startOfDay()->subDays(3)->format(DateFormatHelper::getSystemFormat()) . '"
todt="'.Carbon::now()->startOfDay()->subDays(2)->format(DateFormatHelper::getSystemFormat()) .'"
type="'.$this->type.'"
scheduleid="0"
layoutid="'.$this->layout->layoutId.'"/>
<stat fromdt="'. Carbon::now()->startOfDay()->subDays(2)->format(DateFormatHelper::getSystemFormat()) . '"
todt="'.Carbon::now()->startOfDay()->subDays()->format(DateFormatHelper::getSystemFormat()) .'"
type="'.$this->type.'"
scheduleid="0"
layoutid="'.$this->layout->layoutId.'"/>
<stat fromdt="'. Carbon::now()->startOfDay()->subDays()->format(DateFormatHelper::getSystemFormat()) . '"
todt="'.Carbon::now()->startOfDay()->format(DateFormatHelper::getSystemFormat()) .'"
type="'.$this->type.'"
scheduleid="0"
layoutid="'.$this->layout->layoutId.'" />
</stats>'
);
$this->getLogger()->debug('Finished Setup');
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete stat records
self::$container->get('timeSeriesStore')
->deleteStats(Carbon::now(), Carbon::now()->startOfDay()->subDays(10));
}
/**
* Check if proof of play statistics are correct
*/
public function testProofOfPlayReportYesterday()
{
$response = $this->sendRequest('GET', '/report/data/proofofplayReport', [
'reportFilter'=> 'yesterday',
'groupByFilter' => 'byday',
'displayId' => $this->display->displayId,
'layoutId' => [$this->layout->layoutId],
'type' => $this->type
], ['HTTP_ACCEPT'=>'application/json'], 'web', true);
$this->getLogger()->debug('Response code is: ' . $response->getStatusCode());
$body = $response->getBody();
$this->getLogger()->debug($body);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($body);
$object = json_decode($body);
$this->assertObjectHasAttribute('table', $object, $body);
$this->assertSame(1, $object->table[0]->numberPlays);
}
/**
* Check if proof of play statistics are correct for Proof of play Report
*/
public function testProofOfPlayReport()
{
$response = $this->sendRequest('GET', '/report/data/proofofplayReport', [
'statsFromDt' => Carbon::now()->startOfDay()->subDays(3)->format(DateFormatHelper::getSystemFormat()),
'statsToDt' => Carbon::now()->startOfDay()->subDays(2)->format(DateFormatHelper::getSystemFormat()),
'groupByFilter' => 'byday',
'displayId' => $this->display->displayId,
'layoutId' => [$this->layout->layoutId],
'type' => $this->type
], ['HTTP_ACCEPT'=>'application/json'], 'web', true);
$this->assertSame(200, $response->getStatusCode());
$body = $response->getBody();
$this->getLogger()->debug($body);
$this->assertNotEmpty($body);
$object = json_decode($body);
$this->assertObjectHasAttribute('table', $object, $response->getBody());
$this->assertSame(1, $object->table[0]->numberPlays);
}
/**
* Check if proof of play statistics are correct for Summary Report
*/
public function testSummaryReport()
{
$response = $this->sendRequest('GET', '/report/data/summaryReport', [
'statsFromDt' => Carbon::now()->startOfDay()->subDays(4)->format(DateFormatHelper::getSystemFormat()),
'statsToDt' => Carbon::now()->startOfDay()->format(DateFormatHelper::getSystemFormat()),
'groupByFilter' => 'byday',
'displayId' => $this->display->displayId,
'layoutId' => $this->layout->layoutId,
'type' => $this->type
], ['HTTP_ACCEPT'=>'application/json'], 'web', true);
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('chart', $object, $response->getBody());
$expectedSeconds = Carbon::now()->startOfDay()->subDays(3)->format('U') -
Carbon::now()->startOfDay()->subDays(4)->format('U');
$this->assertSame($expectedSeconds, $object->chart->data->datasets[0]->data[0]);
}
}

View File

@@ -0,0 +1,208 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboReportSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ReportScheduleTest
* @package Xibo\Tests\Integration
*/
class ReportScheduleTest extends LocalWebTestCase
{
use LayoutHelperTrait, DisplayHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
// Create a Layout
$this->layout = $this->createLayout();
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetLicensed($this->display);
$this->getLogger()->debug('Finished Setup');
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
/**
* Each array is a test run
* Format (filter, reportName)
* @return array
*/
public function filterCreateCases()
{
return [
'proofofplayReport Daily' => ['daily', 'proofofplayReport'],
'proofofplayReport Weekly' => ['weekly', 'proofofplayReport'],
'proofofplayReport Monthly' => ['monthly', 'proofofplayReport'],
'proofofplayReport Yearly' => ['yearly', 'proofofplayReport'],
'summaryReport Daily' => ['daily', 'summaryReport'],
'summaryReport Weekly' => ['weekly', 'summaryReport'],
'summaryReport Monthly' => ['monthly', 'summaryReport'],
'summaryReport Yearly' => ['yearly', 'summaryReport'],
'distributionReport Daily' => ['daily', 'distributionReport'],
'distributionReport Weekly' => ['weekly', 'distributionReport'],
'distributionReport Monthly' => ['monthly', 'distributionReport'],
'distributionReport Yearly' => ['yearly', 'distributionReport'],
];
}
/**
* Create Report Schedule
* @dataProvider filterCreateCases
*/
public function testCreateReportSchedule($filter, $report)
{
$reportSchedule = (new XiboReportSchedule($this->getEntityProvider()))
->create('Report Schedule', $report, $filter, 'byhour', null,
$this->display->displayId, '{"type":"layout","selectedId":'.$this->layout->layoutId.',"eventTag":null}');
$this->assertSame($report, $reportSchedule->reportName);
// Delete Report Schedule
$reportSchedule->delete();
}
/**
* Report Schedule Delete All Saved Report
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function testReportScheduleDeleteAllSavedReport()
{
$reportSchedule = (new XiboReportSchedule($this->getEntityProvider()))
->create('Report Schedule', 'proofofplayReport', 'daily');
$reportScheduleId = $reportSchedule->reportScheduleId;
$task = $this->getTask('\Xibo\XTR\ReportScheduleTask');
$task->run();
self::$container->get('store')->commitIfNecessary();
// Delete All Saved Report
$resDelete = $this->sendRequest('POST', '/report/reportschedule/' .
$reportScheduleId. '/deletesavedreport');
$this->assertSame(200, $resDelete->getStatusCode(), $resDelete->getBody());
// Delete Report Schedule
$reportSchedule->delete();
}
/**
* Report Schedule Toggle Active
*/
public function testReportScheduleToggleActive()
{
$reportSchedule = (new XiboReportSchedule($this->getEntityProvider()))
->create('Report Schedule', 'proofofplayReport', 'daily');
// Toggle Active
$response = $this->sendRequest('POST', '/report/reportschedule/'.
$reportSchedule->reportScheduleId.'/toggleactive');
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame('Paused Report Schedule', $object->message);
// Delete Report Schedule
$reportSchedule->delete();
}
/**
* Report Schedule Reset
*/
public function testReportScheduleReset()
{
$reportSchedule = (new XiboReportSchedule($this->getEntityProvider()))
->create('Report Schedule', 'proofofplayReport', 'daily');
// Reset
$response = $this->sendRequest('POST', '/report/reportschedule/'.
$reportSchedule->reportScheduleId.'/reset');
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$object = json_decode($response->getBody());
$this->assertSame('Success', $object->message);
// Delete Report Schedule
$reportSchedule->delete();
}
/**
* Delete Saved Report
* @throws \Xibo\OAuth2\Client\Exception\XiboApiException|\Xibo\Support\Exception\NotFoundException
*/
public function testDeleteSavedReport()
{
$reportSchedule = (new XiboReportSchedule($this->getEntityProvider()))
->create('Report Schedule', 'proofofplayReport', 'daily');
// Create a saved report
$task = $this->getTask('\Xibo\XTR\ReportScheduleTask');
$task->run();
self::$container->get('store')->commitIfNecessary();
// Get updated report schedule's last saved report Id
$rs = (new XiboReportSchedule($this->getEntityProvider()))
->getById($reportSchedule->reportScheduleId);
// Delete Saved Report
$response = $this->sendRequest('DELETE', '/report/savedreport/'.
$rs->lastSavedReportId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
// Delete Report Schedule
$rs->delete();
}
}

View File

@@ -0,0 +1,232 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboResolution;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ResolutionTest
* @package Xibo\Tests\Integration
*/
class ResolutionTest extends LocalWebTestCase
{
protected $startResolutions;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startResolutions = (new XiboResolution($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all resolutions that weren't there initially
$finalResolutions = (new XiboResolution($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining resolutions and nuke them
foreach ($finalResolutions as $resolution) {
/** @var XiboResolution $resolution */
$flag = true;
foreach ($this->startResolutions as $startRes) {
if ($startRes->resolutionId == $resolution->resolutionId) {
$flag = false;
}
}
if ($flag) {
try {
$resolution->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $resolution->resolutionId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
public function testListAll()
{
$response = $this->sendRequest('GET','/resolution');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
}
/**
* testAddSuccess - test adding various Resolutions that should be valid
* @dataProvider provideSuccessCases
* @group minimal
*/
public function testAddSuccess($resolutionName, $resolutionWidth, $resolutionHeight)
{
# Loop through any pre-existing resolutions to make sure we're not
# going to get a clash
foreach ($this->startResolutions as $tmpRes) {
if ($tmpRes->resolution == $resolutionName) {
$this->skipTest("There is a pre-existing resolution with this name");
return;
}
}
# Create new resolutions with data from provideSuccessCases
$response = $this->sendRequest('POST','/resolution', [
'resolution' => $resolutionName,
'width' => $resolutionWidth,
'height' => $resolutionHeight
]);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($resolutionName, $object->data->resolution);
$this->assertSame($resolutionWidth, $object->data->width);
$this->assertSame($resolutionHeight, $object->data->height);
# Check that the resolution was added correctly
$resolution = (new XiboResolution($this->getEntityProvider()))->getById($object->id);
$this->assertSame($resolutionName, $resolution->resolution);
$this->assertSame($resolutionWidth, $resolution->width);
$this->assertSame($resolutionHeight, $resolution->height);
# Clean up the Resolutions as we no longer need it
$this->assertTrue($resolution->delete(), 'Unable to delete ' . $resolution->resolutionId);
}
/**
* Each array is a test run
* Format (resolution name, width, height)
* @return array
*/
public function provideSuccessCases()
{
# Sets of correct data, which should be successfully added
return [
'resolution 1' => ['test resolution', 800, 200],
'resolution 2' => ['different test resolution', 1069, 1699]
];
}
/**
* testAddFailure - test adding various resolutions that should be invalid
* @dataProvider provideFailureCases
*/
public function testAddFailure($resolutionName, $resolutionWidth, $resolutionHeight)
{
# create new resolution with data from provideFailureCases
$response = $this->sendRequest('POST','/resolution', [
'resolution' => $resolutionName,
'width' => $resolutionWidth,
'height' => $resolutionHeight
]);
# Check if it fails as expected
$this->assertSame(422, $response->getStatusCode(), 'Expecting failure, received ' . $response->getStatusCode());
}
/**
* Each array is a test run
* Format (resolution name, width, height)
* @return array
*/
public function provideFailureCases()
{
# Sets of incorrect data, which should lead to a failure
return [
'incorrect width and height' => ['wrong parameters', 'abc', NULL],
'incorrect width' => [12, 'width', 1699]
];
}
/**
* Edit an existing resolution
* @group minimal
*/
public function testEdit()
{
# Load in a known resolution
/** @var XiboResolution $resolution */
$resolution = (new XiboResolution($this->getEntityProvider()))->create('phpunit resolution', 1200, 860);
$newWidth = 2400;
# Change the resolution name, width and enable flag
$name = Random::generateString(8, 'phpunit');
$response = $this->sendRequest('PUT','/resolution/' . $resolution->resolutionId, [
'resolution' => $name,
'width' => $newWidth,
'height' => $resolution->height,
'enabled' => 0
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
# Examine the returned object and check that it's what we expect
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$this->assertSame($name, $object->data->resolution);
$this->assertSame($newWidth, $object->data->width);
$this->assertSame(0, $object->data->enabled);
# Check that the resolution was actually renamed
$resolution = (new XiboResolution($this->getEntityProvider()))->getById($object->id);
$this->assertSame($name, $resolution->resolution);
$this->assertSame($newWidth, $resolution->width);
# Clean up the resolution as we no longer need it
$resolution->delete();
}
/**
* Test delete
* @group minimal
*/
public function testDelete()
{
# Generate two random names
$name1 = Random::generateString(8, 'phpunit');
$name2 = Random::generateString(8, 'phpunit');
# Load in a couple of known resolutions
$res1 = (new XiboResolution($this->getEntityProvider()))->create($name1, 1000, 500);
$res2 = (new XiboResolution($this->getEntityProvider()))->create($name2, 2000, 760);
# Delete the one we created last
$response = $this->sendRequest('DELETE','/resolution/' . $res2->resolutionId);
# This should return 204 for success
$object = json_decode($response->getBody());
$this->assertSame(204, $object->status, $response->getBody());
# Check only one remains
$resolutions = (new XiboResolution($this->getEntityProvider()))->get();
$this->assertEquals(count($this->startResolutions) + 1, count($resolutions));
$flag = false;
foreach ($resolutions as $res) {
if ($res->resolutionId == $res1->resolutionId) {
$flag = true;
}
}
$this->assertTrue($flag, 'Resolution ID ' . $res1->resolutionId . ' was not found after deleting a different Resolution');
# Clean up
$res1->delete();
}
}

View File

@@ -0,0 +1,188 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\OAuth2\Client\Entity\XiboDaypart;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
class ScheduleDayPartTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $layout;
/** @var \Xibo\OAuth2\Client\Entity\XiboDisplay */
protected $display;
/** @var \Xibo\OAuth2\Client\Entity\XiboSchedule */
protected $event;
/** @var \Xibo\OAuth2\Client\Entity\XiboDaypart */
protected $dayPart;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Make sure the Layout Status is as we expect
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Create a Day Part
// calculate a few hours either side of now
// must be tomorrow
// must not cross the day boundary
$now = Carbon::now()->startOfDay()->addDay()->addHour();
$this->dayPart = (new XiboDaypart($this->getEntityProvider()))->create(
Random::generateString(5),
'',
$now->format('H:i'),
$now->copy()->addHours(5)->format('H:i')
);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
// Delete the DayPart
$this->dayPart->delete();
}
// </editor-fold>
public function testSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my player timezone
$date = Carbon::now()->addDay()->setTime(0,0,0);
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()));
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'dayPartId' => $this->dayPart->dayPartId,
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'scheduleRecurrenceType' => null,
'scheduleRecurrenceDetail' => null,
'scheduleRecurrenceRange' => null,
'syncTimezone' => 0
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
$this->assertTrue(count($layouts) == 1, 'Unexpected number of events');
foreach ($layouts as $layout) {
$xmlFromDt = $layout->getAttribute('fromdt');
$xmlToDt = $layout->getAttribute('todt');
$this->assertEquals($date->format('Y-m-d') . ' ' . $this->dayPart->startTime . ':00', $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
$this->assertEquals($date->format('Y-m-d') . ' ' . $this->dayPart->endTime . ':00', $xmlToDt, 'To date doesnt match: ' . $xmlToDt);
}
// Also check this layout is in required files.
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->RequiredFiles($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Find using XPATH
$xpath = new \DOMXPath($xml);
$nodes =$xpath->query('//file[@type="layout"]');
$this->assertGreaterThanOrEqual(1, $nodes->count(), 'Layout not in required files');
$found = false;
foreach ($nodes as $node) {
/** @var \DOMNode $node */
if ($this->layout->layoutId == $node->attributes->getNamedItem('id')->nodeValue) {
$found = true;
break;
}
}
if (!$found) {
$this->fail('Layout not found in Required Files XML');
}
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboDisplay;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ScheduleDisplayDeleteTest
* @package Xibo\Tests\Integration
*/
class ScheduleDisplayDeleteTest extends LocalWebTestCase
{
use DisplayHelperTrait;
use LayoutHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboDisplay */
protected $display;
/** @var XiboDisplay */
protected $display2;
/** @var XiboSchedule */
protected $event;
// <editor-fold desc="Init">
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
// We need 2 displays
$this->display = $this->createDisplay();
$this->display2 = $this->createDisplay();
// This is the remaining display we will test for the schedule
$this->displaySetLicensed($this->display2);
// 1 Layout
$this->layout = $this->createLayout();
// 1 Schedule
$this->event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$this->layout->campaignId,
[$this->display->displayGroupId, $this->display2->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// Delete the Layout and Remaining Display
$this->deleteDisplay($this->display2);
$this->deleteLayout($this->layout);
parent::tearDown();
}
//</editor-fold>
/**
* Do the test
*/
public function test()
{
// Delete 1 display
$this->sendRequest('DELETE','/display/' . $this->display->displayId);
// Test to ensure the schedule remains
$schedule = $this->getXmdsWrapper()->Schedule($this->display2->license);
$this->assertContains('file="' . $this->layout->layoutId . '"', $schedule, 'Layout not scheduled');
}
}

View File

@@ -0,0 +1,246 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ScheduleNotificationTest
* @package Xibo\Tests\Integration
*/
class ScheduleNotificationTest extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $layout;
/** @var \Xibo\OAuth2\Client\Entity\XiboDisplay */
protected $display;
/** @var \Xibo\OAuth2\Client\Entity\XiboSchedule */
protected $event;
protected $timeZone = 'Asia/Hong_Kong';
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetTimezone($this->display, $this->timeZone);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Make sure the Layout Status is as we expect
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Check our timzone is set correctly
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->RegisterDisplay($this->display->license, $this->timeZone));
$this->assertEquals($this->timeZone, $xml->documentElement->getAttribute('localTimezone'), 'Timezone not correct');
$xml = null;
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
public function testSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my player timezone
$localNow = Carbon::now()->setTimezone($this->timeZone);
$date = $localNow->copy()->addHour()->startOfHour();
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()));
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'toDt' => $date->copy()->addMinutes(30)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'recurrenceType' => null,
'recurrenceDetail' => null,
'recurrenceRange' => null,
'syncTimezone' => 0,
'scheduleReminders' => [
[
'reminder_value' => 1,
'reminder_type' => 1,
'reminder_option' => 1,
'reminder_isEmailHidden' => 1
],
[
'reminder_value' => 1,
'reminder_type' => 1,
'reminder_option' => 2,
'reminder_isEmailHidden' => 1
]
],
'embed' => 'scheduleReminders'
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if two reminders are created
$this->assertSame(2, count($object->data->scheduleReminders));
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check the filter from and to dates are correct
$this->assertEquals($localNow->startOfHour()->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterFrom'), 'Filter from date incorrect');
$this->assertEquals($localNow->addDays(2)->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterTo'), 'Filter to date incorrect');
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
$this->assertTrue(count($layouts) == 1, 'Unexpected number of events');
foreach ($layouts as $layout) {
$xmlFromDt = $layout->getAttribute('fromdt');
$this->assertEquals($date->format(DateFormatHelper::getSystemFormat()), $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
}
}
public function testRecurringSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my player timezone
// we start this schedule the day before
$localNow = Carbon::now()->setTimezone($this->timeZone);
$date = $localNow->copy()->subDay()->addHour()->startOfHour();
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()));
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'toDt' => $date->copy()->addMinutes(30)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'recurrenceType' => 'Day',
'recurrenceDetail' => 1,
'recurrenceRange' => null,
'syncTimezone' => 0,
'scheduleReminders' => [
[
'reminder_value' => 1,
'reminder_type' => 1,
'reminder_option' => 1,
'reminder_isEmailHidden' => 1
],
[
'reminder_value' => 1,
'reminder_type' => 1,
'reminder_option' => 2,
'reminder_isEmailHidden' => 1
]
],
'embed' => 'scheduleReminders'
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if two reminders are created
$this->assertSame(2, count($object->data->scheduleReminders));
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check the filter from and to dates are correct
$this->assertEquals($localNow->startOfHour()->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterFrom'), 'Filter from date incorrect');
$this->assertEquals($localNow->addDays(2)->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterTo'), 'Filter to date incorrect');
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
foreach ($layouts as $layout) {
// Move our day on (we know we're recurring by day), and that we started a day behind
$date->addDay();
$xmlFromDt = $layout->getAttribute('fromdt');
$this->assertEquals($date->format(DateFormatHelper::getSystemFormat()), $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
}
}
}

View File

@@ -0,0 +1,404 @@
<?php
/**
* Copyright (C) 2020 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\Integration;
use Carbon\Carbon;
use Xibo\Helper\DateFormatHelper;
use Xibo\OAuth2\Client\Entity\XiboCampaign;
use Xibo\OAuth2\Client\Entity\XiboCommand;
use Xibo\OAuth2\Client\Entity\XiboDisplayGroup;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\OAuth2\Client\Entity\XiboSchedule;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ScheduleTest
* @package Xibo\Tests\Integration
*/
class ScheduleTest extends LocalWebTestCase
{
use LayoutHelperTrait;
protected $route = '/schedule';
protected $startCommands;
protected $startDisplayGroups;
protected $startEvents;
protected $startLayouts;
protected $startCampaigns;
/**
* setUp - called before every test automatically
*/
public function setup()
{
parent::setup();
$this->startDisplayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startCampaigns = (new XiboCampaign($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
$this->startCommands = (new XiboCommand($this->getEntityProvider()))->get(['start' => 0, 'length' => 1000]);
}
/**
* tearDown - called after every test automatically
*/
public function tearDown()
{
// tearDown all display groups that weren't there initially
$finalDisplayGroups = (new XiboDisplayGroup($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining display groups and nuke them
foreach ($finalDisplayGroups as $displayGroup) {
/** @var XiboDisplayGroup $displayGroup */
$flag = true;
foreach ($this->startDisplayGroups as $startGroup) {
if ($startGroup->displayGroupId == $displayGroup->displayGroupId) {
$flag = false;
}
}
if ($flag) {
try {
$displayGroup->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Unable to delete ' . $displayGroup->displayGroupId . '. E:' . $e->getMessage());
}
}
}
// tearDown all layouts that weren't there initially
$finalLayouts = (new XiboLayout($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining layouts and nuke them
foreach ($finalLayouts as $layout) {
/** @var XiboLayout $layout */
$flag = true;
foreach ($this->startLayouts as $startLayout) {
if ($startLayout->layoutId == $layout->layoutId) {
$flag = false;
}
}
if ($flag) {
try {
$layout->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Layout: Unable to delete ' . $layout->layoutId . '. E:' . $e->getMessage());
}
}
}
// tearDown all campaigns that weren't there initially
$finalCamapigns = (new XiboCampaign($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining campaigns and nuke them
foreach ($finalCamapigns as $campaign) {
/** @var XiboCampaign $campaign */
$flag = true;
foreach ($this->startCampaigns as $startCampaign) {
if ($startCampaign->campaignId == $campaign->campaignId) {
$flag = false;
}
}
if ($flag) {
try {
$campaign->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Campaign: Unable to delete ' . $campaign->campaignId . '. E:' . $e->getMessage());
}
}
}
// tearDown all commands that weren't there initially
$finalCommands = (new XiboCommand($this->getEntityProvider()))->get(['start' => 0, 'length' => 10000]);
# Loop over any remaining commands and nuke them
foreach ($finalCommands as $command) {
/** @var XiboCommand $command */
$flag = true;
foreach ($this->startCommands as $startCom) {
if ($startCom->commandId == $command->commandId) {
$flag = false;
}
}
if ($flag) {
try {
$command->delete();
} catch (\Exception $e) {
fwrite(STDERR, 'Command: Unable to delete ' . $command->commandId . '. E:' . $e->getMessage());
}
}
}
parent::tearDown();
}
/**
* testListAll
*/
public function testListAll()
{
# list all scheduled events
$response = $this->sendRequest('GET','/schedule/data/events');
$this->assertSame(200, $response->getStatusCode());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('result', $object, $response->getBody());
}
/**
* @group add
* @dataProvider provideSuccessCasesCampaign
*/
public function testAddEventCampaign($isCampaign, $scheduleFrom, $scheduleTo, $scheduledayPartId, $scheduleRecurrenceType, $scheduleRecurrenceDetail, $scheduleRecurrenceRange, $scheduleOrder, $scheduleIsPriority)
{
# Create new display group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
$layout = null;
$campaign = null;
# isCampaign checks if we want to add campaign or layout to our event
if ($isCampaign) {
# Create Campaign
/* @var XiboCampaign $campaign */
$campaign = (new XiboCampaign($this->getEntityProvider()))->create('phpunit');
# Create new event with data from provideSuccessCasesCampaign where isCampaign is set to true
$response = $this->sendRequest('POST', $this->route, [
'fromDt' => Carbon::createFromTimestamp($scheduleFrom)->format(DateFormatHelper::getSystemFormat()),
'toDt' => Carbon::createFromTimestamp($scheduleTo)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $campaign->campaignId,
'displayGroupIds' => [$displayGroup->displayGroupId],
'displayOrder' => $scheduleOrder,
'isPriority' => $scheduleIsPriority,
'scheduleRecurrenceType' => $scheduleRecurrenceType,
'scheduleRecurrenceDetail' => $scheduleRecurrenceDetail,
'scheduleRecurrenceRange' => $scheduleRecurrenceRange
]);
} else {
# Create layout
$layout = $this->createLayout();
# Create new event with data from provideSuccessCasesCampaign where isCampaign is set to false
$response = $this->sendRequest('POST', $this->route, [
'fromDt' => Carbon::createFromTimestamp($scheduleFrom)->format(DateFormatHelper::getSystemFormat()),
'toDt' => Carbon::createFromTimestamp($scheduleTo)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $layout->campaignId,
'displayGroupIds' => [$displayGroup->displayGroupId],
'displayOrder' => $scheduleOrder,
'isPriority' => $scheduleIsPriority,
'scheduleRecurrenceType' => $scheduleRecurrenceType,
'scheduleRecurrenceDetail' => $scheduleRecurrenceDetail,
'scheduleRecurrenceRange' => $scheduleRecurrenceRange
]);
}
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Clean up
$displayGroup->delete();
if ($campaign != null)
$campaign->delete();
if ($layout != null)
$layout->delete();
}
/**
* Each array is a test run
* Format ($isCampaign, $scheduleFrom, $scheduleTo, $scheduledayPartId, $scheduleRecurrenceType, $scheduleRecurrenceDetail, $scheduleRecurrenceRange, $scheduleOrder, $scheduleIsPriority)
* @return array
*/
public function provideSuccessCasesCampaign()
{
# Sets of data used in testAddEventCampaign, first argument (isCampaign) controls if it's layout or campaign
return [
'Campaign no priority, no recurrence' => [true, time()+3600, time()+7200, 0, NULL, NULL, NULL, 0, 0],
'Layout no priority, no recurrence' => [false, time()+3600, time()+7200, 0, NULL, NULL, NULL, 0, 0]
];
}
/**
* @group add
* @dataProvider provideSuccessCasesCommand
*/
public function testAddEventCommand($scheduleFrom, $scheduledayPartId, $scheduleRecurrenceType, $scheduleRecurrenceDetail, $scheduleRecurrenceRange, $scheduleOrder, $scheduleIsPriority)
{
# Create command
$command = (new XiboCommand($this->getEntityProvider()))->create('phpunit command', 'phpunit command desc', 'code');
# Create Display Group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
# Create new event with scheduled command and data from provideSuccessCasesCommand
$response = $this->sendRequest('POST', $this->route, [
'fromDt' => Carbon::createFromTimestamp($scheduleFrom)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 2,
'commandId' => $command->commandId,
'displayGroupIds' => [$displayGroup->displayGroupId],
'displayOrder' => $scheduleOrder,
'isPriority' => $scheduleIsPriority,
'scheduleRecurrenceType' => $scheduleRecurrenceType,
'scheduleRecurrenceDetail' => $scheduleRecurrenceDetail,
'scheduleRecurrenceRange' => $scheduleRecurrenceRange
]);
# Check if successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Clean up
$displayGroup->delete();
$command->delete();
}
/**
* Each array is a test run
* Format ($scheduleFrom, $scheduleDisplays, $scheduledayPartId, $scheduleRecurrenceType, $scheduleRecurrenceDetail, $scheduleRecurrenceRange, $scheduleOrder, $scheduleIsPriority)
* @return array
*/
public function provideSuccessCasesCommand()
{
return [
'Command no priority, no recurrence' => [time()+3600, 0, NULL, NULL, NULL, 0, 0],
];
}
/**
* @group add
* @dataProvider provideSuccessCasesOverlay
*/
public function testAddEventOverlay($scheduleFrom, $scheduleTo, $scheduleCampaignId, $scheduleDisplays, $scheduledayPartId, $scheduleRecurrenceType, $scheduleRecurrenceDetail, $scheduleRecurrenceRange, $scheduleOrder, $scheduleIsPriority)
{
# Create new dispay group
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
# Create layout
$layout = $this->createLayout();
# Create new event with data from provideSuccessCasesOverlay
$response = $this->sendRequest('POST', $this->route, [
'fromDt' => Carbon::createFromTimestamp($scheduleFrom)->format(DateFormatHelper::getSystemFormat()),
'toDt' => Carbon::createFromTimestamp($scheduleTo)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 3,
'campaignId' => $layout->campaignId,
'displayGroupIds' => [$displayGroup->displayGroupId],
'displayOrder' => $scheduleOrder,
'isPriority' => $scheduleIsPriority,
'scheduleRecurrenceType' => $scheduleRecurrenceType,
'scheduleRecurrenceDetail' => $scheduleRecurrenceDetail,
'scheduleRecurrenceRange' => $scheduleRecurrenceRange
]);
# Check if call was successful
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Clean up
$displayGroup->delete();
if ($layout != null)
$layout->delete();
}
/**
* Each array is a test run
* Format ($scheduleFrom, $scheduleTo, $scheduledayPartId, $scheduleRecurrenceType, $scheduleRecurrenceDetail, $scheduleRecurrenceRange, $scheduleOrder, $scheduleIsPriority)
* @return array
*/
public function provideSuccessCasesOverlay()
{
return [
'Overlay, no recurrence' => [time()+3600, time()+7200, 0, NULL, NULL, NULL, 0, 0, 0, 0],
];
}
/**
* @group minimal
*/
public function testEdit()
{
// Get a Display Group Id
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
// Create Campaign
/* @var XiboCampaign $campaign */
$campaign = (new XiboCampaign($this->getEntityProvider()))->create('phpunit');
# Create new event
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$campaign->campaignId,
[$displayGroup->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
$fromDt = time() + 3600;
$toDt = time() + 86400;
# Edit event
$response = $this->sendRequest('PUT',$this->route . '/' . $event->eventId, [
'fromDt' => Carbon::createFromTimestamp($fromDt)->format(DateFormatHelper::getSystemFormat()),
'toDt' => Carbon::createFromTimestamp($toDt)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $event->campaignId,
'displayGroupIds' => [$displayGroup->displayGroupId],
'displayOrder' => 1,
'isPriority' => 1
], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']);
$this->assertSame(200, $response->getStatusCode(), "Not successful: " . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
# Check if edit was successful
$this->assertSame($toDt, intval($object->data->toDt));
$this->assertSame($fromDt, intval($object->data->fromDt));
# Tidy up
$displayGroup->delete();
$campaign->delete();
}
/**
* @param $eventId
*/
public function testDelete()
{
# Get a Display Group Id
$displayGroup = (new XiboDisplayGroup($this->getEntityProvider()))->create('phpunit group', 'phpunit description', 0, '');
# Create Campaign
/* @var XiboCampaign $campaign */
$campaign = (new XiboCampaign($this->getEntityProvider()))->create('phpunit');
# Create event
$event = (new XiboSchedule($this->getEntityProvider()))->createEventLayout(
Carbon::now()->format(DateFormatHelper::getSystemFormat()),
Carbon::now()->addSeconds(7200)->format(DateFormatHelper::getSystemFormat()),
$campaign->campaignId,
[$displayGroup->displayGroupId],
0,
NULL,
NULL,
NULL,
0,
0,
0
);
# Delete event
$response = $this->sendRequest('DELETE',$this->route . '/' . $event->eventId);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
# Clean up
$displayGroup->delete();
$campaign->delete();
}
}

View File

@@ -0,0 +1,311 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Carbon\Carbon;
use Xibo\Entity\Display;
use Xibo\Helper\DateFormatHelper;
use Xibo\Tests\Helper\DisplayHelperTrait;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class ScheduleTimezoneTest
* @package Xibo\Tests\integration
*/
class ScheduleTimezoneBaseCase extends LocalWebTestCase
{
use LayoutHelperTrait;
use DisplayHelperTrait;
/** @var \Xibo\OAuth2\Client\Entity\XiboLayout */
protected $layout;
/** @var \Xibo\OAuth2\Client\Entity\XiboDisplay */
protected $display;
/** @var \Xibo\OAuth2\Client\Entity\XiboSchedule */
protected $event;
protected $timeZone = 'Asia/Hong_Kong';
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) .' Test');
// Create a Layout
$this->layout = $this->createLayout();
// Checkout
$layout = $this->getDraft($this->layout);
$response = $this->getEntityProvider()->post('/playlist/widget/text/' . $layout->regions[0]->regionPlaylist->playlistId);
$response = $this->getEntityProvider()->put('/playlist/widget/' . $response['widgetId'], [
'text' => 'Widget A',
'duration' => 100,
'useDuration' => 1
]);
// Check us in again
$this->layout = $this->publish($this->layout);
// Build the layout
$this->buildLayout($this->layout);
// Create a Display
$this->display = $this->createDisplay();
$this->displaySetTimezone($this->display, $this->timeZone);
$this->displaySetStatus($this->display, Display::$STATUS_DONE);
$this->displaySetLicensed($this->display);
// Make sure the Layout Status is as we expect
$this->assertTrue($this->layoutStatusEquals($this->layout, 1), 'Layout Status isnt as expected');
// Make sure our Display is already DONE
$this->assertTrue($this->displayStatusEquals($this->display, Display::$STATUS_DONE), 'Display Status isnt as expected');
// Check our timzone is set correctly
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->RegisterDisplay($this->display->license, $this->timeZone));
$this->assertEquals($this->timeZone, $xml->documentElement->getAttribute('localTimezone'), 'Timezone not correct');
$xml = null;
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
parent::tearDown();
// Delete the Layout we've been working with
$this->deleteLayout($this->layout);
// Delete the Display
$this->deleteDisplay($this->display);
}
// </editor-fold>
public function testSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my player timezone
$localNow = Carbon::now()->setTimezone($this->timeZone);
$date = $localNow->copy()->addHour()->startOfHour();
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()));
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'toDt' => $date->copy()->addMinutes(30)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'scheduleRecurrenceType' => null,
'scheduleRecurrenceDetail' => null,
'scheduleRecurrenceRange' => null,
'syncTimezone' => 0
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check the filter from and to dates are correct
$this->assertEquals($localNow->startOfHour()->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterFrom'), 'Filter from date incorrect');
$this->assertEquals($localNow->addDays(2)->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterTo'), 'Filter to date incorrect');
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
$this->assertTrue(count($layouts) == 1, 'Unexpected number of events');
foreach ($layouts as $layout) {
$xmlFromDt = $layout->getAttribute('fromdt');
$this->assertEquals($date->format(DateFormatHelper::getSystemFormat()), $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
}
}
public function testRecurringSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my player timezone
// we start this schedule the day before
$localNow = Carbon::now()->setTimezone($this->timeZone);
$date = $localNow->copy()->subDay()->addHour()->startOfHour();
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()));
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'toDt' => $date->copy()->addMinutes(30)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'recurrenceType' => 'Day',
'recurrenceDetail' => 1,
'recurrenceRange' => null,
'syncTimezone' => 0
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check the filter from and to dates are correct
$this->assertEquals($localNow->startOfHour()->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterFrom'), 'Filter from date incorrect');
$this->assertEquals($localNow->addDays(2)->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterTo'), 'Filter to date incorrect');
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
foreach ($layouts as $layout) {
// Move our day on (we know we're recurring by day), and that we started a day behind
$date->addDay();
$xmlFromDt = $layout->getAttribute('fromdt');
$this->assertEquals($date->format(DateFormatHelper::getSystemFormat()), $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
}
}
public function testSyncedSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my CMS timezone
$localNow = Carbon::now()->setTimezone($this->timeZone);
// If this was 8AM local CMS time, we would expect the resulting date/times in the XML to have the equivilent
// timezone specific date/times
$date = Carbon::now()->copy()->addHour()->startOfHour();
$localDate = $date->copy()->timezone($this->timeZone);
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()) . ' which is ' . $localDate->format(DateFormatHelper::getSystemFormat()) . ' local time.');
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'toDt' => $date->copy()->addMinutes(30)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'recurrenceType' => null,
'recurrenceDetail' => null,
'recurrenceRange' => null,
'syncTimezone' => 1
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check the filter from and to dates are correct
$this->assertEquals($localNow->startOfHour()->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterFrom'), 'Filter from date incorrect');
$this->assertEquals($localNow->addDays(2)->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterTo'), 'Filter to date incorrect');
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
foreach ($layouts as $layout) {
$xmlFromDt = $layout->getAttribute('fromdt');
$this->assertEquals($localDate->format(DateFormatHelper::getSystemFormat()), $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
}
}
public function testSyncedRecurringSchedule()
{
// Our CMS is in GMT
// Create a schedule one hours time in my CMS timezone
$localNow = Carbon::now()->setTimezone($this->timeZone);
// If this was 8AM local CMS time, we would expect the resulting date/times in the XML to have the equivilent
// timezone specific date/times
$date = Carbon::now()->copy()->subDay()->addHour()->startOfHour();
$localDate = $date->copy()->timezone($this->timeZone);
$this->getLogger()->debug('Event start will be at: ' . $date->format(DateFormatHelper::getSystemFormat()) . ' which is ' . $localDate->format(DateFormatHelper::getSystemFormat()) . ' local time.');
$response = $this->sendRequest('POST','/schedule', [
'fromDt' => $date->format(DateFormatHelper::getSystemFormat()),
'toDt' => $date->copy()->addMinutes(30)->format(DateFormatHelper::getSystemFormat()),
'eventTypeId' => 1,
'campaignId' => $this->layout->campaignId,
'displayGroupIds' => [$this->display->displayGroupId],
'displayOrder' => 1,
'isPriority' => 0,
'recurrenceType' => 'Day',
'recurrenceDetail' => 1,
'recurrenceRange' => null,
'syncTimezone' => 1
]);
$this->assertSame(200, $response->getStatusCode(), 'Not successful: ' . $response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object);
$this->assertObjectHasAttribute('id', $object);
$xml = new \DOMDocument();
$xml->loadXML($this->getXmdsWrapper()->Schedule($this->display->license));
//$this->getLogger()->debug($xml->saveXML());
// Check the filter from and to dates are correct
$this->assertEquals($localNow->startOfHour()->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterFrom'), 'Filter from date incorrect');
$this->assertEquals($localNow->addDays(2)->format(DateFormatHelper::getSystemFormat()), $xml->documentElement->getAttribute('filterTo'), 'Filter to date incorrect');
// Check our event is present.
$layouts = $xml->getElementsByTagName('layout');
foreach ($layouts as $layout) {
// Move our day on (we know we're recurring by day), and that we started a day behind
// we use addRealDay because our synced calendar entry _should_ change its time over a DST switch
$localDate->addRealDay();
$xmlFromDt = $layout->getAttribute('fromdt');
$this->assertEquals($localDate->format(DateFormatHelper::getSystemFormat()), $xmlFromDt, 'From date doesnt match: ' . $xmlFromDt);
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Copyright (C) 2019 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
class ScheduleTimezoneHongKongTest extends ScheduleTimezoneBaseCase
{
protected $timeZone = 'Asia/Hong_Kong';
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Copyright (C) 2019 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
class ScheduleTimezoneLATest extends ScheduleTimezoneBaseCase
{
protected $timeZone = 'America/Los_Angeles';
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Copyright (C) 2019 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
class ScheduleTimezoneLondonTest extends ScheduleTimezoneBaseCase
{
protected $timeZone = 'Europe/London';
}

View File

@@ -0,0 +1,232 @@
<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Tests\integration;
use Xibo\OAuth2\Client\Entity\XiboLayout;
use Xibo\Tests\Helper\LayoutHelperTrait;
use Xibo\Tests\LocalWebTestCase;
/**
* Class SearchFilterTest
* @package Xibo\Tests\integration
*/
class SearchFilterTest extends LocalWebTestCase
{
use LayoutHelperTrait;
/** @var XiboLayout */
protected $layout;
/** @var XiboLayout */
protected $layout2;
/** @var XiboLayout */
protected $layout3;
/** @var XiboLayout */
protected $layout4;
/** @var XiboLayout */
protected $layout5;
/** @var XiboLayout */
protected $layout6;
// <editor-fold desc="Init">
public function setup()
{
parent::setup();
$this->getLogger()->debug('Setup test for ' . get_class($this) . ' Test');
// Create 6 layouts to test with
$this->layout = (new XiboLayout($this->getEntityProvider()))
->create(
'integration layout 1',
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->layout2 = (new XiboLayout($this->getEntityProvider()))
->create(
'integration example layout 2',
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->layout3 = (new XiboLayout($this->getEntityProvider()))
->create(
'integration layout 3',
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->layout4 = (new XiboLayout($this->getEntityProvider()))
->create(
'integration example 4',
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->layout5 = (new XiboLayout($this->getEntityProvider()))
->create(
'example layout 5',
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->layout6 = (new XiboLayout($this->getEntityProvider()))
->create(
'display different name',
'PHPUnit Created Layout for Automated Integration Testing',
'',
$this->getResolutionId('landscape')
);
$this->getLogger()->debug('Finished Setup');
}
public function tearDown()
{
$this->getLogger()->debug('Tear Down');
$this->deleteLayout($this->layout);
$this->deleteLayout($this->layout2);
$this->deleteLayout($this->layout3);
$this->deleteLayout($this->layout4);
$this->deleteLayout($this->layout5);
$this->deleteLayout($this->layout6);
parent::tearDown();
}
// </editor-fold>
/**
* Search filter test.
*
* Single keyword
*/
public function testSearch()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => 'integration']);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(4, $object->data->recordsFiltered);
}
/**
* Search filter test
*
* Comma separated
*/
public function testSearchCommaSeparated()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => 'integration,example']);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(5, $object->data->recordsFiltered);
}
/**
* Search filter test
*
* Comma separated with not RLIKE filter
*/
public function testSearchCommaSeparatedWithNotRlike()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => 'integration layout, -example']);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(4, $object->data->recordsFiltered);
}
/**
* Search filter test
*
* Comma separated with not RLIKE filter
*/
public function testSearchCommaSeparatedWithNotRlike2()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => 'example, -layout']);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(4, $object->data->recordsFiltered);
}
/**
* Search filter test.
*
* partial match filter
*/
public function testSearchPartialMatch()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => 'inte, exa']);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(5, $object->data->recordsFiltered);
}
/**
* Search filter test.
*
* using regexp
*/
public function testSearchWithRegEx()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => 'name$', 'useRegexForName' => 1]);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(1, $object->data->recordsFiltered);
}
/**
* Search filter test.
*
* using regexp
*/
public function testSearchWithRegEx2()
{
$response = $this->sendRequest('GET', '/layout', ['layout' => '^example, ^disp', 'useRegexForName' => 1]);
$this->assertSame(200, $response->getStatusCode(), $response->getBody());
$this->assertNotEmpty($response->getBody());
$object = json_decode($response->getBody());
$this->assertObjectHasAttribute('data', $object, $response->getBody());
$this->assertSame(2, $object->data->recordsFiltered);
}
}

Some files were not shown because too many files have changed in this diff Show More