Repository: chicio/ID3TagEditor (swift)

Issue:
ID3Tag: add Lyrics frame in debugDescription and add tests to this property
**Is your feature request related to a problem? Please describe.**
Lyrics frame should be include in `debugDescription` of `ID3Tag`. 

**Additional context**
Search for `ID3Tag.debugDescription` in the code.

Pull request:
Added unsynchronised lyrics to debug description

## Description
* modified `ID3Tag` class to print out USTL frame if it exists
* added tests for the debugDescription property

## Motivation and Context
debug description should contain a complete view of the tag to help debug problems.

## How Has This Been Tested?
Unit tests

## Types of changes
- [X] New feature :sparkles: (non-breaking change which adds functionality)

## Checklist:
- [X] My code follows the code style of this project :beers:.
- [X] My change requires a change to the documentation :bulb: and I have updated the documentation accordingly.
- [X] I have read the [CONTRIBUTING](https://github.com/chicio/ID3TagEditor/blob/master/CONTRIBUTING.md) document :busts_in_silhouette:.
- [X] I have added tests to cover my changes :tada:.
- [X] All new and existing tests passed :white_check_mark:.

Patch:
diff --git a/ID3TagEditor.xcodeproj/project.pbxproj b/ID3TagEditor.xcodeproj/project.pbxproj
index 07728084..cd8ca142 100644
--- a/ID3TagEditor.xcodeproj/project.pbxproj
+++ b/ID3TagEditor.xcodeproj/project.pbxproj
@@ -563,6 +563,9 @@
 		45FA7DCD209917C6000A7484 /* ID3AlbumArtistFrameCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FA7DC720990873000A7484 /* ID3AlbumArtistFrameCreator.swift */; };
 		45FA7DCE209917C8000A7484 /* ID3AlbumArtistFrameCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FA7DC720990873000A7484 /* ID3AlbumArtistFrameCreator.swift */; };
 		45FA7DCF209917C8000A7484 /* ID3AlbumArtistFrameCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FA7DC720990873000A7484 /* ID3AlbumArtistFrameCreator.swift */; };
+		45FFF108253EEB0E0083C6E6 /* ID3TagTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FFF107253EEB0E0083C6E6 /* ID3TagTest.swift */; };
+		45FFF109253EEB0E0083C6E6 /* ID3TagTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FFF107253EEB0E0083C6E6 /* ID3TagTest.swift */; };
+		45FFF10A253EEB0E0083C6E6 /* ID3TagTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FFF107253EEB0E0083C6E6 /* ID3TagTest.swift */; };
 		5151CACD240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5151CACC240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift */; };
 		5151CACE240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5151CACC240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift */; };
 		5151CACF240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5151CACC240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift */; };
@@ -1136,6 +1139,7 @@
 		45F833E3205904680046C804 /* ID3TagEditor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ID3TagEditor.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		45FA7DC720990873000A7484 /* ID3AlbumArtistFrameCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ID3AlbumArtistFrameCreator.swift; sourceTree = "<group>"; };
 		45FA7DC9209917BF000A7484 /* ID3AlbumArtistFrameCreatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ID3AlbumArtistFrameCreatorTest.swift; sourceTree = "<group>"; };
+		45FFF107253EEB0E0083C6E6 /* ID3TagTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ID3TagTest.swift; sourceTree = "<group>"; };
 		5151CACC240D8E60002A7C05 /* ID3FrameWithIntegerContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ID3FrameWithIntegerContent.swift; sourceTree = "<group>"; };
 		5151CAD1240D9253002A7C05 /* ID3iTunesMovementCountFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ID3iTunesMovementCountFrame.swift; sourceTree = "<group>"; };
 		5151CAD6240D943D002A7C05 /* ID3iTunesMovementCountFrameContentParsingOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ID3iTunesMovementCountFrameContentParsingOperationFactory.swift; sourceTree = "<group>"; };
@@ -2100,6 +2104,7 @@
 			isa = PBXGroup;
 			children = (
 				C50678397D8313DE97785976 /* ID3TagPresenceTest.swift */,
+				45FFF107253EEB0E0083C6E6 /* ID3TagTest.swift */,
 			);
 			path = Tag;
 			sourceTree = "<group>";
@@ -2785,6 +2790,7 @@
 				45775F01210A65E400B1B3FE /* ID3RecordingTimestampOperationTest.swift in Sources */,
 				C5067BEC47B6904F785C4858 /* ID3ArtistFrameCreatorTest.swift in Sources */,
 				C5067AA87AF01780F9AC3637 /* ID3AttachedPicturesFrameCreatorsTest.swift in Sources */,
+				45FFF108253EEB0E0083C6E6 /* ID3TagTest.swift in Sources */,
 				C5067BD1C050694A3AE6F77B /* MockFrameContentSizeCalculator.swift in Sources */,
 				45D953EB25371922008AA05D /* ID3UnsynchronisedLyricForLanguageFrameCreatorTest.swift in Sources */,
 				45537A6B20932E9F009F639C /* ID3UTF16StringToByteAdapterTest.swift in Sources */,
@@ -3033,6 +3039,7 @@
 				45775F02210A65E400B1B3FE /* ID3RecordingTimestampOperationTest.swift in Sources */,
 				C50671D87F09A66699907CF0 /* ID3ArtistFrameCreatorTest.swift in Sources */,
 				C5067A2D29C081E56E4ED2A7 /* ID3AttachedPicturesFrameCreatorsTest.swift in Sources */,
+				45FFF109253EEB0E0083C6E6 /* ID3TagTest.swift in Sources */,
 				C5067DA770FF7CD970A11DA3 /* MockFrameContentSizeCalculator.swift in Sources */,
 				45537A6C20932E9F009F639C /* ID3UTF16StringToByteAdapterTest.swift in Sources */,
 				5F6A0D2D251E3F2200989D7E /* PathLoaderXcodeProj.swift in Sources */,
@@ -3281,6 +3288,7 @@
 				45775F03210A65E400B1B3FE /* ID3RecordingTimestampOperationTest.swift in Sources */,
 				45541BF2205995F60025A8BF /* ID3AttachedPicturesFrameCreatorsTest.swift in Sources */,
 				45541BF3205995F60025A8BF /* ID3FrameCreatorsChainTest.swift in Sources */,
+				45FFF10A253EEB0E0083C6E6 /* ID3TagTest.swift in Sources */,
 				45541BF4205995F60025A8BF /* ID3FrameFromStringContentCreatorTest.swift in Sources */,
 				45537A6D20932E9F009F639C /* ID3UTF16StringToByteAdapterTest.swift in Sources */,
 				5F6A0D2E251E3F2200989D7E /* PathLoaderXcodeProj.swift in Sources */,
diff --git a/Source/Frame/ID3FrameUnsynchronisedLyrics.swift b/Source/Frame/ID3FrameUnsynchronisedLyrics.swift
index 49c61fb0..ded170f0 100644
--- a/Source/Frame/ID3FrameUnsynchronisedLyrics.swift
+++ b/Source/Frame/ID3FrameUnsynchronisedLyrics.swift
@@ -8,9 +8,19 @@
 
 import Foundation
 
-public class ID3FrameUnsynchronisedLyrics: ID3FrameWithStringContent {
+public class ID3FrameUnsynchronisedLyrics: ID3FrameWithStringContent, CustomDebugStringConvertible {
+    /// The language of the lyrics contained in the frame
     public let language: ID3FrameContentLanguage
+    /// A short description of the lyrics contained in the frame
     public let contentDescription: String
+    /// ID3FrameAttachedPicture debug description.
+    public var debugDescription: String {
+        return """
+            language: \(language)
+            contentDescription: \(contentDescription)
+            content: \(content)
+            """
+    }
 
     public init(language: ID3FrameContentLanguage, contentDescription: String, content: String) {
         self.language = language
diff --git a/Source/Tag/ID3Tag.swift b/Source/Tag/ID3Tag.swift
index 66073b6c..6569187e 100644
--- a/Source/Tag/ID3Tag.swift
+++ b/Source/Tag/ID3Tag.swift
@@ -57,9 +57,20 @@ public class ID3Tag: CustomDebugStringConvertible {
         - recordingDayMonth: \((self.frames[.recordingDayMonth] as? ID3FrameRecordingDayMonth)?.debugDescription ?? "-")
         - recordingHourMinute: \((self.frames[.recordingHourMinute] as? ID3FrameRecordingHourMinute)?.debugDescription ?? "-")
         - genre: \((self.frames[.genre] as? ID3FrameGenre)?.debugDescription ?? "-")
-        - attachedPicture: \(ID3PictureType.allCases.reduce("", {
-            $0 + " - " + ((self.frames[.attachedPicture($1)] as? ID3FrameAttachedPicture)?.debugDescription ?? "")
-        }) )
+        - attachedPicture:\(ID3PictureType.allCases.reduce("", { (accumulator: String, current: ID3PictureType) in
+            if let frameDescription = (self.frames[.attachedPicture(current)] as? ID3FrameAttachedPicture)?.debugDescription {
+                return accumulator + "\n" + frameDescription + "\n"
+            }
+
+            return accumulator
+        }))
+        - unsynchronisedLyrics:\(ID3FrameContentLanguage.allCases.reduce("", { (accumulator: String, current: ID3FrameContentLanguage) in
+            if let frameDescription = (self.frames[.unsynchronizedLyrics(current)] as? ID3FrameUnsynchronisedLyrics)?.debugDescription {
+                return accumulator + "\n" + frameDescription + "\n"
+            }
+
+            return accumulator
+        }))
         """
     }
 


Test patch:
diff --git a/Tests/Tag/ID3TagTest.swift b/Tests/Tag/ID3TagTest.swift
new file mode 100644
index 00000000..43af842d
--- /dev/null
+++ b/Tests/Tag/ID3TagTest.swift
@@ -0,0 +1,216 @@
+//
+//  ID3TagTest.swift
+//  ID3TagEditor
+//
+//  Created by Fabrizio Duroni on 20.10.20.
+//  2020 Fabrizio Duroni.
+//
+
+// swiftlint:disable line_length
+// swiftlint:disable function_body_length
+
+import XCTest
+@testable import ID3TagEditor
+
+class ID3TagTest: XCTestCase {
+    static let allTests = [
+        ("testDebugDescriptionv3", testDebugDescriptionv3),
+        ("testDebugDescriptionv4", testDebugDescriptionv4)
+    ]
+
+    func testDebugDescriptionv3() throws {
+        let artFront = try Data(
+            contentsOf: URL(fileURLWithPath: PathLoader().pathFor(name: "example-cover", fileType: "jpg"))
+        )
+        let artBack = try Data(
+            contentsOf: URL(fileURLWithPath: PathLoader().pathFor(name: "cover2", fileType: "jpg"))
+        )
+
+        let id3Tag = ID3Tag(
+            version: .version3,
+            frames: [
+                .title: ID3FrameWithStringContent(content: "title V3"),
+                .album: ID3FrameWithStringContent(content: "album V3"),
+                .albumArtist: ID3FrameWithStringContent(content: "album artist V3"),
+                .artist: ID3FrameWithStringContent(content: "artist V3"),
+                .composer: ID3FrameWithStringContent(content: "composer V3"),
+                .conductor: ID3FrameWithStringContent(content: "conductor V3"),
+                .contentGrouping: ID3FrameWithStringContent(content: "ContentGrouping V3"),
+                .copyright: ID3FrameWithStringContent(content: "Copyright V3"),
+                .encodedBy: ID3FrameWithStringContent(content: "EncodedBy V3"),
+                .encoderSettings: ID3FrameWithStringContent(content: "EncoderSettings V3"),
+                .fileOwner: ID3FrameWithStringContent(content: "FileOwner V3"),
+                .lyricist: ID3FrameWithStringContent(content: "Lyricist V3"),
+                .mixArtist: ID3FrameWithStringContent(content: "MixArtist V3"),
+                .publisher: ID3FrameWithStringContent(content: "Publisher V3"),
+                .subtitle: ID3FrameWithStringContent(content: "Subtitle V3"),
+                .genre: ID3FrameGenre(genre: .metal, description: "Metalcore"),
+                .discPosition: ID3FramePartOfTotal(part: 1, total: 3),
+                .trackPosition: ID3FramePartOfTotal(part: 2, total: 9),
+                .recordingDayMonth: ID3FrameRecordingDayMonth(day: 5, month: 8),
+                .recordingYear: ID3FrameRecordingYear(year: 2020),
+                .recordingHourMinute: ID3FrameRecordingHourMinute(hour: 15, minute: 39),
+                .attachedPicture(.frontCover): ID3FrameAttachedPicture(picture: artFront, type: .frontCover, format: .jpeg),
+                .attachedPicture(.backCover): ID3FrameAttachedPicture(picture: artBack, type: .backCover, format: .jpeg),
+                .unsynchronizedLyrics(.ita): ID3FrameUnsynchronisedLyrics(language: ID3FrameContentLanguage.ita, contentDescription: "CD", content: "v3 ita unsync lyrics"),
+                .unsynchronizedLyrics(.eng): ID3FrameUnsynchronisedLyrics(language: ID3FrameContentLanguage.eng, contentDescription: "CD", content: "v3 eng unsync lyrics"),
+                .iTunesGrouping: ID3FrameWithStringContent(content: "ItunesGrouping V3"),
+                .iTunesMovementName: ID3FrameWithStringContent(content: "MovementName V3"),
+                .iTunesMovementIndex: ID3FrameWithIntegerContent(value: 6),
+                .iTunesMovementCount: ID3FrameWithIntegerContent(value: 13),
+                .iTunesPodcastCategory: ID3FrameWithStringContent(content: "PodcastCategory V3"),
+                .iTunesPodcastDescription: ID3FrameWithStringContent(content: "PodcastDescription V3"),
+                .iTunesPodcastID: ID3FrameWithStringContent(content: "PodcastID V3"),
+                .iTunesPodcastKeywords: ID3FrameWithStringContent(content: "PodcastKeywords V3")
+            ]
+        )
+
+        XCTAssertEqual(
+            id3Tag.debugDescription,
+                       """
+            ID3Tag:
+            - size: 0
+            - version: version3
+            - artist: artist V3
+            - composer: composer V3
+            - conductor: conductor V3
+            - contentGrouping: ContentGrouping V3
+            - copyright: Copyright V3
+            - encodedBy: EncodedBy V3
+            - encoderSettings: EncoderSettings V3
+            - fileOwner: FileOwner V3
+            - iTunesGrouping: ItunesGrouping V3
+            - lyricist: Lyricist V3
+            - mixArtist: MixArtist V3
+            - iTunesMovementIndex: 6
+            - iTunesMovementCount: 13
+            - iTunesMovementName: MovementName V3
+            - podcastCategory: PodcastCategory V3
+            - podcastDescription: PodcastDescription V3
+            - podcastID: PodcastID V3
+            - podcastKeywords: PodcastKeywords V3
+            - publisher: Publisher V3
+            - subtitle: Subtitle V3
+            - albumArtist: album artist V3
+            - title: title V3
+            - trackPosition: 2 of 9
+            - discPosition: 1 of 3
+            - album: album V3
+            - recordingDateTime: -
+            - recordingYear: 2020
+            - recordingDayMonth: 5 8
+            - recordingHourMinute: 15 39
+            - genre: Optional(ID3TagEditor.ID3Genre.metal) - Optional("Metalcore")
+            - attachedPicture:
+            frontCover jpeg
+
+            backCover jpeg
+
+            - unsynchronisedLyrics:
+            language: eng
+            contentDescription: CD
+            content: v3 eng unsync lyrics
+
+            language: ita
+            contentDescription: CD
+            content: v3 ita unsync lyrics
+
+            """)
+    }
+
+    func testDebugDescriptionv4() throws {
+        let artFront: Data = try Data(contentsOf: URL(fileURLWithPath: PathLoader().pathFor(name: "example-cover", fileType: "jpg")))
+        let artBack: Data = try Data(contentsOf: URL(fileURLWithPath: PathLoader().pathFor(name: "cover2", fileType: "jpg")))
+
+        let id3Tag = ID3Tag(
+            version: .version4,
+            frames: [
+                .title: ID3FrameWithStringContent(content: "title V4"),
+                .album: ID3FrameWithStringContent(content: "album V4"),
+                .albumArtist: ID3FrameWithStringContent(content: "album artist V4"),
+                .artist: ID3FrameWithStringContent(content: "artist V4"),
+                .composer: ID3FrameWithStringContent(content: "composer V4"),
+                .conductor: ID3FrameWithStringContent(content: "conductor V4"),
+                .contentGrouping: ID3FrameWithStringContent(content: "ContentGrouping V4"),
+                .copyright: ID3FrameWithStringContent(content: "Copyright V4"),
+                .encodedBy: ID3FrameWithStringContent(content: "EncodedBy V4"),
+                .encoderSettings: ID3FrameWithStringContent(content: "EncoderSettings V4"),
+                .fileOwner: ID3FrameWithStringContent(content: "FileOwner V4"),
+                .lyricist: ID3FrameWithStringContent(content: "Lyricist V4"),
+                .mixArtist: ID3FrameWithStringContent(content: "MixArtist V4"),
+                .publisher: ID3FrameWithStringContent(content: "Publisher V4"),
+                .subtitle: ID3FrameWithStringContent(content: "Subtitle V4"),
+                .genre: ID3FrameGenre(genre: .metal, description: "Metalcore"),
+                .discPosition: ID3FramePartOfTotal(part: 1, total: 3),
+                .trackPosition: ID3FramePartOfTotal(part: 2, total: 9),
+                .recordingDateTime: ID3FrameRecordingDateTime(recordingDateTime: RecordingDateTime(date: RecordingDate(day: 15, month: 10, year: 2020),
+                                                                                                    time: RecordingTime(hour: 21, minute: 50))),
+                .attachedPicture(.frontCover): ID3FrameAttachedPicture(picture: artFront, type: .frontCover, format: .jpeg),
+                .attachedPicture(.backCover): ID3FrameAttachedPicture(picture: artBack, type: .backCover, format: .jpeg),
+                .unsynchronizedLyrics(.ita): ID3FrameUnsynchronisedLyrics(language: ID3FrameContentLanguage.ita, contentDescription: "CD", content: "V4 ita unsync lyrics"),
+                .unsynchronizedLyrics(.eng): ID3FrameUnsynchronisedLyrics(language: ID3FrameContentLanguage.eng, contentDescription: "CD", content: "V4 eng unsync lyrics"),
+                .iTunesGrouping: ID3FrameWithStringContent(content: "ItunesGrouping V4"),
+                .iTunesMovementName: ID3FrameWithStringContent(content: "MovementName V4"),
+                .iTunesMovementIndex: ID3FrameWithIntegerContent(value: 6),
+                .iTunesMovementCount: ID3FrameWithIntegerContent(value: 13),
+                .iTunesPodcastCategory: ID3FrameWithStringContent(content: "PodcastCategory V4"),
+                .iTunesPodcastDescription: ID3FrameWithStringContent(content: "PodcastDescription V4"),
+                .iTunesPodcastID: ID3FrameWithStringContent(content: "PodcastID V4"),
+                .iTunesPodcastKeywords: ID3FrameWithStringContent(content: "PodcastKeywords V4")
+            ]
+        )
+
+        XCTAssertEqual(
+            id3Tag.debugDescription,
+            """
+            ID3Tag:
+            - size: 0
+            - version: version4
+            - artist: artist V4
+            - composer: composer V4
+            - conductor: conductor V4
+            - contentGrouping: ContentGrouping V4
+            - copyright: Copyright V4
+            - encodedBy: EncodedBy V4
+            - encoderSettings: EncoderSettings V4
+            - fileOwner: FileOwner V4
+            - iTunesGrouping: ItunesGrouping V4
+            - lyricist: Lyricist V4
+            - mixArtist: MixArtist V4
+            - iTunesMovementIndex: 6
+            - iTunesMovementCount: 13
+            - iTunesMovementName: MovementName V4
+            - podcastCategory: PodcastCategory V4
+            - podcastDescription: PodcastDescription V4
+            - podcastID: PodcastID V4
+            - podcastKeywords: PodcastKeywords V4
+            - publisher: Publisher V4
+            - subtitle: Subtitle V4
+            - albumArtist: album artist V4
+            - title: title V4
+            - trackPosition: 2 of 9
+            - discPosition: 1 of 3
+            - album: album V4
+            - recordingDateTime: Date: (15 10 2020 - Time: (21 50)
+            - recordingYear: -
+            - recordingDayMonth: -
+            - recordingHourMinute: -
+            - genre: Optional(ID3TagEditor.ID3Genre.metal) - Optional("Metalcore")
+            - attachedPicture:
+            frontCover jpeg
+
+            backCover jpeg
+
+            - unsynchronisedLyrics:
+            language: eng
+            contentDescription: CD
+            content: V4 eng unsync lyrics
+
+            language: ita
+            contentDescription: CD
+            content: V4 ita unsync lyrics
+
+            """
+        )
+    }
+}
diff --git a/Tests/XCTestManifests.swift b/Tests/XCTestManifests.swift
index 9c625346..bff44b54 100644
--- a/Tests/XCTestManifests.swift
+++ b/Tests/XCTestManifests.swift
@@ -525,7 +525,8 @@ public func __allTests() -> [XCTestCaseEntry] {
         testCase(ID3FrameHeaderCreatorTest.__allTests),
         testCase(ID3UnsynchronisedLyricsFrameContentParsingOperationTest.__allTests),
         testCase(ID3TagEditorAcceptanceTest.__allTests),
-        testCase(ID3TagEditorWriteReadAcceptanceTest.__allTests)
+        testCase(ID3TagEditorWriteReadAcceptanceTest.__allTests),
+        testCase(ID3TagTest.allTests)
     ]
 }
 #endif