class TAGLIB_TEST_SET

(source code)

description

Tag info test set

note
	description: "Tag info test set"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2024-09-23 13:31:17 GMT (Monday 23rd September 2024)"
	revision: "54"

class
	TAGLIB_TEST_SET

inherit
	EL_COPIED_FILE_DATA_TEST_SET
		undefine
			new_lio
		redefine
			on_clean
		end

	EL_CRC_32_TESTABLE

	EL_MODULE_NAMING; EL_MODULE_TUPLE

	EL_LOGGABLE_CONSTANTS

	TL_SHARED_PICTURE_TYPE_ENUM

	TL_SHARED_ONCE_STRING_LIST

	SHARED_DEV_ENVIRON

	EL_SHARED_TEST_TEXT

create
	make

feature {NONE} -- Initialization

	make
		-- initialize `test_table'
		do
			make_named (<<
				["comments", agent test_comments],
				["musicbrainz", agent test_musicbrainz],
				["picture_edit", agent test_picture_edit],
				["picture_mime_types", agent test_picture_mime_types],
				["get_set_basic_fields", agent test_get_set_basic_fields],
				["read_v2_frames", agent test_read_v2_frames],
				["string_conversion", agent test_string_conversion],
				["string_list", agent test_string_list],
				["string_setting", agent test_string_setting],
				["ufid", agent test_ufid],
				["user_text", agent test_user_text],
				["major_version_change", agent test_major_version_change]
			>>)
		end

feature -- Tests

	test_comments
		local
			mp3: TL_MPEG_FILE; preference: ZSTRING; musicmatch: STRING
			table: EL_HASH_TABLE [STRING, STRING]
		do
			musicmatch := "MusicMatch_"
			table := <<
				["Tempo", "Pretty fast"], ["Mood", "Upbeat"], ["Situation", "Any"], ["Preference", "Excellent"]
			>>
			file_list.find_first_base (That_spot_tag)
			assert ("exists", file_list.found)
			create mp3.make (file_list.path)
			across table as str loop
				assert_same_string (Void, mp3.tag.comment_with (musicmatch + str.key).text, str.item)
			end

			table := <<
				["First_line", "In that spot, over here in that spot."], -- Test new comment
				["Preference", "5 stars"]
			>>
			across table as str loop
				mp3.tag.set_comment_with (musicmatch + str.key, str.item)
				assert_same_string (Void, mp3.tag.comment_with (musicmatch + str.key).text, str.item)
			end
		end

	test_musicbrainz
		local
			mp3: TL_MUSICBRAINZ_MPEG_FILE; id: STRING
		do
			across file_list as path loop
				create mp3.make (path.item)
				if mp3.tag.version = 2 then
					id := Digest.md5 (path.item.base).to_base_64_string
					mp3.set_track_id (id)
					mp3.set_recording_id (id)
					assert ("track_id set", id ~ mp3.track_id)
					assert ("recording_id set", id ~ mp3.recording_id)
				end
			end
		end

	test_picture_edit
		local
			mp3: TL_MPEG_FILE; picture: TL_ID3_PICTURE
		do
			file_list.find_first_base (Picture_230_tag)
			if file_list.found then
				create mp3.make (file_list.path)
				create picture.make (Top_png_path, "Upwards arrow", Picture_type.other)
				mp3.tag.set_picture (picture)
				mp3.save
				mp3.dispose
				create mp3.make (file_list.path)
				assert ("same picture", picture ~ mp3.tag.picture)
			else
				failed (Picture_230_tag + " not found")
			end
		end

	test_picture_mime_types
		local
			picture: TL_ID3_PICTURE
			table: EL_HASH_TABLE [STRING, STRING]
		do
			table := <<
				["pic.jpeg", "image/jpeg"], ["pic.jpg", "image/jpeg"], ["pic.png", "image/png"]
			>>
			across table as pic loop
				create picture.make (pic.key, "", Picture_type.other)
				assert ("valid mime type", picture.mime_type ~ pic.item)
			end
		end

	test_get_set_basic_fields
		do
			across file_list as path loop
				do_test (
					"get_set_basic_fields", Checksum_table.item (path.item.base).get_set_basic_fields,
					agent get_set_basic_fields, [path.item.relative_path (Work_area_dir)]
				)
			end
		end

	test_read_v2_frames
		-- TAGLIB_TEST_SET.test_read_v2_frames
		do
			across file_list as path loop
				do_test (
					"print_v2_frames", Checksum_table.item (path.item.base).print_frames,
					agent print_v2_frames, [path.item.relative_path (Work_area_dir)]
				)
			end
		end

	test_string_conversion
		local
			tl_string: TL_STRING
		do
			create tl_string.make_empty
			across Text.lines_32 as line loop
				tl_string.set_from_string (line.item)
				assert ("same string", tl_string.to_string_32 ~ line.item)
			end
		end

	test_string_list
		local
			list: EL_ZSTRING_LIST
		do
			create list.make_adjusted_split ("one, two, three", ',', {EL_SIDE}.Left)
			Once_string_list.wipe_out
			Once_string_list.append (list)
			assert ("same list", list ~ Once_string_list.to_list)
		end

	test_string_setting
		-- TAGLIB_TEST_SET.string_setting
		local
			mp3: TL_MPEG_FILE; title: ZSTRING
		do
			across file_list as path loop
				create mp3.make (path.item)
				title := Text.lines_32.circular_i_th (path.cursor_index)
				if not mp3.tag.is_default then
					mp3.tag.set_title (title)
					assert ("title set", mp3.tag.title ~ title)
				end
			end
		end

	test_ufid
		local
			mp3: TL_MPEG_FILE; ufid: TL_UNIQUE_FILE_IDENTIFIER
			ufid_list: ARRAYED_LIST [TL_UNIQUE_FILE_IDENTIFIER]
			owner: ZSTRING; id: STRING
		do
			file_list.find_first_base (Unicode_230_tag)
			create ufid_list.make (2)
			if file_list.found then
				create mp3.make (file_list.path)
				across << "FJR", "MGH" >> as owner_id loop
					owner := owner_id.item; id := Digest.md5 (owner_id.item).to_base_64_string
					mp3.tag.set_unique_id (owner, id)
					ufid := mp3.tag.unique_id (owner)
					ufid_list.extend (ufid)
					assert ("not default", not ufid.is_default)
					assert ("same owner", owner ~ ufid.owner)
					assert ("same id", id ~ ufid.identifier)
				end
				across mp3.tag.unique_id_list as l_ufid loop
					assert ("same identifier", l_ufid.item.same_as (ufid_list.i_th (l_ufid.cursor_index)))
				end
			end
		end

	test_user_text
		local
			mp3: TL_MPEG_FILE; title: ZSTRING
			user_text_table: EL_HASH_TABLE [STRING, STRING]
			count: INTEGER
		do
			user_text_table := <<
				["compression example", "This sample user text"],
				["example text frame", "This text and the description"]
			>>
			across file_list as path loop
				create mp3.make (path.item)
				if mp3.tag.has_user_text then
					across user_text_table as table loop
						if mp3.tag.has_user_text_with (table.key) then
							assert ("text starts with", mp3.tag.user_text (table.key).starts_with_general (table.item))
							mp3.tag.set_user_text (table.key, table.item)
							assert ("same text", table.item ~ mp3.tag.user_text (table.key).to_latin_1)
							count := count + 1
						end
					end
				end
			end
			assert ("all examples found", count = user_text_table.count)
		end

	test_major_version_change
		local
			mp3: TL_MPEG_FILE
		do
			file_list.find_first_base (Silence_240_mp3)
			if file_list.found then
				create mp3.make (file_list.path)
				assert ("major version 4", mp3.id3_version.major = 4)
				mp3.save_version (3)
				mp3.dispose
				create mp3.make (file_list.path)
				assert ("major version 3", mp3.id3_version.major = 3)
			end
		end

feature {NONE} -- Implementation

	enclosed (a_str: ZSTRING): ZSTRING
		do
			Result := a_str.enclosed ('(', ')')
		end

	checksums (a_print_tag, a_print_frames: NATURAL): like Checksum_table.item
		do
			Result := [a_print_tag, a_print_frames]
		end

	print_field (name: STRING; value: ZSTRING)
		local
			list: EL_SPLIT_ZSTRING_LIST; s: EL_ZSTRING_ROUTINES
		do
			if value.has ('%N') then
				if value.has_substring (CR_new_line) then
					create list.make_by_string (value, CR_new_line)
				else
					create list.make_by_string (value, s.character_string ('%N'))
				end
				lio.put_labeled_string (name, "%"[")
				lio.tab_right
				lio.put_new_line
				across << list.first_item_copy, s.n_character_string ('.', 2), list.last_item_copy >> as line loop
					lio.put_string (line.item)
					if line.is_last then
						lio.tab_left
					end
					lio.put_new_line
				end
				lio.set_text_color (Color.Yellow)
				lio.put_string ("]%"")
				lio.set_text_color (Color.Default)
			elseif value.count >= 80 then
				lio.put_curtailed_string_field (name, value, 80)
			else
				lio.put_string_field (name, value)
			end
			lio.put_new_line
		end

	get_set_basic_fields (relative_path: FILE_PATH)
		local
			mp3: TL_MPEG_FILE; tag: TL_ID3_TAG; field_string, string: ZSTRING
			value, index: INTEGER
		do
			create mp3.make (Work_area_dir + relative_path)
			tag := mp3.tag
			print_version (mp3)
			if tag.version > 0 then
				across Get_set_names as name loop
					index := (name.cursor_index - 1) * 2 + 1
					if attached {FUNCTION [TL_ID3_TAG, ZSTRING]} Get_set_routines [index] as get_string then
						field_string := get_string (tag)
						if not field_string.is_empty then
							print_field (name.item, field_string)
						end
						if attached {PROCEDURE [TL_ID3_TAG, READABLE_STRING_GENERAL]} Get_set_routines [index + 1] as set_string then
							if field_string.starts_with_general ("THAT SPOT") then
								-- why is post-condition failing?
							else
								field_string := enclosed (field_string)
							end
							set_string (tag, field_string)
							string := get_string (tag)
							assert ("same string", get_string (tag) ~ field_string)
						end
					elseif attached {FUNCTION [TL_ID3_TAG, INTEGER]} Get_set_routines [index] as get_integer then
						value := get_integer (tag)
						if value > 0 then
							lio.put_integer_field (name.item, value)
							lio.put_new_line
							if attached {PROCEDURE [TL_ID3_TAG, INTEGER]} Get_set_routines [index + 1] as set_integer then
								set_integer (tag, value + 1)
								assert ("same value", get_integer (tag) = value + 1)
							end
						end
					end
				end
			end
		end

	print_v2_frames (relative_path: FILE_PATH)
		local
			mp3: TL_MPEG_FILE; name: STRING
		do
			create mp3.make (Work_area_dir + relative_path)
			if mp3.has_version_1 then
				print_version (mp3)

			elseif attached {TL_ID3_V2_TAG} mp3.tag as tag then
				print_version (mp3)
				across tag.all_frames_list as frame loop
					name := Naming.class_as_snake_upper (frame.item, 1, 2)
					lio.put_labeled_string (name, frame.item.id.to_string_8)
					if attached {TL_COMMENTS_ID3_FRAME} frame.item as comments then
						print_field ("; language", comments.language)
						print_field ("description", comments.description)
						print_field ("text", comments.text)
					elseif attached {TL_PICTURE_ID3_FRAME} frame.item as pic then
						lio.put_string_field ("; mime_type", pic.mime_type)
						lio.put_string_field ("; type", pic.type)
						lio.put_integer_field ("; byte count", pic.picture.count)
						lio.put_new_line
						print_field ("description", pic.description)
					elseif attached {TL_TEXT_IDENTIFICATION_ID3_FRAME} frame.item as id_frame then
						if attached {TL_USER_TEXT_IDENTIFICATION_ID3_FRAME} id_frame as user then
							lio.put_string_field ("; description", user.description)
						end
						lio.put_new_line
						across id_frame.field_list as field loop
							print_field (Array_template #$ ["field_list", field.cursor_index], field.item)
						end

					elseif attached {TL_UNIQUE_FILE_IDENTIFIER_ID3_FRAME} frame.item as l_unique then
						lio.put_string_field ("; identifier", l_unique.identifier)
						lio.put_string_field ("; owner", l_unique.owner)
						lio.put_new_line
					else
						print_field ("; text", frame.item.text)
					end
				end
			else
				lio.put_line ("Unknown version")
			end
		end

	print_version (mp3: TL_MPEG_FILE)
		do
			lio.put_labeled_substitution ("Version", "%S.%S.%S", mp3.id3_version)
			lio.put_new_line
		end

	source_file_list: EL_FILE_PATH_LIST
		do
			Result := OS.file_list (Data_dir, "*")
		end

feature {NONE} -- Events

	on_clean
		do
			{MEMORY}.full_collect
			Precursor {EL_COPIED_FILE_DATA_TEST_SET}
		end

feature {NONE} -- Constants

	Array_template: ZSTRING
		once
			Result := "%S [%S]"
		end

	CR_new_line: ZSTRING
		once
			Result := "%R%N"
		end

	Checksum_table: HASH_TABLE [TUPLE [get_set_basic_fields, print_frames: NATURAL], STRING]
		once
			create Result.make (11)
			Result ["221-compressed.tag"] := checksums (3085819510, 3246236924)
			Result ["230-compressed.tag"] := checksums (839599359, 2921631902)
			Result ["230-syncedlyrics.tag"] := checksums (1669786640, 4124037141)
			Result [Picture_230_tag] := checksums (1095970239, 32249346)
			Result [Unicode_230_tag] := checksums (109896957, 3709611927)
			Result ["ozzy.tag"] := checksums (3778416931, 67249581)
			Result [That_spot_tag] := checksums (4253022495, 1648797912)

			-- MP3 extension
			Result [Silence_240_mp3] := checksums (3490969276, 1488597223)
			Result ["crc53865.mp3"] := checksums (1317037298, 3992252498)
		end

	Data_dir: DIR_PATH
		once
			Result := Dev_environ.EL_test_data_dir #+ "id3$"
		end

	Get_set_routines: ARRAY [ROUTINE]
		once
			Result := <<
				agent {TL_ID3_TAG}.album, agent {TL_ID3_TAG}.set_album,
				agent {TL_ID3_TAG}.artist, agent {TL_ID3_TAG}.set_artist,
				agent {TL_ID3_TAG}.comment, agent {TL_ID3_TAG}.set_comment,
				agent {TL_ID3_TAG}.genre, agent {TL_ID3_TAG}.set_genre,
				agent {TL_ID3_TAG}.title, agent {TL_ID3_TAG}.set_title,
				agent {TL_ID3_TAG}.track, agent {TL_ID3_TAG}.set_track,
				agent {TL_ID3_TAG}.year, agent {TL_ID3_TAG}.set_year
			>>
		ensure
			twice_number_of_names: Result.count // Get_set_names.count = 2
		end

	Get_set_names: EL_STRING_8_LIST
		once
			create Result.make_adjusted_split ("album, artist, comment, genre, title, track, year", ',', {EL_SIDE}.Left)
		end

	Unicode_230_tag: ZSTRING
		once
			Result := "230-unicode.tag"
		end

	Picture_230_tag: ZSTRING
		once
			Result := "230-picture.tag"
		end

	Silence_240_mp3: ZSTRING
		once
			Result := "240-silence.mp3"
		end

	That_spot_tag: ZSTRING
		once
			Result := "thatspot.tag"
		end

	Top_png_path: FILE_PATH
		once
			Result := Dev_environ.Eiffel_loop_dir + "doc/images/top.png"
		end

end