From 27c9599502f28bc0655f869a5765fb16f0e07d65 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sat, 25 Aug 2018 12:54:42 +0200 Subject: [PATCH 01/16] Added UnitTest target --- Logger.xcodeproj/project.pbxproj | 125 +++++++++++++++++++++++++++++++ UnitTests/Info.plist | 22 ++++++ UnitTests/UnitTests.swift | 33 ++++++++ 3 files changed, 180 insertions(+) create mode 100644 UnitTests/Info.plist create mode 100644 UnitTests/UnitTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 7355162..2b38cbb 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 2E58D35D21316C3500BEF81A /* UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E58D35C21316C3500BEF81A /* UnitTests.swift */; }; + 2E58D35F21316C3500BEF81A /* libLogger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EBF4B3A2122AA34008E4117 /* libLogger.a */; }; 2EBF4B3E2122AA34008E4117 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B3D2122AA34008E4117 /* Logger.swift */; }; 2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B442122ACD6008E4117 /* LogStringConvertible.swift */; }; 2EBF4B4A2122AF53008E4117 /* AgregateLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B472122AF53008E4117 /* AgregateLogger.swift */; }; @@ -18,6 +20,16 @@ 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 2E58D36021316C3500BEF81A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2EBF4B322122AA34008E4117 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2EBF4B392122AA34008E4117; + remoteInfo = Logger; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 2EBF4B382122AA34008E4117 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -31,6 +43,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2E58D35A21316C3500BEF81A /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E58D35C21316C3500BEF81A /* UnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitTests.swift; sourceTree = "<group>"; }; + 2E58D35E21316C3500BEF81A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 2EBF4B3A2122AA34008E4117 /* libLogger.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLogger.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2EBF4B3D2122AA34008E4117 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; }; 2EBF4B442122ACD6008E4117 /* LogStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogStringConvertible.swift; sourceTree = "<group>"; }; @@ -46,6 +61,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2E58D35721316C3500BEF81A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2E58D35F21316C3500BEF81A /* libLogger.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2EBF4B372122AA34008E4117 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -56,10 +79,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2E58D35B21316C3500BEF81A /* UnitTests */ = { + isa = PBXGroup; + children = ( + 2E58D35C21316C3500BEF81A /* UnitTests.swift */, + 2E58D35E21316C3500BEF81A /* Info.plist */, + ); + path = UnitTests; + sourceTree = "<group>"; + }; 2EBF4B312122AA34008E4117 = { isa = PBXGroup; children = ( 2EBF4B3C2122AA34008E4117 /* Logger */, + 2E58D35B21316C3500BEF81A /* UnitTests */, 2EBF4B3B2122AA34008E4117 /* Products */, ); sourceTree = "<group>"; @@ -68,6 +101,7 @@ isa = PBXGroup; children = ( 2EBF4B3A2122AA34008E4117 /* libLogger.a */, + 2E58D35A21316C3500BEF81A /* UnitTests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -109,6 +143,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 2E58D35921316C3500BEF81A /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2E58D36221316C3500BEF81A /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + 2E58D35621316C3500BEF81A /* Sources */, + 2E58D35721316C3500BEF81A /* Frameworks */, + 2E58D35821316C3500BEF81A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2E58D36121316C3500BEF81A /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = 2E58D35A21316C3500BEF81A /* UnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 2EBF4B392122AA34008E4117 /* Logger */ = { isa = PBXNativeTarget; buildConfigurationList = 2EBF4B412122AA34008E4117 /* Build configuration list for PBXNativeTarget "Logger" */; @@ -136,6 +188,9 @@ LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Wojciech Nagrodzki"; TargetAttributes = { + 2E58D35921316C3500BEF81A = { + CreatedOnToolsVersion = 10.0; + }; 2EBF4B392122AA34008E4117 = { CreatedOnToolsVersion = 10.0; LastSwiftMigration = 1000; @@ -155,11 +210,30 @@ projectRoot = ""; targets = ( 2EBF4B392122AA34008E4117 /* Logger */, + 2E58D35921316C3500BEF81A /* UnitTests */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + 2E58D35821316C3500BEF81A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ + 2E58D35621316C3500BEF81A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2E58D35D21316C3500BEF81A /* UnitTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2EBF4B362122AA34008E4117 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -178,7 +252,49 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 2E58D36121316C3500BEF81A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2EBF4B392122AA34008E4117 /* Logger */; + targetProxy = 2E58D36021316C3500BEF81A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ + 2E58D36321316C3500BEF81A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = UnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.wnagrodzki.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2E58D36421316C3500BEF81A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = UnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.wnagrodzki.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 2EBF4B3F2122AA34008E4117 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -337,6 +453,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2E58D36221316C3500BEF81A /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2E58D36321316C3500BEF81A /* Debug */, + 2E58D36421316C3500BEF81A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2EBF4B352122AA34008E4117 /* Build configuration list for PBXProject "Logger" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/UnitTests/Info.plist b/UnitTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/UnitTests/Info.plist @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleExecutable</key> + <string>$(EXECUTABLE_NAME)</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>$(PRODUCT_NAME)</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> diff --git a/UnitTests/UnitTests.swift b/UnitTests/UnitTests.swift new file mode 100644 index 0000000..5ff3dfe --- /dev/null +++ b/UnitTests/UnitTests.swift @@ -0,0 +1,33 @@ +// +// UnitTests.swift +// UnitTests +// +// Created by Wojciech Nagrodzki on 25/08/2018. +// Copyright © 2018 Wojciech Nagrodzki. All rights reserved. +// + +import XCTest + +class UnitTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} From 0d1abcdb54e57ab540bfa58891f3e88cc01879ff Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sat, 25 Aug 2018 13:04:05 +0200 Subject: [PATCH 02/16] Removed UnitTests.swift file --- Logger.xcodeproj/project.pbxproj | 4 ---- UnitTests/UnitTests.swift | 33 -------------------------------- 2 files changed, 37 deletions(-) delete mode 100644 UnitTests/UnitTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 2b38cbb..8dc73dd 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 2E58D35D21316C3500BEF81A /* UnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E58D35C21316C3500BEF81A /* UnitTests.swift */; }; 2E58D35F21316C3500BEF81A /* libLogger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EBF4B3A2122AA34008E4117 /* libLogger.a */; }; 2EBF4B3E2122AA34008E4117 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B3D2122AA34008E4117 /* Logger.swift */; }; 2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B442122ACD6008E4117 /* LogStringConvertible.swift */; }; @@ -44,7 +43,6 @@ /* Begin PBXFileReference section */ 2E58D35A21316C3500BEF81A /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 2E58D35C21316C3500BEF81A /* UnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitTests.swift; sourceTree = "<group>"; }; 2E58D35E21316C3500BEF81A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 2EBF4B3A2122AA34008E4117 /* libLogger.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLogger.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2EBF4B3D2122AA34008E4117 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; }; @@ -82,7 +80,6 @@ 2E58D35B21316C3500BEF81A /* UnitTests */ = { isa = PBXGroup; children = ( - 2E58D35C21316C3500BEF81A /* UnitTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, ); path = UnitTests; @@ -230,7 +227,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2E58D35D21316C3500BEF81A /* UnitTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/UnitTests/UnitTests.swift b/UnitTests/UnitTests.swift deleted file mode 100644 index 5ff3dfe..0000000 --- a/UnitTests/UnitTests.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// UnitTests.swift -// UnitTests -// -// Created by Wojciech Nagrodzki on 25/08/2018. -// Copyright © 2018 Wojciech Nagrodzki. All rights reserved. -// - -import XCTest - -class UnitTests: XCTestCase { - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} From 3a6c531dad07a6511d12313ba59a690cf0236dc3 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sat, 25 Aug 2018 13:12:29 +0200 Subject: [PATCH 03/16] Made Logger scheme shared --- .../xcshareddata/xcschemes/Logger.xcscheme | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Logger.xcodeproj/xcshareddata/xcschemes/Logger.xcscheme diff --git a/Logger.xcodeproj/xcshareddata/xcschemes/Logger.xcscheme b/Logger.xcodeproj/xcshareddata/xcschemes/Logger.xcscheme new file mode 100644 index 0000000..1b5258f --- /dev/null +++ b/Logger.xcodeproj/xcshareddata/xcschemes/Logger.xcscheme @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1000" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "2EBF4B392122AA34008E4117" + BuildableName = "libLogger.a" + BlueprintName = "Logger" + ReferencedContainer = "container:Logger.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "2E58D35921316C3500BEF81A" + BuildableName = "UnitTests.xctest" + BlueprintName = "UnitTests" + ReferencedContainer = "container:Logger.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "2EBF4B392122AA34008E4117" + BuildableName = "libLogger.a" + BlueprintName = "Logger" + ReferencedContainer = "container:Logger.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </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"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "2EBF4B392122AA34008E4117" + BuildableName = "libLogger.a" + BlueprintName = "Logger" + ReferencedContainer = "container:Logger.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "2EBF4B392122AA34008E4117" + BuildableName = "libLogger.a" + BlueprintName = "Logger" + ReferencedContainer = "container:Logger.xcodeproj"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> From f032da4ed83eb71668678bbad7a08d2b2b469cc4 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sat, 25 Aug 2018 13:28:09 +0200 Subject: [PATCH 04/16] Added LogStringConvertibleTests --- Logger.xcodeproj/project.pbxproj | 4 ++ UnitTests/LogStringConvertibleTests.swift | 50 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 UnitTests/LogStringConvertibleTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 8dc73dd..684a132 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */; }; 2E58D35F21316C3500BEF81A /* libLogger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EBF4B3A2122AA34008E4117 /* libLogger.a */; }; 2EBF4B3E2122AA34008E4117 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B3D2122AA34008E4117 /* Logger.swift */; }; 2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B442122ACD6008E4117 /* LogStringConvertible.swift */; }; @@ -43,6 +44,7 @@ /* Begin PBXFileReference section */ 2E58D35A21316C3500BEF81A /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogStringConvertibleTests.swift; sourceTree = "<group>"; }; 2E58D35E21316C3500BEF81A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 2EBF4B3A2122AA34008E4117 /* libLogger.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLogger.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2EBF4B3D2122AA34008E4117 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; }; @@ -80,6 +82,7 @@ 2E58D35B21316C3500BEF81A /* UnitTests */ = { isa = PBXGroup; children = ( + 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, ); path = UnitTests; @@ -227,6 +230,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/UnitTests/LogStringConvertibleTests.swift b/UnitTests/LogStringConvertibleTests.swift new file mode 100644 index 0000000..58bc9b2 --- /dev/null +++ b/UnitTests/LogStringConvertibleTests.swift @@ -0,0 +1,50 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import XCTest +@testable import Logger + +class LogStringConvertibleTests: XCTestCase { + + func testZeroObjectsDescription() { + let objects = [Int]() + XCTAssertEqual(objects.logDescription, "[]") + } + + func testOneObjectDescription() { + let objects = [1] + XCTAssertEqual(objects.logDescription, "[1]") + } + + func testTwoObjectsDescription() { + let objects = [1, 2] + XCTAssertEqual(objects.logDescription, "[1, 2]") + } +} + +extension Int: LogStringConvertible { + public var logDescription: String { + return String(describing: self) + } +} From 4df5b125a80bc70bfc1a43845e13abc364006c91 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 26 Aug 2018 10:51:36 +0200 Subject: [PATCH 05/16] Added LoggerTests --- Logger.xcodeproj/project.pbxproj | 4 +++ UnitTests/LoggetTests.swift | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 UnitTests/LoggetTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 684a132..14a8da4 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 2EBF4B572122B598008E4117 /* DiskLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B542122B598008E4117 /* DiskLogger.swift */; }; 2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B552122B598008E4117 /* FileWriter.swift */; }; 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; }; + 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D621329CA30058EEFC /* LoggetTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,6 +59,7 @@ 2EBF4B542122B598008E4117 /* DiskLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskLogger.swift; sourceTree = "<group>"; }; 2EBF4B552122B598008E4117 /* FileWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileWriter.swift; sourceTree = "<group>"; }; 2EBF4B562122B598008E4117 /* Logrotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logrotate.swift; sourceTree = "<group>"; }; + 2ED077D621329CA30058EEFC /* LoggetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggetTests.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -83,6 +85,7 @@ isa = PBXGroup; children = ( 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */, + 2ED077D621329CA30058EEFC /* LoggetTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, ); path = UnitTests; @@ -231,6 +234,7 @@ buildActionMask = 2147483647; files = ( 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */, + 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/UnitTests/LoggetTests.swift b/UnitTests/LoggetTests.swift new file mode 100644 index 0000000..a753ca7 --- /dev/null +++ b/UnitTests/LoggetTests.swift @@ -0,0 +1,55 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import XCTest +@testable import Logger + +class LoggetTests: XCTestCase { + + func testLocation() { + let mock = LoggerMock() + mock.log("", level: .critical) + XCTAssertEqual(mock.location, "LoggetTests:32 testLocation()") + } + + func testLogLevelLogDescription() { + XCTAssertEqual(LogLevel.emergency.logDescription, "emerg") + XCTAssertEqual(LogLevel.alert.logDescription, "alert") + XCTAssertEqual(LogLevel.critical.logDescription, "crit") + XCTAssertEqual(LogLevel.error.logDescription, "err") + XCTAssertEqual(LogLevel.warning.logDescription, "warning") + XCTAssertEqual(LogLevel.notice.logDescription, "notice") + XCTAssertEqual(LogLevel.informational.logDescription, "info") + XCTAssertEqual(LogLevel.debug.logDescription, "debug") + } +} + +private class LoggerMock: Logger { + + var location: String? + + func log(time: Date, level: LogLevel, location: String, message: @autoclosure () -> String) { + self.location = location + } +} From ce546d00726b5fbe89454cde0d8fd15f48099e2c Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 26 Aug 2018 11:24:15 +0200 Subject: [PATCH 06/16] Added AgregateLoggerTests --- Logger.xcodeproj/project.pbxproj | 4 ++ UnitTests/AgregateLoggerTests.swift | 68 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 UnitTests/AgregateLoggerTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 14a8da4..69657b4 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B552122B598008E4117 /* FileWriter.swift */; }; 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; }; 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D621329CA30058EEFC /* LoggetTests.swift */; }; + 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,6 +61,7 @@ 2EBF4B552122B598008E4117 /* FileWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileWriter.swift; sourceTree = "<group>"; }; 2EBF4B562122B598008E4117 /* Logrotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logrotate.swift; sourceTree = "<group>"; }; 2ED077D621329CA30058EEFC /* LoggetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggetTests.swift; sourceTree = "<group>"; }; + 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgregateLoggerTests.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -86,6 +88,7 @@ children = ( 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */, 2ED077D621329CA30058EEFC /* LoggetTests.swift */, + 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, ); path = UnitTests; @@ -234,6 +237,7 @@ buildActionMask = 2147483647; files = ( 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */, + 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */, 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UnitTests/AgregateLoggerTests.swift b/UnitTests/AgregateLoggerTests.swift new file mode 100644 index 0000000..c3c2704 --- /dev/null +++ b/UnitTests/AgregateLoggerTests.swift @@ -0,0 +1,68 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import XCTest +@testable import Logger + +class AgregateLoggerTests: XCTestCase { + + func testCallForwarding() { + let loggerA = LoggerMock() + let loggerB = LoggerMock() + let agregateLogger = AgregateLogger(loggers: [loggerA, loggerB]) + agregateLogger.log("0", level: .emergency) + agregateLogger.log("1", level: .alert) + agregateLogger.log("2", level: .critical) + agregateLogger.log("3", level: .error) + agregateLogger.log("4", level: .warning) + agregateLogger.log("5", level: .notice) + agregateLogger.log("6", level: .informational) + agregateLogger.log("7", level: .debug) + XCTAssertEqual(loggerA, loggerB) + } +} + +private class LoggerMock: Logger { + + struct Log: Equatable { + let time: Date + let level: LogLevel + let location: String + let message: String + } + + private var logs = [Log]() + + func log(time: Date, level: LogLevel, location: String, message: @autoclosure () -> String) { + let log = Log(time: time, level: level, location: location, message: message()) + logs.append(log) + } +} + +extension LoggerMock: Equatable { + + static func == (lhs: LoggerMock, rhs: LoggerMock) -> Bool { + return lhs.logs == rhs.logs + } +} From 11cdd72a04db9c0932831118f613f3f946ab1f28 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 26 Aug 2018 12:00:10 +0200 Subject: [PATCH 07/16] Uncoupled Logrotate from FileManager --- Logger.xcodeproj/project.pbxproj | 4 +++ Logger/Loggers/DiskLogger/DiskLogger.swift | 2 +- Logger/Loggers/DiskLogger/FileSystem.swift | 38 ++++++++++++++++++++++ Logger/Loggers/DiskLogger/Logrotate.swift | 12 ++++--- 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 Logger/Loggers/DiskLogger/FileSystem.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 69657b4..11a519e 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; }; 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D621329CA30058EEFC /* LoggetTests.swift */; }; 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */; }; + 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* FileSystem.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,6 +63,7 @@ 2EBF4B562122B598008E4117 /* Logrotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logrotate.swift; sourceTree = "<group>"; }; 2ED077D621329CA30058EEFC /* LoggetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggetTests.swift; sourceTree = "<group>"; }; 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgregateLoggerTests.swift; sourceTree = "<group>"; }; + 2ED077DA2132B0320058EEFC /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -142,6 +144,7 @@ 2EBF4B562122B598008E4117 /* Logrotate.swift */, 2EBF4B4F2122B06E008E4117 /* NSFileHandle+Swift.h */, 2EBF4B502122B06E008E4117 /* NSFileHandle+Swift.m */, + 2ED077DA2132B0320058EEFC /* FileSystem.swift */, ); path = DiskLogger; sourceTree = "<group>"; @@ -252,6 +255,7 @@ 2EBF4B3E2122AA34008E4117 /* Logger.swift in Sources */, 2EBF4B512122B06E008E4117 /* NSFileHandle+Swift.m in Sources */, 2EBF4B4B2122AF53008E4117 /* ConsoleLogger.swift in Sources */, + 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */, 2EBF4B4A2122AF53008E4117 /* AgregateLogger.swift in Sources */, 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */, 2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */, diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index d4cb80f..20e864e 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -97,7 +97,7 @@ public final class DiskLogger: Logger { } private func rotateLogFiles() throws { - let logrotate = Logrotate(fileURL: fileURL, rotations: rotations) + let logrotate = Logrotate(fileURL: fileURL, rotations: rotations, fileSystem: FileManager.default) try logrotate.rotate() } } diff --git a/Logger/Loggers/DiskLogger/FileSystem.swift b/Logger/Loggers/DiskLogger/FileSystem.swift new file mode 100644 index 0000000..d78c5f4 --- /dev/null +++ b/Logger/Loggers/DiskLogger/FileSystem.swift @@ -0,0 +1,38 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + +protocol FileSystem { + func itemExists(at URL: URL) -> Bool + func removeItem(at URL: URL) throws + func moveItem(at srcURL: URL, to dstURL: URL) throws +} + +extension FileManager: FileSystem { + + func itemExists(at URL: URL) -> Bool { + return fileExists(atPath: URL.path) + } +} diff --git a/Logger/Loggers/DiskLogger/Logrotate.swift b/Logger/Loggers/DiskLogger/Logrotate.swift index c377d2e..d2a97e4 100644 --- a/Logger/Loggers/DiskLogger/Logrotate.swift +++ b/Logger/Loggers/DiskLogger/Logrotate.swift @@ -29,16 +29,18 @@ final class Logrotate { private let fileURL: URL private let rotations: Int + private let fileSystem: FileSystem /// Initializes new Logrotate instance. /// /// - Parameters: /// - fileURL: URL of the log file. /// - rotations: Number of times log files are rotated before being removed. - init(fileURL: URL, rotations: Int) { + init(fileURL: URL, rotations: Int, fileSystem: FileSystem) { precondition(rotations > 0) self.fileURL = fileURL self.rotations = rotations + self.fileSystem = fileSystem } /// Rotates log files `rotations` number of times. @@ -58,12 +60,12 @@ final class Logrotate { let toDelete = rotatedURLs.last! let toMove = zip(allURLs, rotatedURLs).reversed() - if FileManager.default.fileExists(atPath: toDelete.path) { - try FileManager.default.removeItem(at: toDelete) + if fileSystem.itemExists(at: toDelete) { + try fileSystem.removeItem(at: toDelete) } for (oldURL, newURL) in toMove { - guard FileManager.default.fileExists(atPath: oldURL.path) else { continue } - try FileManager.default.moveItem(at: oldURL, to: newURL) + guard fileSystem.itemExists(at: oldURL) else { continue } + try fileSystem.moveItem(at: oldURL, to: newURL) } } } From 7c58a507787a58cc3541f31ceea4b2d6819902a8 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 26 Aug 2018 12:08:07 +0200 Subject: [PATCH 08/16] Uncoupled DiskLogger from FileManager --- Logger/Loggers/DiskLogger/DiskLogger.swift | 10 ++++++---- Logger/Loggers/DiskLogger/FileSystem.swift | 9 +++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index 20e864e..dfc6859 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -30,6 +30,7 @@ public final class DiskLogger: Logger { private let fileURL: URL private let fileSizeLimit: UInt64 private let rotations: Int + private let fileSystem: FileSystem private let formatter: DateFormatter private let queue = DispatchQueue(label: "com.wnagrodzki.DiskLogger", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil) private var buffer = Data() @@ -41,10 +42,11 @@ public final class DiskLogger: Logger { /// - fileURL: URL of the log file. /// - fileSizeLimit: Maximum size log file can reach in bytes. Attempt to exceeding that limit triggers log files rotation. /// - rotations: Number of times log files are rotated before being removed. - public init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int) { + public init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileSystem: FileSystem) { self.fileURL = fileURL self.fileSizeLimit = fileSizeLimit self.rotations = rotations + self.fileSystem = fileSystem formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) @@ -80,8 +82,8 @@ public final class DiskLogger: Logger { private func openFileWriter() throws { guard fileWriter == nil else { return } - if FileManager.default.fileExists(atPath: fileURL.path) == false { - FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: nil) + if fileSystem.itemExists(at: fileURL) == false { + _ = fileSystem.createFile(at: fileURL) } fileWriter = try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit) } @@ -97,7 +99,7 @@ public final class DiskLogger: Logger { } private func rotateLogFiles() throws { - let logrotate = Logrotate(fileURL: fileURL, rotations: rotations, fileSystem: FileManager.default) + let logrotate = Logrotate(fileURL: fileURL, rotations: rotations, fileSystem: fileSystem) try logrotate.rotate() } } diff --git a/Logger/Loggers/DiskLogger/FileSystem.swift b/Logger/Loggers/DiskLogger/FileSystem.swift index d78c5f4..b47b316 100644 --- a/Logger/Loggers/DiskLogger/FileSystem.swift +++ b/Logger/Loggers/DiskLogger/FileSystem.swift @@ -24,15 +24,20 @@ import Foundation -protocol FileSystem { +public protocol FileSystem { func itemExists(at URL: URL) -> Bool func removeItem(at URL: URL) throws func moveItem(at srcURL: URL, to dstURL: URL) throws + func createFile(at URL: URL) -> Bool } extension FileManager: FileSystem { - func itemExists(at URL: URL) -> Bool { + public func itemExists(at URL: URL) -> Bool { return fileExists(atPath: URL.path) } + + public func createFile(at URL: URL) -> Bool { + return createFile(atPath: URL.path, contents: nil, attributes: nil) + } } From d333992ca6bdcfebac58d33f264ac45656ecd4ab Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 26 Aug 2018 13:24:48 +0200 Subject: [PATCH 09/16] Renamed Logrotate to FileRotate --- Logger.xcodeproj/project.pbxproj | 8 ++++---- Logger/Loggers/DiskLogger/DiskLogger.swift | 2 +- .../DiskLogger/{Logrotate.swift => Filerotate.swift} | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename Logger/Loggers/DiskLogger/{Logrotate.swift => Filerotate.swift} (99%) diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 11a519e..a26581b 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -17,7 +17,7 @@ 2EBF4B512122B06E008E4117 /* NSFileHandle+Swift.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B502122B06E008E4117 /* NSFileHandle+Swift.m */; }; 2EBF4B572122B598008E4117 /* DiskLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B542122B598008E4117 /* DiskLogger.swift */; }; 2EBF4B582122B598008E4117 /* FileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B552122B598008E4117 /* FileWriter.swift */; }; - 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* Logrotate.swift */; }; + 2EBF4B592122B598008E4117 /* FileRotate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBF4B562122B598008E4117 /* FileRotate.swift */; }; 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D621329CA30058EEFC /* LoggetTests.swift */; }; 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */; }; 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* FileSystem.swift */; }; @@ -60,7 +60,7 @@ 2EBF4B532122B2AA008E4117 /* Logger-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Logger-Bridging-Header.h"; sourceTree = "<group>"; }; 2EBF4B542122B598008E4117 /* DiskLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiskLogger.swift; sourceTree = "<group>"; }; 2EBF4B552122B598008E4117 /* FileWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileWriter.swift; sourceTree = "<group>"; }; - 2EBF4B562122B598008E4117 /* Logrotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logrotate.swift; sourceTree = "<group>"; }; + 2EBF4B562122B598008E4117 /* FileRotate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileRotate.swift; sourceTree = "<group>"; }; 2ED077D621329CA30058EEFC /* LoggetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggetTests.swift; sourceTree = "<group>"; }; 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgregateLoggerTests.swift; sourceTree = "<group>"; }; 2ED077DA2132B0320058EEFC /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = "<group>"; }; @@ -141,7 +141,7 @@ children = ( 2EBF4B542122B598008E4117 /* DiskLogger.swift */, 2EBF4B552122B598008E4117 /* FileWriter.swift */, - 2EBF4B562122B598008E4117 /* Logrotate.swift */, + 2EBF4B562122B598008E4117 /* FileRotate.swift */, 2EBF4B4F2122B06E008E4117 /* NSFileHandle+Swift.h */, 2EBF4B502122B06E008E4117 /* NSFileHandle+Swift.m */, 2ED077DA2132B0320058EEFC /* FileSystem.swift */, @@ -257,7 +257,7 @@ 2EBF4B4B2122AF53008E4117 /* ConsoleLogger.swift in Sources */, 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */, 2EBF4B4A2122AF53008E4117 /* AgregateLogger.swift in Sources */, - 2EBF4B592122B598008E4117 /* Logrotate.swift in Sources */, + 2EBF4B592122B598008E4117 /* FileRotate.swift in Sources */, 2EBF4B452122ACD6008E4117 /* LogStringConvertible.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index dfc6859..89e46b8 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -99,7 +99,7 @@ public final class DiskLogger: Logger { } private func rotateLogFiles() throws { - let logrotate = Logrotate(fileURL: fileURL, rotations: rotations, fileSystem: fileSystem) + let logrotate = FileRotate(fileURL: fileURL, rotations: rotations, fileSystem: fileSystem) try logrotate.rotate() } } diff --git a/Logger/Loggers/DiskLogger/Logrotate.swift b/Logger/Loggers/DiskLogger/Filerotate.swift similarity index 99% rename from Logger/Loggers/DiskLogger/Logrotate.swift rename to Logger/Loggers/DiskLogger/Filerotate.swift index d2a97e4..bda34b9 100644 --- a/Logger/Loggers/DiskLogger/Logrotate.swift +++ b/Logger/Loggers/DiskLogger/Filerotate.swift @@ -25,7 +25,7 @@ import Foundation /// Allows log files rotation. -final class Logrotate { +final class FileRotate { private let fileURL: URL private let rotations: Int From 1144d6a90fbad06e9da0fa545e5e6f5a9186282e Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 26 Aug 2018 13:30:53 +0200 Subject: [PATCH 10/16] Uncoupled DiskLogger from FileRotate --- Logger/Loggers/DiskLogger/DiskLogger.swift | 40 ++++++++++++++++++++-- Logger/Loggers/DiskLogger/Filerotate.swift | 12 ++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index 89e46b8..4088a62 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -24,6 +24,30 @@ import Foundation +/// Allows log files rotation. +protocol Logrotate { + + /// Rotates log files `rotations` number of times. + /// + /// First deletes file at `<fileURL>.<rotations>`. + /// Next moves files located at: + /// + /// `<fileURL>, <fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations - 1>` + /// + /// to `<fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations>` + func rotate() throws +} + +protocol LogrotateFactory { + + /// Returns newly initialized Logrotate instance. + /// + /// - Parameters: + /// - fileURL: URL of the log file. + /// - rotations: Number of times log files are rotated before being removed. + func makeInstance(fileURL: URL, rotations: Int) -> Logrotate +} + /// Logger that writes messages into the file at specified URL with log rotation support. public final class DiskLogger: Logger { @@ -31,6 +55,7 @@ public final class DiskLogger: Logger { private let fileSizeLimit: UInt64 private let rotations: Int private let fileSystem: FileSystem + private let logrotateFactory: LogrotateFactory private let formatter: DateFormatter private let queue = DispatchQueue(label: "com.wnagrodzki.DiskLogger", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil) private var buffer = Data() @@ -42,11 +67,16 @@ public final class DiskLogger: Logger { /// - fileURL: URL of the log file. /// - fileSizeLimit: Maximum size log file can reach in bytes. Attempt to exceeding that limit triggers log files rotation. /// - rotations: Number of times log files are rotated before being removed. - public init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileSystem: FileSystem) { + public convenience init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int) { + self.init(fileURL: fileURL, fileSizeLimit: fileSizeLimit, rotations: rotations, fileSystem: FileManager.default, logrotateFactory: FileRotateFactory()) + } + + init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileSystem: FileSystem, logrotateFactory: LogrotateFactory) { self.fileURL = fileURL self.fileSizeLimit = fileSizeLimit self.rotations = rotations self.fileSystem = fileSystem + self.logrotateFactory = logrotateFactory formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) @@ -99,7 +129,13 @@ public final class DiskLogger: Logger { } private func rotateLogFiles() throws { - let logrotate = FileRotate(fileURL: fileURL, rotations: rotations, fileSystem: fileSystem) + let logrotate = logrotateFactory.makeInstance(fileURL: fileURL, rotations: rotations) try logrotate.rotate() } } + +private class FileRotateFactory: LogrotateFactory { + func makeInstance(fileURL: URL, rotations: Int) -> Logrotate { + return FileRotate(fileURL: fileURL, rotations: rotations, fileSystem: FileManager.default) + } +} diff --git a/Logger/Loggers/DiskLogger/Filerotate.swift b/Logger/Loggers/DiskLogger/Filerotate.swift index bda34b9..0963cfa 100644 --- a/Logger/Loggers/DiskLogger/Filerotate.swift +++ b/Logger/Loggers/DiskLogger/Filerotate.swift @@ -24,7 +24,6 @@ import Foundation -/// Allows log files rotation. final class FileRotate { private let fileURL: URL @@ -42,15 +41,10 @@ final class FileRotate { self.rotations = rotations self.fileSystem = fileSystem } +} + +extension FileRotate: Logrotate { - /// Rotates log files `rotations` number of times. - /// - /// First deletes file at `<fileURL>.<rotations>`. - /// Next moves files located at: - /// - /// `<fileURL>, <fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations - 1>` - /// - /// to `<fileURL>.1, <fileURL>.2 ... <fileURL>.<rotations>` func rotate() throws { let range = 1...rotations let pathExtensions = range.map { "\($0)" } From 74fe9ba0d2ec276adcfdf0d7d13491469b9a9b5c Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Tue, 28 Aug 2018 19:14:55 +0200 Subject: [PATCH 11/16] Uncoupled DiskLogger from FileWriter --- Logger/Loggers/DiskLogger/DiskLogger.swift | 62 +++++++++++++++++----- Logger/Loggers/DiskLogger/FileWriter.swift | 14 ++--- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index 4088a62..5eebbb5 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -24,6 +24,34 @@ import Foundation +/// Write failed as allowed size limit would be exceeded. +public struct SizeLimitedFileQuotaReached: Error {} + +/// Allows writing to a file while respecting allowed size limit. +protocol SizeLimitedFile { + + /// Synchronously writes `data` at the end of the file. + /// + /// - Parameter data: The data to be written. + /// - Throws: Throws an error if no free space is left on the file system, or if any other writing error occurs. + /// Throws `SizeLimitedFileQuotaReached` if allowed size limit would be exceeded. + func write(_ data: Data) throws + + /// Writes all in-memory data to permanent storage and closes the file. + func synchronizeAndCloseFile() +} + +protocol SizeLimitedFileFactory { + + /// Returns newly initialized SizeLimitedFile instance. + /// + /// - Parameters: + /// - fileURL: URL of the file. + /// - fileSizeLimit: Maximum size the file can reach in bytes. + /// - Throws: An error that may occur while the file is being opened for writing. + func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile +} + /// Allows log files rotation. protocol Logrotate { @@ -55,11 +83,12 @@ public final class DiskLogger: Logger { private let fileSizeLimit: UInt64 private let rotations: Int private let fileSystem: FileSystem + private let sizeLimitedFileFactory: SizeLimitedFileFactory private let logrotateFactory: LogrotateFactory private let formatter: DateFormatter private let queue = DispatchQueue(label: "com.wnagrodzki.DiskLogger", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil) private var buffer = Data() - private var fileWriter: FileWriter! + private var sizeLimitedFile: SizeLimitedFile! /// Initializes new DiskLogger instance. /// @@ -68,14 +97,15 @@ public final class DiskLogger: Logger { /// - fileSizeLimit: Maximum size log file can reach in bytes. Attempt to exceeding that limit triggers log files rotation. /// - rotations: Number of times log files are rotated before being removed. public convenience init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int) { - self.init(fileURL: fileURL, fileSizeLimit: fileSizeLimit, rotations: rotations, fileSystem: FileManager.default, logrotateFactory: FileRotateFactory()) + self.init(fileURL: fileURL, fileSizeLimit: fileSizeLimit, rotations: rotations, fileSystem: FileManager.default, sizeLimitedFileFactory: FileWriterFactory(), logrotateFactory: FileRotateFactory()) } - init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileSystem: FileSystem, logrotateFactory: LogrotateFactory) { + init(fileURL: URL, fileSizeLimit: UInt64, rotations: Int, fileSystem: FileSystem, sizeLimitedFileFactory: SizeLimitedFileFactory, logrotateFactory: LogrotateFactory) { self.fileURL = fileURL self.fileSizeLimit = fileSizeLimit self.rotations = rotations self.fileSystem = fileSystem + self.sizeLimitedFileFactory = sizeLimitedFileFactory self.logrotateFactory = logrotateFactory formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" @@ -93,12 +123,12 @@ public final class DiskLogger: Logger { self.buffer.append(data) do { - try self.openFileWriter() + try self.openSizeLimitedFile() do { try self.writeBuffer() } - catch is FileWriter.FileSizeLimitReached { - self.closeFileWriter() + catch is SizeLimitedFileQuotaReached { + self.closeSizeLimitedFile() try self.rotateLogFiles() } } @@ -110,22 +140,22 @@ public final class DiskLogger: Logger { } } - private func openFileWriter() throws { - guard fileWriter == nil else { return } + private func openSizeLimitedFile() throws { + guard sizeLimitedFile == nil else { return } if fileSystem.itemExists(at: fileURL) == false { _ = fileSystem.createFile(at: fileURL) } - fileWriter = try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit) + sizeLimitedFile = try sizeLimitedFileFactory.makeInstance(fileURL: fileURL, fileSizeLimit: fileSizeLimit) } private func writeBuffer() throws { - try fileWriter.write(buffer) + try sizeLimitedFile.write(buffer) buffer.removeAll() } - private func closeFileWriter() { - self.fileWriter.synchronizeAndCloseFile() - self.fileWriter = nil + private func closeSizeLimitedFile() { + self.sizeLimitedFile.synchronizeAndCloseFile() + self.sizeLimitedFile = nil } private func rotateLogFiles() throws { @@ -139,3 +169,9 @@ private class FileRotateFactory: LogrotateFactory { return FileRotate(fileURL: fileURL, rotations: rotations, fileSystem: FileManager.default) } } + +private class FileWriterFactory: SizeLimitedFileFactory { + func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile { + return try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit) + } +} diff --git a/Logger/Loggers/DiskLogger/FileWriter.swift b/Logger/Loggers/DiskLogger/FileWriter.swift index 1083812..471703d 100644 --- a/Logger/Loggers/DiskLogger/FileWriter.swift +++ b/Logger/Loggers/DiskLogger/FileWriter.swift @@ -27,9 +27,6 @@ import Foundation /// Allows writing to a file while respecting allowed size limit. final class FileWriter { - /// Write failed as allowed size limit would be exceeded for the file. - struct FileSizeLimitReached: Error {} - private let handle: FileHandle private let sizeLimit: UInt64 private var currentSize: UInt64 @@ -45,22 +42,19 @@ final class FileWriter { self.sizeLimit = fileSizeLimit currentSize = handle.seekToEndOfFile() } +} + +extension FileWriter: SizeLimitedFile { - /// Synchronously writes `data` at the end of the file. - /// - /// - Parameter data: The data to be written. - /// - Throws: Throws an error if no free space is left on the file system, or if any other writing error occurs. - /// Throws `FileSizeLimitReached` if allowed size limit would be exceeded for the file. func write(_ data: Data) throws { let dataSize = UInt64(data.count) guard currentSize + dataSize <= sizeLimit else { - throw FileSizeLimitReached() + throw SizeLimitedFileQuotaReached() } try handle.swift_write(data) currentSize += dataSize } - /// Writes all in-memory data to permanent storage and closes the file. func synchronizeAndCloseFile() { handle.synchronizeFile() handle.closeFile() From ab7ec33418fe89cf0f07d82ac1b26d8fca58b810 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Tue, 28 Aug 2018 20:01:26 +0200 Subject: [PATCH 12/16] Uncoupled FileWriter from FileHandle --- Logger/Loggers/DiskLogger/DiskLogger.swift | 12 ++++++++++- Logger/Loggers/DiskLogger/FileWriter.swift | 25 ++++++++++++++++------ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index 5eebbb5..6718d09 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -172,6 +172,16 @@ private class FileRotateFactory: LogrotateFactory { private class FileWriterFactory: SizeLimitedFileFactory { func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile { - return try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit) + return try FileWriter(fileURL: fileURL, fileSizeLimit: fileSizeLimit, fileFactory: FileHandleFactory()) } } + +private class FileHandleFactory: FileFactory { + func makeInstance(forWritingTo: URL) throws -> File { + return try FileHandle(forWritingTo: forWritingTo) + } +} + +extension FileHandle: File { + +} diff --git a/Logger/Loggers/DiskLogger/FileWriter.swift b/Logger/Loggers/DiskLogger/FileWriter.swift index 471703d..be1ff30 100644 --- a/Logger/Loggers/DiskLogger/FileWriter.swift +++ b/Logger/Loggers/DiskLogger/FileWriter.swift @@ -24,10 +24,21 @@ import Foundation +protocol File { + func seekToEndOfFile() -> UInt64 + func swift_write(_ data: Data) throws + func synchronizeFile() + func closeFile() +} + +protocol FileFactory { + func makeInstance(forWritingTo: URL) throws -> File +} + /// Allows writing to a file while respecting allowed size limit. final class FileWriter { - private let handle: FileHandle + private let file: File private let sizeLimit: UInt64 private var currentSize: UInt64 @@ -37,10 +48,10 @@ final class FileWriter { /// - fileURL: URL of the file. /// - fileSizeLimit: Maximum size the file can reach in bytes. /// - Throws: An error that may occur while the file is being opened for writing. - init(fileURL: URL, fileSizeLimit: UInt64) throws { - handle = try FileHandle(forWritingTo: fileURL) + init(fileURL: URL, fileSizeLimit: UInt64, fileFactory: FileFactory) throws { + file = try fileFactory.makeInstance(forWritingTo: fileURL) self.sizeLimit = fileSizeLimit - currentSize = handle.seekToEndOfFile() + currentSize = file.seekToEndOfFile() } } @@ -51,12 +62,12 @@ extension FileWriter: SizeLimitedFile { guard currentSize + dataSize <= sizeLimit else { throw SizeLimitedFileQuotaReached() } - try handle.swift_write(data) + try file.swift_write(data) currentSize += dataSize } func synchronizeAndCloseFile() { - handle.synchronizeFile() - handle.closeFile() + file.synchronizeFile() + file.closeFile() } } From 9253166f1dddb11a88ec9f732054837075224367 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Tue, 28 Aug 2018 21:02:51 +0200 Subject: [PATCH 13/16] Added FileRotateTests --- Logger.xcodeproj/project.pbxproj | 4 + UnitTests/FileRotateTests.swift | 181 +++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 UnitTests/FileRotateTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index a26581b..076fc31 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D621329CA30058EEFC /* LoggetTests.swift */; }; 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */; }; 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* FileSystem.swift */; }; + 2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E02135C61100EB3683 /* FileRotateTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -64,6 +65,7 @@ 2ED077D621329CA30058EEFC /* LoggetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggetTests.swift; sourceTree = "<group>"; }; 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgregateLoggerTests.swift; sourceTree = "<group>"; }; 2ED077DA2132B0320058EEFC /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = "<group>"; }; + 2ED103E02135C61100EB3683 /* FileRotateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRotateTests.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -91,6 +93,7 @@ 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */, 2ED077D621329CA30058EEFC /* LoggetTests.swift */, 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */, + 2ED103E02135C61100EB3683 /* FileRotateTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, ); path = UnitTests; @@ -241,6 +244,7 @@ files = ( 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */, 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */, + 2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */, 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UnitTests/FileRotateTests.swift b/UnitTests/FileRotateTests.swift new file mode 100644 index 0000000..13ca7c0 --- /dev/null +++ b/UnitTests/FileRotateTests.swift @@ -0,0 +1,181 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import XCTest +@testable import Logger + +class FileRotateTests: XCTestCase { + + let logURL = URL(fileURLWithPath: "/var/log/application.log") + let log1URL = URL(fileURLWithPath: "/var/log/application.log.1") + let log2URL = URL(fileURLWithPath: "/var/log/application.log.2") + let log3URL = URL(fileURLWithPath: "/var/log/application.log.3") + + func test_1rotation_0files() { + let fileSystem = FileSystemMock(files: []) + let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>() + XCTAssertEqual(actual, expected) + } + + func test_1rotation_1file() { + let fileSystem = FileSystemMock(files: [logURL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL]) + XCTAssertEqual(actual, expected) + } + + func test_1rotation_2files() { + let fileSystem = FileSystemMock(files: [logURL, log1URL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL]) + XCTAssertEqual(actual, expected) + } + + func test_1rotation_3files() { + let fileSystem = FileSystemMock(files: [logURL, log1URL, log2URL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL, log2URL]) + XCTAssertEqual(actual, expected) + } + + func test_2rotations_0files() { + let fileSystem = FileSystemMock(files: []) + let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>() + XCTAssertEqual(actual, expected) + } + + func test_2rotations_1file() { + let fileSystem = FileSystemMock(files: [logURL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL]) + XCTAssertEqual(actual, expected) + } + + func test_2rotations_2files() { + let fileSystem = FileSystemMock(files: [logURL, log1URL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL, log2URL]) + XCTAssertEqual(actual, expected) + } + + func test_2rotations_3files() { + let fileSystem = FileSystemMock(files: [logURL, log1URL, log2URL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL, log2URL]) + XCTAssertEqual(actual, expected) + } + + func test_2rotations_4files() { + let fileSystem = FileSystemMock(files: [logURL, log1URL, log2URL, log3URL]) + let logrotate = FileRotate(fileURL: logURL, rotations: 2, fileSystem: fileSystem) + try? logrotate.rotate() + + let actual = fileSystem.files + let expected = Set<URL>([log1URL, log2URL, log3URL]) + XCTAssertEqual(actual, expected) + } + + func testErrorPropagation() { + let fileSystem = BrokenFileSystem() + let logrotate = FileRotate(fileURL: logURL, rotations: 1, fileSystem: fileSystem) + + XCTAssertThrowsError(try logrotate.rotate(), "An error when removing or moving an item") { (error) in + XCTAssertTrue(error is BrokenFileSystem.IOError) + } + } +} + +private class FileSystemMock: FileSystem { + + private(set) var files = Set<URL>() + + init(files: Set<URL>) { + self.files = files + } + + func itemExists(at URL: URL) -> Bool { + return files.contains(URL) + } + + func removeItem(at URL: URL) throws { + files.remove(URL) + } + + func moveItem(at srcURL: URL, to dstURL: URL) throws { + files.remove(srcURL) + files.insert(dstURL) + } + + func createFile(at URL: URL) -> Bool { + files.insert(URL) + return true + } +} + +private class BrokenFileSystem: FileSystem { + + struct IOError: Error { } + + func itemExists(at URL: URL) -> Bool { + return true + } + + func removeItem(at URL: URL) throws { + throw IOError() + } + + func moveItem(at srcURL: URL, to dstURL: URL) throws { + throw IOError() + } + + func createFile(at URL: URL) -> Bool { + return false + } +} From b224674222ea3114f9411956db960aebc3e64565 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Thu, 30 Aug 2018 18:38:53 +0200 Subject: [PATCH 14/16] Added FileWriterTests --- Logger.xcodeproj/project.pbxproj | 4 ++ UnitTests/FileWriterTests.swift | 107 +++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 UnitTests/FileWriterTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 076fc31..6a86dba 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */; }; 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* FileSystem.swift */; }; 2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E02135C61100EB3683 /* FileRotateTests.swift */; }; + 2ED103E32135D3FB00EB3683 /* FileWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,6 +67,7 @@ 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgregateLoggerTests.swift; sourceTree = "<group>"; }; 2ED077DA2132B0320058EEFC /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = "<group>"; }; 2ED103E02135C61100EB3683 /* FileRotateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRotateTests.swift; sourceTree = "<group>"; }; + 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWriterTests.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -93,6 +95,7 @@ 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */, 2ED077D621329CA30058EEFC /* LoggetTests.swift */, 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */, + 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */, 2ED103E02135C61100EB3683 /* FileRotateTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, ); @@ -243,6 +246,7 @@ buildActionMask = 2147483647; files = ( 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */, + 2ED103E32135D3FB00EB3683 /* FileWriterTests.swift in Sources */, 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */, 2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */, 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */, diff --git a/UnitTests/FileWriterTests.swift b/UnitTests/FileWriterTests.swift new file mode 100644 index 0000000..cb3ebc7 --- /dev/null +++ b/UnitTests/FileWriterTests.swift @@ -0,0 +1,107 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import XCTest +@testable import Logger + +class FileWriterTests: XCTestCase { + + let logURL = URL(fileURLWithPath: "/var/log/application.log") + + func testFileOpeningFailure() { + let factory = UnopenableFileFactory() + XCTAssertThrowsError(try FileWriter(fileURL: logURL, fileSizeLimit: 0, fileFactory: factory), "file open failure") { (error) in + XCTAssertTrue(error is UnopenableFileFactory.OpenFileError) + } + } + + func testKeepingFileSizeLimit() throws { + let factory = FileMockFactory() + let writer = try FileWriter(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory) + let data = Data(bytes: [0]) + try writer.write(data) + + XCTAssertEqual(factory.mock.writtenData, data) + } + + func testExceedingFileSizeLimit() throws { + let factory = FileMockFactory() + let writer = try FileWriter(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory) + let data = Data(bytes: [0, 0]) + + XCTAssertThrowsError(try writer.write(data), "file size limit exceeded") { (error) in + XCTAssertTrue(error is SizeLimitedFileQuotaReached) + } + + XCTAssertEqual(factory.mock.writtenData.count, 0) + } + + func testSynchronizingAndClosingFile() throws { + let factory = FileMockFactory() + let writer = try FileWriter(fileURL: logURL, fileSizeLimit: 1, fileFactory: factory) + writer.synchronizeAndCloseFile() + XCTAssertTrue(factory.mock.synchronizeFileCallCount == 1 && factory.mock.closeFileCallCount == 1) + } +} + +private class UnopenableFileFactory: FileFactory { + + struct OpenFileError: Error {} + + func makeInstance(forWritingTo: URL) throws -> File { + throw OpenFileError() + } +} + +private class FileMockFactory: FileFactory { + + let mock = FileMock() + + func makeInstance(forWritingTo: URL) throws -> File { + return mock + } +} + +private class FileMock: File { + + private(set) var writtenData = Data() + private(set) var synchronizeFileCallCount = 0 + private(set) var closeFileCallCount = 0 + + func seekToEndOfFile() -> UInt64 { + return 0 + } + + func swift_write(_ data: Data) throws { + self.writtenData.append(data) + } + + func synchronizeFile() { + synchronizeFileCallCount += 1 + } + + func closeFile() { + closeFileCallCount += 1 + } +} From cedf3a2b52472cd5d6c75284a6c83f0d9835629b Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sat, 1 Sep 2018 08:57:49 +0200 Subject: [PATCH 15/16] Added DiskLoggerTests --- Logger.xcodeproj/project.pbxproj | 4 + UnitTests/DiskLoggerTests.swift | 241 +++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 UnitTests/DiskLoggerTests.swift diff --git a/Logger.xcodeproj/project.pbxproj b/Logger.xcodeproj/project.pbxproj index 6a86dba..0b36d05 100644 --- a/Logger.xcodeproj/project.pbxproj +++ b/Logger.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 2ED077DB2132B0320058EEFC /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED077DA2132B0320058EEFC /* FileSystem.swift */; }; 2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E02135C61100EB3683 /* FileRotateTests.swift */; }; 2ED103E32135D3FB00EB3683 /* FileWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */; }; + 2ED103E52138553B00EB3683 /* DiskLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ED103E42138553B00EB3683 /* DiskLoggerTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -68,6 +69,7 @@ 2ED077DA2132B0320058EEFC /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = "<group>"; }; 2ED103E02135C61100EB3683 /* FileRotateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRotateTests.swift; sourceTree = "<group>"; }; 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWriterTests.swift; sourceTree = "<group>"; }; + 2ED103E42138553B00EB3683 /* DiskLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskLoggerTests.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,6 +97,7 @@ 2E58D35C21316C3500BEF81A /* LogStringConvertibleTests.swift */, 2ED077D621329CA30058EEFC /* LoggetTests.swift */, 2ED077D82132A4820058EEFC /* AgregateLoggerTests.swift */, + 2ED103E42138553B00EB3683 /* DiskLoggerTests.swift */, 2ED103E22135D3FB00EB3683 /* FileWriterTests.swift */, 2ED103E02135C61100EB3683 /* FileRotateTests.swift */, 2E58D35E21316C3500BEF81A /* Info.plist */, @@ -247,6 +250,7 @@ files = ( 2E58D35D21316C3500BEF81A /* LogStringConvertibleTests.swift in Sources */, 2ED103E32135D3FB00EB3683 /* FileWriterTests.swift in Sources */, + 2ED103E52138553B00EB3683 /* DiskLoggerTests.swift in Sources */, 2ED077D92132A4820058EEFC /* AgregateLoggerTests.swift in Sources */, 2ED103E12135C61100EB3683 /* FileRotateTests.swift in Sources */, 2ED077D721329CA30058EEFC /* LoggetTests.swift in Sources */, diff --git a/UnitTests/DiskLoggerTests.swift b/UnitTests/DiskLoggerTests.swift new file mode 100644 index 0000000..aaededc --- /dev/null +++ b/UnitTests/DiskLoggerTests.swift @@ -0,0 +1,241 @@ +// +// MIT License +// +// Copyright (c) 2018 Wojciech Nagrodzki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import XCTest +@testable import Logger + +class DiskLoggerTests: XCTestCase { + + let logURL = URL(fileURLWithPath: "/var/log/application.log") + + func testLoggingMessageToFile() { + let expectation = XCTestExpectation(description: "write(_:) was called on SizeLimitedFile") + expectation.expectedFulfillmentCount = 1 + + let filesystem = FileSystemStub() + let sizeLimitedFileFactory = SizeLimitedFileMockFactory(writeCall: expectation) + let logrotateFactory = LogrotateMockFactory() + let logger = DiskLogger(fileURL: logURL, + fileSizeLimit: 1024, + rotations: 1, + fileSystem: filesystem, + sizeLimitedFileFactory: sizeLimitedFileFactory, + logrotateFactory: logrotateFactory) + + logger.log("message", level: .critical) + + let result = XCTWaiter().wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(result, .completed) + + XCTAssertEqual(sizeLimitedFileFactory.files.count, 1) + + // "2018-08-30 17:23:11.514 <crit> DiskLoggerTests:46 testWritingMessageToFile() a message\n" + let loggedMessage = String(decoding: sizeLimitedFileFactory.files[0].data, as: UTF8.self) + let expectedSuffix = " <crit> DiskLoggerTests:46 testLoggingMessageToFile() message\n" + + XCTAssertTrue(loggedMessage.hasSuffix(expectedSuffix)) + } + + func testExceedingFileSizeLimit() { + let expectation = XCTestExpectation(description: "write(_:) was called on SizeLimitedFile") + expectation.expectedFulfillmentCount = 2 + + let filesystem = FileSystemStub() + let sizeLimitedFileFactory = SizeLimitedFileMockFactory(writeCall: expectation) + let logrotateFactory = LogrotateMockFactory() + let logger = DiskLogger(fileURL: logURL, + fileSizeLimit: 91, + rotations: 1, + fileSystem: filesystem, + sizeLimitedFileFactory: sizeLimitedFileFactory, + logrotateFactory: logrotateFactory) + + logger.log("1st message", level: .critical) + logger.log("2st message", level: .critical) + + let result = XCTWaiter().wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(result, .completed) + + XCTAssertEqual(sizeLimitedFileFactory.files.count, 2) + + // "2018-08-31 18:29:34.748 <crit> DiskLoggerTests:75 testExceedingFileSizeLimit() 2st message\n" + let loggedMessage = String(decoding: sizeLimitedFileFactory.files[1].data, as: UTF8.self) + let expectedSuffix = " <crit> DiskLoggerTests:75 testExceedingFileSizeLimit() 2st message\n" + + XCTAssertTrue(loggedMessage.hasSuffix(expectedSuffix)) + } + + func testErrorDuringLogProcess() { + let expectation = XCTestExpectation(description: "write(_:) was called on SizeLimitedFile") + expectation.expectedFulfillmentCount = 2 + + let filesystem = FileSystemStub() + let sizeLimitedFileFactory = UnwritableFileStubFactory(writeCall: expectation) + let logrotateFactory = LogrotateMockFactory() + let logger = DiskLogger(fileURL: logURL, + fileSizeLimit: 91, + rotations: 1, + fileSystem: filesystem, + sizeLimitedFileFactory: sizeLimitedFileFactory, + logrotateFactory: logrotateFactory) + + logger.log("1st message", level: .critical) + logger.log("2st message", level: .critical) + + let result = XCTWaiter().wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(result, .completed) + + XCTAssertEqual(sizeLimitedFileFactory.files.count, 1) + + // "2018-08-31 18:29:34.748 <crit> DiskLoggerTests:75 testExceedingFileSizeLimit() 2st message\n" + let loggedMessage = String(decoding: sizeLimitedFileFactory.files[0].data, as: UTF8.self) + let expectedOccurence = " <warning> WriteFailed()" + + XCTAssertTrue(loggedMessage.contains(expectedOccurence)) + } +} + +private class FileSystemStub: FileSystem { + func itemExists(at URL: URL) -> Bool { + return false + } + + func removeItem(at URL: URL) throws { + + } + + func moveItem(at srcURL: URL, to dstURL: URL) throws { + + } + + func createFile(at URL: URL) -> Bool { + return true + } +} + +private class SizeLimitedFileMockFactory: SizeLimitedFileFactory { + + private(set) var files = [SizeLimitedFileMock]() + private let writeCall: XCTestExpectation + + init(writeCall: XCTestExpectation) { + self.writeCall = writeCall + } + + func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile { + let mock = SizeLimitedFileMock(writeCall: writeCall, fileSizeLimit: fileSizeLimit) + files.append(mock) + return mock + } +} + +private class SizeLimitedFileMock: SizeLimitedFile { + + private(set) var data = Data() + private(set) var synchronizeAndCloseFileCallCount = 0 + private let writeCall: XCTestExpectation + private let fileSizeLimit: UInt64 + + init(writeCall: XCTestExpectation, fileSizeLimit: UInt64) { + self.writeCall = writeCall + self.fileSizeLimit = fileSizeLimit + } + + func write(_ data: Data) throws { + guard data.count + self.data.count <= fileSizeLimit else { + throw SizeLimitedFileQuotaReached() + } + self.data.append(data) + writeCall.fulfill() + } + + func synchronizeAndCloseFile() { + synchronizeAndCloseFileCallCount += 1 + } +} + +private class UnwritableFileStubFactory: SizeLimitedFileFactory { + + private(set) var files = [UnwritableFileStub]() + private let writeCall: XCTestExpectation + + init(writeCall: XCTestExpectation) { + self.writeCall = writeCall + } + + func makeInstance(fileURL: URL, fileSizeLimit: UInt64) throws -> SizeLimitedFile { + let file = UnwritableFileStub(writeCall: writeCall, fileSizeLimit: fileSizeLimit) + files.append(file) + return file + } +} + +private class UnwritableFileStub: SizeLimitedFile { + + struct WriteFailed: Error {} + + private(set) var data = Data() + private let writeCall: XCTestExpectation + private var didThrowError = false + + init(writeCall: XCTestExpectation, fileSizeLimit: UInt64) { + self.writeCall = writeCall + } + + func write(_ data: Data) throws { + if didThrowError { + self.data.append(data) + writeCall.fulfill() + } + else { + didThrowError = true + writeCall.fulfill() + throw WriteFailed() + } + } + + func synchronizeAndCloseFile() { + + } +} + +private class LogrotateMockFactory: LogrotateFactory { + + private(set) var logrotates = [LogrotateMock]() + + func makeInstance(fileURL: URL, rotations: Int) -> Logrotate { + let mock = LogrotateMock() + logrotates.append(mock) + return mock + } +} + +private class LogrotateMock: Logrotate { + + private(set) var rotateCallCount = 0 + + func rotate() throws { + rotateCallCount += 1 + } +} From 677db8a73a8d7266564f74f517fb9b6380a58367 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sat, 1 Sep 2018 08:59:25 +0200 Subject: [PATCH 16/16] Fixed two bugs caught by unit tests in DiskLogger --- Logger/Loggers/DiskLogger/DiskLogger.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Logger/Loggers/DiskLogger/DiskLogger.swift b/Logger/Loggers/DiskLogger/DiskLogger.swift index 6718d09..c2a44d0 100644 --- a/Logger/Loggers/DiskLogger/DiskLogger.swift +++ b/Logger/Loggers/DiskLogger/DiskLogger.swift @@ -130,10 +130,12 @@ public final class DiskLogger: Logger { catch is SizeLimitedFileQuotaReached { self.closeSizeLimitedFile() try self.rotateLogFiles() + try self.openSizeLimitedFile() + try self.writeBuffer() } } catch { - let message = self.formatter.string(from: Date()) + " <" + LogLevel.warning.logDescription + "> " + String(describing: error) + let message = self.formatter.string(from: Date()) + " <" + LogLevel.warning.logDescription + "> " + String(describing: error) + "\n" let data = Data(message.utf8) self.buffer.append(data) }