Swift: Deconstruct SPF - Qualifier and MechanismKind

Learning Swift: Enum

In this article I will be looking at defining the enums for Qualifier and MechanismKind. I will also be adding tests to ensure that the functions work as expected. Further I will implement a simple asMechanism() as a proof of concept.

Qualifier

As mentioned in an earlier article. The Qualifier will consist of the following values.

  • + Pass
  • - Fail
  • ~ SoftFail
  • ? Neutral
  • None

I expect that I will want to be able to obtain the character value (rawValue) at some point in the future.

Definition

enum Qualifier: String {
    case Pass
    case Fail
    case SoftFail
    case Neutral
    case None
    
    func get() -> String {
        switch self {
        case .Pass:
            return "+"
        case .Fail:
            return "-"
        case .SoftFail:
            return "~"
        case .Neutral:
            return "?"
        case .None:
            return ""
        }
    }
}

At this point I have diverged from the original definition. This gave me a chance to look at the switch statement in Swift. I think will change this in a refactor session, as I expect to be able to access the Raw Value. Scroll down to find the actual documentation.

Testing

To make testing a little cleaner and manageable moving forward. I also created a new file under DeconfSpfTests called DeconfSpfQualifierTests.swift.

This contains the following code.

import XCTest
@testable import DeconSpf;

final class SPFQualifierTests: XCTestCase {

    func testQualifierPass() {
        let Q = Qualifier.Pass;
        XCTAssertEqual(Q.get(), "+");
    }
    func testQualifierFail() {
        let Q = Qualifier.Fail;
        XCTAssertEqual(Q.get(), "-");
    }
    func testQualifierSoftFail() {
        let Q = Qualifier.SoftFail;
        XCTAssertEqual(Q.get(), "~");
    }
    func testQualifierNeutral() {
        let Q = Qualifier.Neutral;
        XCTAssertEqual(Q.get(), "?");
    }
    func testQualifierNone() {
        let Q = Qualifier.None;
        XCTAssertEqual(Q.get(), "");
    }

    static var allTests = [
        ("testQualifierPass", testQualifierPass),
        ("testQualifierFail", testQualifierFail),
        ("testQualifierSoftFail", testQualifierSoftFail),
        ("testQualifierNeutral", testQualifierNeutral),
        ("testQualifierNone", testQualifierNone),

    ]
}
  • Note: The class name needs to be unique
    • final class SPFQualifierTests: XCTestCase
    • I am using SPFQualifierTests

MechanismKind

I took the same approach here with MechanismKind.

Definition

enum MechanismKind {
    case Redirect, Include, A, MX, ip4, ip6, All;
    
    func get() -> String {
        switch self {
        case .Redirect:
            return "redirect="
        case .Include:
            return "include:"
        case .A:
            return "a"
        case .MX:
            return "mx"
        case .ip4:
            return "ip4:"
        case .ip6:
            return "ip6:"
        case .All:
            return "all"
        }
    }
}

Testing

I also again created a new test file called DeconfSpfMechanismKindTests.
This also has a unique class name SPFMechanimsKindTests.

import XCTest
@testable import DeconSpf;

final class SPFMechanimsKindTests: XCTestCase {

    func testMechanimsKindRedirect() {
        let MK = MechanismKind.Redirect;
        XCTAssertEqual(MK.get(), "redirect=");
    }
    func testMechanimsKindInclude() {
        let MK = MechanismKind.Include;
        XCTAssertEqual(MK.get(), "include:");
    }
    func testMechanimsKindA() {
        let MK = MechanismKind.A;
        XCTAssertEqual(MK.get(), "a");
    }
    func testMechanimsKindMX() {
        let MK = MechanismKind.MX;
        XCTAssertEqual(MK.get(), "mx");
    }
    func testMechanimsKindIp4() {
        let MK = MechanismKind.ip4;
        XCTAssertEqual(MK.get(), "ip4:");
    }
    func testMechanimsKindIp6() {
        let MK = MechanismKind.ip6;
        XCTAssertEqual(MK.get(), "ip6:");
    }
    func testMechanimsKindAll() {
        let MK = MechanismKind.All;
        XCTAssertEqual(MK.get(), "all");
    }

    static var allTests = [
        ("testMechanismKindRedirect", testMechanimsKindRedirect),
        ("testMechanismKindInclude", testMechanimsKindInclude),
        ("testMechanismKindA", testMechanimsKindA),
        ("testMechanismKindMX", testMechanimsKindMX),
        ("testMechanismKindIp4", testMechanimsKindIp4),
        ("testMechanismKindIp6", testMechanimsKindIp6),
        ("testMechanismKindAll", testMechanimsKindAll),

    ]
}

asMechanism

Here I would refer you to the documentation on Strings and Characters.

Code

I have updated struct Mechanism as shown below

struct Mechanims {
  --snip--
  func asMechanism() -> String {
      var mechanismString = String();
      // TODO: Qualifier
      // Access the string for this mechanism's kind.
      mechanismString += self.kind.get();
      // Access the string for the mechanim
      mechanismString += self.mechanism;
      return mechanismString;
  }
  --snip--
}

Tests

I now have my previous test working as well as a few new ones.

func testAsMechanismIp4() {
    let Mech = Mechanism(k: MechanismKind.ip4, q: Qualifier.None, m: "192.168.1.0/24");
    XCTAssertEqual(Mech.asMechanism(), "ip4:192.168.1.0/24");
}
func testAsMechanismIp6() {
    let Mech = Mechanism(k: MechanismKind.ip6, q: Qualifier.None, m: "X:X:X:X/16");
    XCTAssertEqual(Mech.asMechanism(), "ip6:X:X:X:X/16");
}
func testAsMechanismRedirect() {
    let Mech = Mechanism(k: MechanismKind.Redirect, q: Qualifier.None, m: "_spf.example.com");
    XCTAssertEqual(Mech.asMechanism(), "redirect=_spf.example.com");
}
func testAsMechanismInclude() {
    let Mech = Mechanism(k: MechanismKind.Include, q: Qualifier.None, m: "_spf1.example.com");
    XCTAssertEqual(Mech.asMechanism(), "include:_spf1.example.com");
}
func testAsMechanismA() {
    let Mech = Mechanism(k: MechanismKind.A, q: Qualifier.None, m: "");
    XCTAssertEqual(Mech.asMechanism(), "a");
}
func testAsMechanismMx() {
    let Mech = Mechanism(k: MechanismKind.MX, q: Qualifier.None, m: "");
    XCTAssertEqual(Mech.asMechanism(), "mx");
}

Note that these tests are not complete. And do not yet cover all cases. In particular these tests will fail with several examples of A and MX Mechanisms.

The code for this project can now be found here.

This is the diff between Initial and Initial-asMechanism.

Closing

Today I implemented an initial get() function for both Qualifier and MechanismKind. I also did an initial implementation for asMechanism().


See also