Create OPMLParserTests.

This commit is contained in:
Brent Simmons 2024-08-26 22:39:46 -07:00
parent f63af89e31
commit 4349dd26ff
21 changed files with 2692 additions and 62 deletions

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OPMLParser"
BuildableName = "OPMLParser"
BlueprintName = "OPMLParser"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OPMLParser"
BuildableName = "OPMLParser"
BlueprintName = "OPMLParser"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OPMLParserTests"
BuildableName = "OPMLParserTests"
BlueprintName = "OPMLParserTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OPMLParser"
BuildableName = "OPMLParser"
BlueprintName = "OPMLParser"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Parser"
BuildableName = "Parser"
BlueprintName = "Parser"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SAX"
BuildableName = "SAX"
BlueprintName = "SAX"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OPMLParserTests"
BuildableName = "OPMLParserTests"
BlueprintName = "OPMLParserTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ParserTests"
BuildableName = "ParserTests"
BlueprintName = "ParserTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "OPMLParser"
BuildableName = "OPMLParser"
BlueprintName = "OPMLParser"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SAX"
BuildableName = "SAX"
BlueprintName = "SAX"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SAX"
BuildableName = "SAX"
BlueprintName = "SAX"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -15,13 +15,26 @@ let package = Package(
.library(
name: "SAX",
type: .dynamic,
targets: ["SAX"])
targets: ["SAX"]),
.library(
name: "OPMLParser",
type: .dynamic,
targets: ["OPMLParser"])
],
dependencies: [
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "OPMLParser",
dependencies: [
"SAX"
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]),
.target(
name: "Parser",
dependencies: [
@ -41,6 +54,10 @@ let package = Package(
dependencies: ["Parser"],
exclude: ["Info.plist"],
resources: [.copy("Resources")]),
.testTarget(
name: "OPMLParserTests",
dependencies: ["OPMLParser"],
resources: [.copy("Resources")]),
]
)

View File

@ -6,6 +6,7 @@
//
import Foundation
import SAX
public struct OPMLFeedSpecifier: Sendable {

View File

@ -27,8 +27,9 @@ public class OPMLItem {
if let feedURL = attributes?.opml_xmlUrl {
self.feedSpecifier = OPMLFeedSpecifier(title: self.titleFromAttributes, feedDescription: attributes?.opml_description, homePageURL: attributes?.opml_htmlUrl, feedURL: feedURL)
} else {
self.feedSpecifier = nil
}
}
func add(_ item: OPMLItem) {

View File

@ -6,13 +6,16 @@
//
import Foundation
import SAX
public final class OPMLParser {
private let url: String
private let data: Data
private let parserData: ParserData
private var data: Data {
parserData.data
}
private let opmlDocument: OPMLDocument
private var opmlDocument: OPMLDocument?
private var itemStack = [OPMLItem]()
private var currentItem: OPMLItem? {
@ -28,26 +31,26 @@ public final class OPMLParser {
public static func document(with parserData: ParserData) -> OPMLDocument? {
let opmlParser = OPMLParser(parserData)
return opmlParser.parse()
opmlParser.parse()
return opmlParser.opmlDocument
}
init(_ parserData: ParserData) {
self.url = parserData.url
self.data = parserData.data
self.opmlDocument = OPMLDocument(url: parserData.url)
self.parserData = parserData
}
}
private extension OPMLParser {
func parse() -> OPMLDocument? {
func parse() {
guard canParseData() else {
return nil
return
}
pushItem(opmlDocument)
opmlDocument = OPMLDocument(url: parserData.url)
push(opmlDocument!)
let saxParser = SAXParser(delegate: self, data: data)
saxParser.parse()
@ -70,20 +73,20 @@ private extension OPMLParser {
return
}
itemStack.dropLast()
_ = itemStack.dropLast()
}
}
extension OPMLParser: SAXParserDelegate {
func saxParser(_ saxParser: SAXParser, xmlStartElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?) {
public func saxParser(_ saxParser: SAXParser, xmlStartElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?) {
if SAXEqualStrings(localName, XMLKey.title) {
if SAXEqualTags(localName, XMLKey.title) {
saxParser.beginStoringCharacters()
return
}
if !SAXEqualStrings(localName, XMLKey.outline) {
if !SAXEqualTags(localName, XMLKey.outline) {
return
}
@ -94,21 +97,21 @@ extension OPMLParser: SAXParserDelegate {
push(item)
}
func saxParser(_ saxParser: SAXParser, xmlEndElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) {
public func saxParser(_ saxParser: SAXParser, xmlEndElement localName: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?) {
if SAXEqualStrings(localname, XMLKey.title) {
if SAXEqualTags(localName, XMLKey.title) {
if let item = currentItem as? OPMLDocument {
item.title = saxParser.currentStringWithTrimmedWhitespace
}
return
}
if SAXEqualStrings(localName, XMLKey.outline) {
if SAXEqualTags(localName, XMLKey.outline) {
popItem()
}
}
func saxParser(_: SAXParser, xmlCharactersFound: XMLPointer, count: Int) {
public func saxParser(_: SAXParser, xmlCharactersFound: XMLPointer, count: Int) {
// Nothing to do, but method is required.
}

View File

@ -7,7 +7,7 @@
import Foundation
extension Data {
public extension Data {
/// Return true if the data contains a given String.
///

View File

@ -7,7 +7,7 @@
import Foundation
extension Dictionary where Key == String, Value == String {
public extension Dictionary where Key == String, Value == String {
func object(forCaseInsensitiveKey key: String) -> String? {

View File

@ -8,7 +8,7 @@
import Foundation
extension String {
public extension String {
var nilIfEmptyOrWhitespace: String? {
return self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? nil : self

View File

@ -9,7 +9,7 @@ import Foundation
public struct HTMLTag: Sendable {
public enum TagType {
public enum TagType: Sendable {
case link
case meta
}

View File

@ -9,8 +9,8 @@ import Foundation
public struct ParserData: Sendable {
let url: String
let data: Data
public let url: String
public let data: Data
public init(url: String, data: Data) {
self.url = url

View File

@ -8,9 +8,9 @@
import Foundation
import libxml2
typealias XMLPointer = UnsafePointer<xmlChar>
public typealias XMLPointer = UnsafePointer<xmlChar>
protocol SAXParserDelegate {
public protocol SAXParserDelegate {
func saxParser(_: SAXParser, xmlStartElement: XMLPointer, prefix: XMLPointer?, uri: XMLPointer?, namespaceCount: Int, namespaces: UnsafePointer<XMLPointer?>?, attributeCount: Int, attributesDefaultedCount: Int, attributes: UnsafePointer<XMLPointer?>?)
@ -19,11 +19,11 @@ protocol SAXParserDelegate {
func saxParser(_: SAXParser, xmlCharactersFound: XMLPointer, count: Int)
}
final class SAXParser {
public final class SAXParser {
fileprivate let delegate: SAXParserDelegate
var currentCharacters: Data? { // UTF-8 encoded
public var currentCharacters: Data? { // UTF-8 encoded
guard storingCharacters else {
return nil
@ -33,7 +33,7 @@ final class SAXParser {
// Conveniences to get string version of currentCharacters
var currentString: String? {
public var currentString: String? {
guard let d = currentCharacters, !d.isEmpty else {
return nil
@ -41,7 +41,7 @@ final class SAXParser {
return String(data: d, encoding: .utf8)
}
var currentStringWithTrimmedWhitespace: String? {
public var currentStringWithTrimmedWhitespace: String? {
guard let s = currentString else {
return nil
@ -53,13 +53,13 @@ final class SAXParser {
private var storingCharacters = false
private var characters = Data()
init(delegate: SAXParserDelegate, data: Data) {
public init(delegate: SAXParserDelegate, data: Data) {
self.delegate = delegate
self.data = data
}
func parse() {
public func parse() {
guard !data.isEmpty else {
return
@ -69,7 +69,7 @@ final class SAXParser {
xmlCtxtUseOptions(context, Int32(XML_PARSE_RECOVER.rawValue | XML_PARSE_NOENT.rawValue))
data.withUnsafeBytes { bufferPointer in
if let bytes = bufferPointer.bindMemory(to: xmlChar.self).baseAddress {
if let bytes = bufferPointer.bindMemory(to: CChar.self).baseAddress {
xmlParseChunk(context, bytes, Int32(data.count), 0)
}
}
@ -79,7 +79,7 @@ final class SAXParser {
}
/// Delegate can call from xmlStartElement. Characters will be available in xmlEndElement as currentCharacters property. Storing characters is stopped after each xmlEndElement.
func beginStoringCharacters() {
public func beginStoringCharacters() {
storingCharacters = true
characters.count = 0
@ -91,7 +91,7 @@ final class SAXParser {
characters.count = 0
}
func attributesDictionary(_ attributes: UnsafePointer<XMLPointer?>?, attributeCount: Int) -> [String: String]? {
public func attributesDictionary(_ attributes: UnsafePointer<XMLPointer?>?, attributeCount: Int) -> [String: String]? {
guard attributeCount > 0, let attributes else {
return nil
@ -154,7 +154,7 @@ private extension SAXParser {
}
}
private func startElement(_ context: UnsafeMutableRawPointer?, name: XMLPointer?, prefix: XMLPointer?, URI: XMLPointer?, nb_namespaces: CInt, namespaces: UnsafePointer<XMLPointer?>?, nb_attributes: CInt, nb_defaulted: CInt, attributes: UnsafeMutablePointer<XMLPointer?>?) {
private func startElement(_ context: UnsafeMutableRawPointer?, name: XMLPointer?, prefix: XMLPointer?, URI: XMLPointer?, nb_namespaces: CInt, namespaces: UnsafeMutablePointer<XMLPointer?>?, nb_attributes: CInt, nb_defaulted: CInt, attributes: UnsafeMutablePointer<XMLPointer?>?) {
guard let context, let name else {
return
@ -194,8 +194,9 @@ nonisolated(unsafe) private var saxHandlerStruct: xmlSAXHandler = {
var handler = xmlSAXHandler()
handler.characters = charactersFound
handler.startElement = startElement
handler.endElement = endElement
handler.startElementNs = startElement
handler.endElementNs = endElement
handler.initialized = XML_SAX2_MAGIC
return handler
}()

View File

@ -8,11 +8,26 @@
import Foundation
import libxml2
func SAXEqualStrings(_ s1: XMLPointer, _ s2: XMLPointer, length: Int? = nil) -> Bool {
public func SAXEqualTags(_ localName: XMLPointer, _ tag: ContiguousArray<Int8>) -> Bool {
if let length {
return xmlStrncmp(s1, s2, Int32(length)) == 0
return tag.withUnsafeBufferPointer { bufferPointer in
let tagCount = tag.count
for i in 0..<tagCount {
let localNameCharacter = localName[i]
if localNameCharacter == 0 {
return false
}
let tagCharacter = UInt8(tag[i])
if localNameCharacter != tagCharacter {
return false
}
}
// localName might actually be longer  make sure its the same length as tag.
return localName[tagCount] == 0
}
return xmlStrEqual(s1, s2) != 0
}

View File

@ -7,8 +7,8 @@
//
import XCTest
import Parser
import ParserObjC
import SAX
@testable import OPMLParser
class OPMLTests: XCTestCase {
@ -18,21 +18,23 @@ class OPMLTests: XCTestCase {
// 0.002 sec on my 2012 iMac.
self.measure {
let _ = try! RSOPMLParser.parseOPML(with: self.subsData)
let _ = OPMLParser.document(with: self.subsData)
}
}
func testNotOPML() {
let d = parserData("DaringFireball", "rss", "http://daringfireball.net/")
XCTAssertThrowsError(try RSOPMLParser.parseOPML(with: d))
XCTAssertNil(OPMLParser.document(with: d))
}
func testSubsStructure() {
let opmlDocument = try! RSOPMLParser.parseOPML(with: subsData)
XCTAssertEqual("Subs", opmlDocument.title)
XCTAssertEqual("http://example.org/", opmlDocument.url)
recursivelyCheckOPMLStructure(opmlDocument)
let opmlDocument = OPMLParser.document(with: subsData)
XCTAssertNotNil(opmlDocument)
XCTAssertEqual("Subs", opmlDocument!.title)
XCTAssertEqual("http://example.org/", opmlDocument!.url)
recursivelyCheckOPMLStructure(opmlDocument!)
}
@ -42,23 +44,23 @@ class OPMLTests: XCTestCase {
// which appears to be true with OPML generated by The Old Reader.
let d = parserData("SubsNoTitleAttributes", "opml", "http://example.org/")
let opmlDocument = try! RSOPMLParser.parseOPML(with: d)
recursivelyCheckOPMLStructure(opmlDocument)
let opmlDocument = OPMLParser.document(with: d)
recursivelyCheckOPMLStructure(opmlDocument!)
}
}
private extension OPMLTests {
func recursivelyCheckOPMLStructure(_ item: RSOPMLItem) {
func recursivelyCheckOPMLStructure(_ item: OPMLItem) {
let feedSpecifier = item.feedSpecifier
if !(item is RSOPMLDocument) {
XCTAssertNotNil((item.attributes! as NSDictionary).opml_text)
if !(item is OPMLDocument) {
XCTAssertNotNil(item.attributes!.opml_text)
}
// If it has no children, it should have a feed specifier. The converse is also true.
var isFolder = item.children != nil && item.children!.count > 0
if !isFolder && (item.attributes! as NSDictionary).opml_title == "Skip" {
var isFolder = item.items != nil && item.items!.count > 0
if !isFolder && item.attributes?.opml_title == "Skip" {
isFolder = true
}
@ -70,10 +72,17 @@ private extension OPMLTests {
XCTAssertNil(feedSpecifier)
}
if item.children != nil && item.children!.count > 0 {
for oneItem in item.children! {
if item.items != nil && item.items!.count > 0 {
for oneItem in item.items! {
recursivelyCheckOPMLStructure(oneItem)
}
}
}
}
func parserData(_ filename: String, _ fileExtension: String, _ url: String) -> ParserData {
let filename = "Resources/\(filename)"
let path = Bundle.module.path(forResource: filename, ofType: fileExtension)!
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
return ParserData(url: url, data: data)
}

File diff suppressed because it is too large Load Diff