Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ jobs:
if: "!contains(github.event.head_commit.message, 'skip ci')"
name: PHP ${{ matrix.php-versions }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.php-versions == '8.2' }}
continue-on-error: ${{ matrix.php-versions >= '8.6' }}
strategy:
fail-fast: false
matrix:
php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5', '8.6']
os: [ubuntu-latest, windows-latest]

steps:
Expand All @@ -24,17 +24,15 @@ jobs:
run: git config --system core.autocrlf false; git config --system core.eol lf

- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Set up PHP ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: com_dotnet
ini-values: date.timezone=Europe/Berlin

- name: Setup Problem Matchers for PHP
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"

- name: Validate composer.json and composer.lock
run: composer validate

Expand All @@ -43,17 +41,17 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)"

- name: Cache dependencies
uses: actions/cache@v2.1.3
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-

- name: Install dependencies
run: >
curl -sSL https://baltocdn.com/xp-framework/xp-runners/distribution/downloads/e/entrypoint/xp-run-8.6.2.sh > xp-run &&
curl -sSL https://github.com/xp-runners/reference/releases/download/v9.3.0/xp-run-9.3.0.sh > xp-run &&
composer install --prefer-dist &&
echo "vendor/autoload.php" > composer.pth

- name: Run test suite
run: sh xp-run xp.unittest.TestRunner src/test/php
run: sh xp-run xp.test.Runner src/test/php
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Mail for XP
[![BSD Licence](https://raw.githubusercontent.com/xp-framework/web/master/static/licence-bsd.png)](https://github.com/xp-framework/core/blob/master/LICENCE.md)
[![Requires PHP 7.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-7_0plus.svg)](http://php.net/)
[![Supports PHP 8.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-8_0plus.svg)](http://php.net/)
[![Latest Stable Version](https://poser.pugx.org/xp-framework/mail/version.png)](https://packagist.org/packages/xp-framework/mail)
[![Latest Stable Version](https://poser.pugx.org/xp-framework/mail/version.svg)](https://packagist.org/packages/xp-framework/mail)

E-Mail APIs, POP3, IMAP, MailDir, SMTP support.

Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
"description" : "Mail for XP",
"keywords": ["module", "xp"],
"require" : {
"xp-framework/core": "^11.0 | ^10.0 | ^9.0 | ^8.0 | ^7.0",
"xp-framework/core": "^12.11 | ^11.11",
"xp-framework/text-encode": "^10.0 | ^9.0 | ^8.0 | ^7.0",
"xp-framework/networking": "^10.0 | ^9.0 | ^8.0 | ^7.0",
"xp-framework/networking": "^11.0 | ^10.0 | ^9.3",
"xp-framework/logging": "^11.0 | ^10.0 | ^9.0 | ^8.0 | ^7.0",
"php" : ">=7.0.0"
},
"require-dev" : {
"xp-framework/unittest": "^11.0 | ^10.0 | ^9.0 | ^8.0 | ^7.0"
"xp-framework/test": "^2.0 | ^1.0"
},
"autoload" : {
"files" : ["src/main/php/autoload.php"]
Expand Down
26 changes: 12 additions & 14 deletions src/main/php/peer/mail/transport/MailTransport.class.php
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
<?php namespace peer\mail\transport;


use io\OperationFailed;
use peer\mail\Message;
use text\encode\QuotedPrintable;

/**
* Mail transport via built-in mail() function
*
* Example:
* <code>
* // [...build messages array...]
* $t= new MailTransport();
* $t->connect(); // use $t->connect('-odq'); for queuing
*
* for ($i= 0, $size= sizeof($message); $i < $size; $i++) {
* $t->send($message);
* }
* $t->close();
* </code>
* ```php
* // [...build messages array...]
* $t= new MailTransport();
* $t->connect(); // use $t->connect('-odq'); for queuing
*
* @see php://mail
* @purpose Provide transport via mail()
* for ($i= 0, $size= sizeof($message); $i < $size; $i++) {
* $t->send($message);
* }
* $t->close();
* ```
*/
class MailTransport extends Transport {
protected
Expand Down Expand Up @@ -72,7 +70,7 @@ public function send(Message $message) {
)) {
throw new TransportException(
'Could not send mail to '.\xp::stringOf($message->to[0]),
new \io\IOException('Call to mail() failed')
new OperationFailed('Call to mail() failed')
);
}
return true;
Expand Down
111 changes: 59 additions & 52 deletions src/test/php/peer/mail/unittest/AbstractMessageTest.class.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
<?php namespace peer\mail\unittest;

use peer\mail\InternetAddress;
use unittest\{Test, Values};
use test\{Assert, Test, Values};
use util\Date;

/**
* Tests Message class
*/
abstract class AbstractMessageTest extends \unittest\TestCase {
protected $fixture= null;
abstract class AbstractMessageTest {

/**
* Returns a new fixture
Expand All @@ -17,13 +13,6 @@ abstract class AbstractMessageTest extends \unittest\TestCase {
*/
protected abstract function newFixture();

/**
* Setup
*/
public function setUp() {
$this->fixture= $this->newFixture();
}

/**
* Returns recipient types
*
Expand All @@ -35,120 +24,138 @@ protected function recipientTypes() {

#[Test, Values('recipientTypes')]
public function getRecipient_for_single_recipient($type) {
$fixture= $this->newFixture();
$r= new InternetAddress('thekid@example.com');
$this->fixture->addRecipient($type, $r);
$this->assertEquals($r, $this->fixture->getRecipient($type));
$fixture->addRecipient($type, $r);
Assert::equals($r, $fixture->getRecipient($type));
}

#[Test, Values('recipientTypes')]
public function getRecipient_for_multiple_recipients($type) {
$fixture= $this->newFixture();
$r1= new InternetAddress('thekid@example.com');
$r2= new InternetAddress('alex@example.com');
$this->fixture->addRecipient($type, $r1);
$this->fixture->addRecipient($type, $r2);
$this->assertEquals($r1, $this->fixture->getRecipient($type));
$this->assertEquals($r2, $this->fixture->getRecipient($type));
$fixture->addRecipient($type, $r1);
$fixture->addRecipient($type, $r2);
Assert::equals($r1, $fixture->getRecipient($type));
Assert::equals($r2, $fixture->getRecipient($type));
}

#[Test, Values('recipientTypes')]
public function getRecipients_initially_returns_empty_array($type) {
$this->assertEquals([], $this->fixture->getRecipients($type));
$fixture= $this->newFixture();
Assert::equals([], $fixture->getRecipients($type));
}

#[Test, Values('recipientTypes')]
public function getRecipients_returns_recipients_added_via_addRecipient($type) {
$fixture= $this->newFixture();
$r1= new InternetAddress('thekid@example.com');
$r2= new InternetAddress('alex@example.com');
$this->fixture->addRecipient($type, $r1);
$this->fixture->addRecipient($type, $r2);
$this->assertEquals([$r1, $r2], $this->fixture->getRecipients($type));
$fixture->addRecipient($type, $r1);
$fixture->addRecipient($type, $r2);
Assert::equals([$r1, $r2], $fixture->getRecipients($type));
}

#[Test, Values('recipientTypes')]
public function getRecipients_returns_recipients_added_via_addRecipients($type) {
$fixture= $this->newFixture();
$r1= new InternetAddress('thekid@example.com');
$r2= new InternetAddress('alex@example.com');
$this->fixture->addRecipients($type, [$r1, $r2]);
$this->assertEquals([$r1, $r2], $this->fixture->getRecipients($type));
$fixture->addRecipients($type, [$r1, $r2]);
Assert::equals([$r1, $r2], $fixture->getRecipients($type));
}

#[Test]
public function getHeader_returns_null_if_header_doesnt_exist() {
$this->assertNull($this->fixture->getHeader('X-Common-Header'));
$fixture= $this->newFixture();
Assert::null($fixture->getHeader('X-Common-Header'));
}

#[Test]
public function getHeader_returns_added_header() {
$this->fixture->setHeader('X-Common-Header', 'test');
$this->assertEquals('test', $this->fixture->getHeader('X-Common-Header'));
$fixture= $this->newFixture();
$fixture->setHeader('X-Common-Header', 'test');
Assert::equals('test', $fixture->getHeader('X-Common-Header'));
}

#[Test, Values(['x-common-header', 'X-COMMON-HEADER', 'X-common-header'])]
public function getHeader_returns_added_header_case_insensitively($variant) {
$this->fixture->setHeader('X-Common-Header', 'test');
$this->assertEquals('test', $this->fixture->getHeader($variant));
$fixture= $this->newFixture();
$fixture->setHeader('X-Common-Header', 'test');
Assert::equals('test', $fixture->getHeader($variant));
}

#[Test]
public function subject_accessors() {
$this->fixture->setSubject('Hello World');
$this->assertEquals('Hello World', $this->fixture->getSubject());
$fixture= $this->newFixture();
$fixture->setSubject('Hello World');
Assert::equals('Hello World', $fixture->getSubject());
}

#[Test]
public function message_id_accessors() {
$this->fixture->setMessageId('1234');
$this->assertEquals('1234', $this->fixture->getMessageId());
$fixture= $this->newFixture();
$fixture->setMessageId('1234');
Assert::equals('1234', $fixture->getMessageId());
}

#[Test]
public function date_accessors() {
$fixture= $this->newFixture();
$d= Date::now();
$this->fixture->setDate($d);
$this->assertEquals($d, $this->fixture->getDate());
$fixture->setDate($d);
Assert::equals($d, $fixture->getDate());
}

#[Test]
public function encoding_accessors() {
$this->fixture->setEncoding('8bit');
$this->assertEquals('8bit', $this->fixture->getEncoding());
$fixture= $this->newFixture();
$fixture->setEncoding('8bit');
Assert::equals('8bit', $fixture->getEncoding());
}

#[Test]
public function charset_accessors() {
$this->fixture->setCharset('utf-8');
$this->assertEquals('utf-8', $this->fixture->getCharset());
$fixture= $this->newFixture();
$fixture->setCharset('utf-8');
Assert::equals('utf-8', $fixture->getCharset());
}

#[Test]
public function content_type_accessors() {
$this->fixture->setContentType('text/plain');
$this->assertEquals('text/plain', $this->fixture->getContentType());
$fixture= $this->newFixture();
$fixture->setContentType('text/plain');
Assert::equals('text/plain', $fixture->getContentType());
}

#[Test]
public function mime_version_accessors() {
$this->fixture->setMimeVersion('1.0');
$this->assertEquals('1.0', $this->fixture->getMimeVersion());
$fixture= $this->newFixture();
$fixture->setMimeVersion('1.0');
Assert::equals('1.0', $fixture->getMimeVersion());
}

#[Test]
public function unencoded_body() {
$this->fixture->setBody('Hello World');
$this->assertEquals('Hello World', $this->fixture->getBody());
$fixture= $this->newFixture();
$fixture->setBody('Hello World');
Assert::equals('Hello World', $fixture->getBody());
}

#[Test]
public function base64_encoded_body() {
$this->fixture->setEncoding('base64');
$this->fixture->setBody('SGVsbG8gV29ybGQ=');
$this->assertEquals('Hello World', $this->fixture->getBody(true));
$fixture= $this->newFixture();
$fixture->setEncoding('base64');
$fixture->setBody('SGVsbG8gV29ybGQ=');
Assert::equals('Hello World', $fixture->getBody(true));
}

#[Test]
public function quoted_printable_encoded_body() {
$this->fixture->setEncoding('quoted-printable');
$this->fixture->setBody('Hello World=3D');
$this->assertEquals('Hello World=', $this->fixture->getBody(true));
$fixture= $this->newFixture();
$fixture->setEncoding('quoted-printable');
$fixture->setBody('Hello World=3D');
Assert::equals('Hello World=', $fixture->getBody(true));
}
}
13 changes: 5 additions & 8 deletions src/test/php/peer/mail/unittest/HeaderParsingTest.class.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
<?php namespace peer\mail\unittest;

use unittest\{Test, Values, TestCase};
use test\{Test, Assert, Values};

/**
* Tests header parsing - Message::setHeaderString()
*/
abstract class HeaderParsingTest extends TestCase {
abstract class HeaderParsingTest {

/**
* Parse a string containing message headers
Expand All @@ -17,16 +14,16 @@ protected abstract function parse($str);

#[Test, Values(["Header: Value", "Header:Value"])]
public function header($variant) {
$this->assertEquals(['Header' => 'Value'], $this->parse($variant)->headers);
Assert::equals(['Header' => 'Value'], $this->parse($variant)->headers);
}

#[Test, Values(["Header:", "Header: "])]
public function empty_header($variant) {
$this->assertEquals(['Header' => null], $this->parse($variant)->headers);
Assert::equals(['Header' => null], $this->parse($variant)->headers);
}

#[Test, Values(["Header: Line 1\n\tLine 2", "Header: Line 1\n Line 2"])]
public function line_continued($variant) {
$this->assertEquals(['Header' => 'Line 1 Line 2'], $this->parse($variant)->headers);
Assert::equals(['Header' => 'Line 1 Line 2'], $this->parse($variant)->headers);
}
}
Loading
Loading