Running Drupal tests on Appveyor and IIS.

1 81
Avatar for Beakerboy
3 years ago

The Drupal CMS is written in PHP, and typically runs on on Linux. The core software supports only three databases, MySql, PostgreSQL, and Sqlite. I maintain the Microsoft SQL Server driver, which is a user provided "contrib driver". Microsoft DOES have a version of SQL Server that runs on Linux, and most of my CI work has taken place with Travis-CI, but since a large number of users will be using Windows and IIS, I thought it would be a good lesson to ensure the test suite runs on Appveyor.

We will need 4 things to work together to have a working system

  • PHP

  • Drupal

  • SQL Server

  • IIS - Used in the Functional tests, and needed for one Unit test

PHP

Installing PHP on appveyor is trivially simple. Appveyor has a package manager called Chocolatey which fills the role that apt would have. choco install php will install the most recent version that fits your system. I used Chocolatey to install composer and urlrewrite as well.

PHP also requires a few extensions to work for our case. Drupal uses the php_yaml extension in some tests, and we need the pdo_sqlsrv extension to be able to talk with the database. These can be downloaded from windows.php.net. A problem that I discovered is that the site does not like people downloading files without handing over some agent headers, so we have to fake them. I used the following Powershell commands:

$cli = New-Object Net.WebClient
$cli.Headers['User-Agent'] = 'Appveyor';
$cli.DownloadFile('https://windows.php.net/downloads/pecl/releases/pdo_sqlsrv/5.8.1/php_pdo_sqlsrv-5.8.1-7.3-nts-vc15-x64.zip', 'C:\testlogs\php_pdo_sqlsrv.zip')

After unzipping the extensions, we can modify the php.ini file, and move on.

Drupal

In order to successfully run the Drupal test suite, the source code must be pulled from the git repository. There are other methods to get the code in a way that is packaged to deploy a site, but these reorganize some of the files in ways the test suite does not expect. The default webroot directory is C:\inetpub\wwwroot, so we will check the code out there. Unfortunately, git requires that the directory be empty when cloning a project, so you can either delete all files from wwwroot, or move it, and make a new directory with the same name.

Once we clone the repository, we need to run composer install to pull all the development dependencies. Some versions of Drupal have extra package updates that need to be specified. As of this writing, I'm working with Drupal 9.1, and no extra steps are needed.

Because we are testing a custom module, we need to copy our module code into the Drupal code-space. I also have some custom test suites to overcome Appveyor's 1 hour VM limit, as well as a custom phpunit.xml file to guide PHPUnit with its activities.

Sql Server

The SQL Server database needs to be able to run regular expression matches to pass the Drupal test suite. I have a general purpose dll that meets the requirements. Here's how we download it, and configure the database to use it:

- cmd: mkdir c:\testlogs
- ps: (New-Object Net.WebClient).DownloadFile('https://github.com/Beakerboy/drupal-sqlsrv-regex/releases/download/1.0/RegEx.dll', 'C:\testlogs\RegEx.dll')
- cmd: sqlcmd -U sa -P Password12! -Q "CREATE DATABASE mydrupalsite"
- cmd: sqlcmd -d mydrupalsite -U sa -P Password12! -Q "EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'clr enable', 1; RECONFIGURE"
- cmd: sqlcmd -d mydrupalsite -U sa -P Password12! -Q "CREATE ASSEMBLY Regex from 'C:\testlogs\RegEx.dll' WITH PERMISSION_SET = SAFE"
- cmd: sqlcmd -d mydrupalsite -U sa -P Password12! -Q "CREATE FUNCTION dbo.REGEXP(@pattern NVARCHAR(100), @matchString NVARCHAR(100)) RETURNS bit EXTERNAL NAME Regex.RegExCompiled.RegExCompiledMatch"

This dll is self-signed, which apparently can cause SQL Server to log errors. It's possible there are other assemblies that can be substituted for this that will not give warnings.

IIS

Lastly, IIS needs to be configured if a user wishes to run any Functional Tests. It needs to be able to write to the sites directory, and it needs to be able to interpret PHP files.

We set permissions on the sites directory with:

$sharepath = "C:\inetpub\wwwroot\sites"

$Acl = Get-ACL $SharePath

$AccessRule= New-Object System.Security.AccessControl.FileSystemAccessRule("everyone","FullControl","ContainerInherit,Objectinherit","none","Allow")

$Acl.AddAccessRule($AccessRule)

Set-Acl $SharePath $Acl

#and we bind PHP into IIS with (From Stack Overflow):
import-module WebAdministration

###############################################################

# Adds a FastCGI process pool in IIS

###############################################################

$php = 'C:\tools\php\php-cgi.exe'

$configPath = get-webconfiguration 'system.webServer/fastcgi/application' | where-object { $_.fullPath -eq $php }

if (!$configPath) {
  add-webconfiguration 'system.webserver/fastcgi' -value @{'fullPath' = $php }
}

###############################################################

# Create IIS handler mapping for handling PHP requests

###############################################################

$handlerName = "PHP 7"

$handler = get-webconfiguration 'system.webserver/handlers/add' | where-object { $_.Name -eq $handlerName }
if (!$handler) {
  add-webconfiguration 'system.webServer/handlers' -Value @{
    Name = $handlerName;
    Path = "*.php";
    Verb = "*";
    Modules = "FastCgiModule";
    scriptProcessor=$php;
    resourceType='Either'
  }
}

###############################################################

# Configure the FastCGI Setting

###############################################################

# Set the max request environment variable for PHP

$configPath = "system.webServer/fastCgi/application[@fullPath='$php']/environmentVariables/environmentVariable"

$config = Get-WebConfiguration $configPath

if (!$config) {
  $configPath = "system.webServer/fastCgi/application[@fullPath='$php']/environmentVariables"
  Add-WebConfiguration $configPath -Value @{ 'Name' = 'PHP_FCGI_MAX_REQUESTS'; Value = 10050 }
}

# Configure the settings
# Available settings:
# instanceMaxRequests, monitorChangesTo, stderrMode, signalBeforeTerminateSeconds
# activityTimeout, requestTimeout, queueLength, rapidFailsPerMinute,
# flushNamedPipe, protocol

$configPath = "system.webServer/fastCgi/application[@fullPath='$php']"

Set-WebConfigurationProperty $configPath -Name instanceMaxRequests -Value 10000

Set-WebConfigurationProperty $configPath -Name monitorChangesTo -Value 'C:\tools\php\php.ini'

# Restart IIS to load new configs.

invoke-command-scriptblock {iisreset /restart }

Running Tests

Finally we can run all the Unit, Kernel, and Functional tests in Drupal on Windows. Next step, Functional Javascript Tests

2
$ 0.05
$ 0.05 from @cesar.cantante
Avatar for Beakerboy
3 years ago

Comments

Nice work, keep it up!

$ 0.00
3 years ago