Swift: Deconstruct SPF - Struct Mechanism

Today, I will start on building out the functionality of the struct Mechanism.
This will involve creating some tests, and basic functions.

Instantiate the Mechanism Struct

We have our struct defined as such:

struct Mechanism {
    var kind: MechanismKind;
    var qualifier: Qualifier;
    var mechanism: String;
    
    init(k: MechanismKind, q: Qualifier, m: String) {
        kind = k;
        qualifier = q;
        mechanism = m;
    }
    func mechanismString() -> String {
        self.mechanism;
    }
    func whatKind() -> MechanismKind {
        return self.kind;
    }
    func isPass() -> Bool {
        return self.qualifier == Qualifier.Pass;
    }
    func isFail() -> Bool {
        return self.qualifier == Qualifier.Fail;
    }
    func isSoftFail() -> Bool {
        return self.qualifier == Qualifier.SoftFail;
    }
    func isNeutral() -> Bool {
        return self.qualifier == Qualifier.Neutral;
    }
    func isNone() -> Bool {
        return self.qualifier == Qualifier.None;
    }
}

init

This will instantiate the struct. Provide what kind of mechanism it is. Its qualifier, and the value of its mechanism.

mechanismString

This will allow us to access the information stored in mechanism as a simple String

isPass

This will return Bool of true if the Qualifier is Pass

isFail

This will return Bool of true if the Qualifier is Fail

isSoftFail

This will return Bool of true if the Qualifier is SoftFail

isNeutral

This will return Bool of true if the Qualifier is Neutral

isNone

This will return Bool of true if the Qualifier is None

whatKind

This will help us understand if the mechanism represents a redirect, A, or some other mechanism.

Testing

For this we need to take a look at Tests/DeconSpfTests/DeconSpfTests.swift.
Within this file we have the following initial set of code.

import XCTest
@testable import DeconSpf;

final class SPFTests: XCTestCase {

    func testMechanismRedirect() {
        let Mech = Mechanism(k: MechanismKind.Redirect, q: Qualifier.None, m: "test.com");
        XCTAssertEqual(Mech.mechanismString(), "test.com");
        XCTAssertEqual(Mech.whatKind(), MechanismKind.Redirect);
        XCTAssertNotEqual(Mech.whatKind(), MechanismKind.A);
        XCTAssertEqual(Mech.isNone(), true);
    }
    func testMechanismInclude() {
        let Mech = Mechanism(k: MechanismKind.Include, q: Qualifier.Pass, m: "_spf.test.com");
        XCTAssertEqual(Mech.mechanismString(), "_spf.test.com");
        XCTAssertEqual(Mech.whatKind(), MechanismKind.Include);
        XCTAssertNotEqual(Mech.whatKind(), MechanismKind.Redirect);
        XCTAssertNotEqual(Mech.whatKind(), MechanismKind.A);
        XCTAssertEqual(Mech.isPass(), true);
    }
    static var allTests = [
        ("testMechanismRedirect", testMechanismRedirect),
        ("testMechanismInclude", testMechanismInclude),
    ]
}

Let’s break down each test function.

func testMechanismRedirect() {
    let Mech = Mechanism(k: MechanismKind.Redirect, q: Qualifier.None, m: "test.com");
    XCTAssertEqual(Mech.mechanismString(), "test.com");
    XCTAssertEqual(Mech.whatKind(), MechanismKind.Redirect);
    XCTAssertNotEqual(Mech.whatKind(), MechanismKind.A);
    XCTAssertEqual(Mech.isNone(), true);
}

What are is happening here:

  1. Instantiate Mech and then we make four assertions.
  2. mechanismString() matches “test.com”
  3. whatKind() matches MechanismKind.Redirect
  4. The MechanismKind is not MechanismKind.A
  5. isNone() is true
func testMechanismInclude() {
    let Mech = Mechanism(k: MechanismKind.Include, q: Qualifier.Pass, m: "_spf.test.com");
    XCTAssertEqual(Mech.mechanismString(), "_spf.test.com");
    XCTAssertEqual(Mech.whatKind(), MechanismKind.Include);
    XCTAssertNotEqual(Mech.whatKind(), MechanismKind.Redirect);
    XCTAssertNotEqual(Mech.whatKind(), MechanismKind.A);
    XCTAssertEqual(Mech.isPass(), true);
}

This does the same basic tests, but for a mechanism that should be of MechanismKind.Include.

Testing

swift test

Output

[6/6] Linking DeconSpfPackageTests
Test Suite 'All tests' started at 2021-05-13 15:09:48.435
Test Suite 'DeconSpfPackageTests.xctest' started at 2021-05-13 15:09:48.436
Test Suite 'SPFTests' started at 2021-05-13 15:09:48.436
Test Case '-[DeconSpfTests.SPFTests testMechanismInclude]' started.
Test Case '-[DeconSpfTests.SPFTests testMechanismInclude]' passed (0.082 seconds).
Test Case '-[DeconSpfTests.SPFTests testMechanismRedirect]' started.
Test Case '-[DeconSpfTests.SPFTests testMechanismRedirect]' passed (0.000 seconds).
Test Suite 'SPFTests' passed at 2021-05-13 15:09:48.519.
  Executed 2 tests, with 0 failures (0 unexpected) in 0.083 (0.083) seconds
Test Suite 'DeconSpfPackageTests.xctest' passed at 2021-05-13 15:09:48.519.
  Executed 2 tests, with 0 failures (0 unexpected) in 0.083 (0.083) seconds
Test Suite 'All tests' passed at 2021-05-13 15:09:48.519.
  Executed 2 tests, with 0 failures (0 unexpected) in 0.083 (0.084) seconds

Let’s add a new test

We want to be able to get a string representation of the mechanism as it was originally seen.

If I had an initially mechanism of ip4:x.x.x.x which was stored as

  • kind: MechanismKind.Ip4
  • qualifier: Qualifier.None
  • mechanism: “x.x.x.x”

I should be able to call mech.asMechanism() and get ip4:x.x.x.x in return.

So let’s make a test for this. (Expect failure)

We update the DeconSpfTests.swift with the following.

func testAsMechanism() {
    let Mech = Mechanism(k: MechanismKind.ip4, q: Qualifier.None, m: "192.168.1.0/24");
    XCTAssertEqual(Mech.asMechanism(), "ip4:192.168.1.0/24");
}
static var allTests = [
    ("testMechanismRedirect", testMechanismRedirect),
    ("testMechanismInclude", testMechanismInclude),
    ("testAsMechanism", testAsMechanism),
]

We add this test to the allTests so that it will be run with the others.

Xcode also complains
Value of type 'Mechanism' has no member 'asMechanism'.
This is because I have not yet defined a member function named asMechanism() on the Mechanism struct.

The code for this project can now be found here.

This is the diff between the initial commit and the current code.

Closing

That’s it for today. In the next article. I will look at returning values based on the Qualifier type. These will be needed to build the string for asMechanism()


See also