class STRING_CONVERSION_TEST_SET

(source code)

description

Test string escaping and other string conversion tests

note
	description: "Test string escaping and other string conversion tests"

	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-20 7:59:13 GMT (Friday 20th September 2024)"
	revision: "31"

class
	STRING_CONVERSION_TEST_SET

inherit
	EL_EQA_TEST_SET

	EL_MODULE_CONVERT_STRING; EL_MODULE_FORMAT

	EL_SHARED_CYCLIC_REDUNDANCY_CHECK_32; EL_SHARED_TEST_TEXT; EL_SHARED_ESCAPE_TABLE

	EL_SHARED_ZCODEC_FACTORY

	EL_SHARED_ZSTRING_CODEC
		rename
			Codec as Codec_
		end

	EL_STRING_HANDLER

create
	make

feature {NONE} -- Initialization

	make
		-- initialize `test_table'
		do
			make_named (<<
				["bash_escape",							 agent test_bash_escape],
				["convert_string_to_makeable",		 agent test_convert_string_to_makeable],
				["convert_string_type_descriptions", agent test_convert_string_type_descriptions],
				["eiffel_string_escaped",				 agent test_eiffel_string_escaped],
				["encodeable_as_string_8",				 agent test_encodeable_as_string_8],
				["encoding_conversion",					 agent test_encoding_conversion],
				["immutable_to_integer_or_natural",	 agent test_immutable_to_integer_or_natural],
				["number_formatting",					 agent test_number_formatting],
				["python_escape",							 agent test_python_escape],
				["substitution_marker_unescape",		 agent test_substitution_marker_unescape],
				["unescape",								 agent test_unescape]
			>>)
		end

feature -- Tests

	test_bash_escape
		-- STRING_CONVERSION_TEST_SET.test_bash_escape
		local
			escaper: EL_STRING_ESCAPER [ZSTRING]; escaper_32: EL_STRING_ESCAPER [STRING_32]
		do
			create escaper.make (Escape_table.bash) ; create escaper_32.make (Escape_table.bash)
			escape_test ("BASH", escaper, escaper_32)
		end

	test_convert_string_to_makeable
		-- STRING_CONVERSION_TEST_SET.test_convert_string_to_makeable
		note
			testing:	"[
				covers/{EL_STRING_CONVERSION_TABLE}.is_convertible,
			 	covers/{EL_STRING_CONVERSION_TABLE}.make_from_zcode_area
			]"
		local
			encoding: EL_ENCODING; uuid: EL_UUID
			name: IMMUTABLE_STRING_8; uuid_string: STRING
		do
			name := "UTF-8"
			create encoding.make_from_name (name)
			assert ("is convertible", Convert_string.is_convertible (name, encoding.generating_type))
			assert ("same encoding", encoding ~ Convert_string.to_type (name, encoding.generating_type))

			create uuid.make_from_string ("A325754F-7BEB-44B6-937C-CC7EBDDA764F")
			uuid_string := uuid.to_string
			assert ("is convertible", Convert_string.is_convertible (uuid_string, uuid.generating_type))
			assert ("same uuid", uuid ~ Convert_string.to_type (uuid_string, uuid.generating_type))
		end

	test_convert_string_type_descriptions
		-- STRING_CONVERSION_TEST_SET.test_convert_string_type_descriptions
		note
			testing: "covers/{EL_READABLE_STRING_GENERAL_TO_TYPE}.new_type_description"
		do
			if attached crc_generator as crc then
				across Convert_string.type_list as list loop
					if attached Convert_string.type_descripton (list.item) as description then
						crc.add_string_8 (description)
						lio.put_labeled_string (list.item.name, description)
						lio.put_new_line
					end
				end
				if crc.checksum /= 1956217266 then
					lio.put_natural_field ("Actual checksum", crc.checksum)
					lio.put_new_line
					failed ("Expected descriptions")
				end
			end
		end

	test_eiffel_string_escaped
		-- STRING_CONVERSION_TEST_SET.test_eiffel_string_escaped
		local
			escaper: EL_STRING_ESCAPER [ZSTRING]; street, street_escaped: ZSTRING
			unescaper: EL_ZSTRING_UNESCAPER
		do
			create escaper.make (Escape_table.Eiffel)
			create unescaper.make (Escape_table.Eiffel.inverted)

			street := "line1%R%Nline2"; street_escaped := "line1%%R%%Nline2"
			assert_same_string ("Eiffel escaped", street.escaped (escaper), street_escaped)
			assert_same_string ("Eiffel unescaped", street_escaped.unescaped (unescaper), street)
		end

	test_encodeable_as_string_8
		-- STRING_CONVERSION_TEST_SET.test_encodeable_as_string_8
		local
			zstr: ZSTRING; codec: EL_ZCODEC; checksum_expected: NATURAL
			first_latin, first_windows: BOOLEAN
		do
			if attached crc_generator as crc then
				across Text.lines_32 as line loop
					zstr := line.item
					crc.add_string (zstr)
					lio.put_labeled_string ("LINE", zstr)
					lio.put_new_line
					first_latin := True; first_windows := True
					across Text.all_encodings as encoding loop
						codec := Codec_factory.codec_by (encoding.item)
						if codec.is_encodeable_as_string_8 (zstr, 1, zstr.count) then
							crc.add_natural (codec.id)
							if first_latin and codec.id <= 15 then
								lio.put_natural_field ("Latin", codec.id)
								first_latin := False
							elseif first_windows and codec.id > 1000 then
								lio.put_new_line
								lio.put_natural_field ("Windows", codec.id)
								first_windows := False
							else
								lio.put_string (", ")
								lio.put_natural (codec.id)
							end
						end
					end
					lio.put_new_line_x2
				end
				inspect Codec_.id
					when 1 then
						checksum_expected := 156405206
				else
					checksum_expected := 4117255750
				end
				assert ("is_encodeable_as_string_8 OK", crc.checksum = checksum_expected)
			end
		end

	test_encoding_conversion
		-- STRING_CONVERSION_TEST_SET.test_encoding_conversion
		local
			buffer: EL_STRING_8_IO_MEDIUM; encoding: EL_ENCODING
			zstr, name: ZSTRING; latin_id: INTEGER
		do
			create buffer.make (100)
			across Text.lines_32 as line loop
				if line.item.starts_with (Latin) then
					zstr := line.item
					name := zstr.substring_to (':')
					name.replace_substring_general ("ISO-8859", 1, Latin.count)
					create encoding.make_from_name (name)
					buffer.wipe_out
					if attached Codec_factory.codec (encoding) as codec then
						lio.put_labeled_string ("Codec", codec.name)
						lio.put_new_line
						codec.write_encoded (zstr, buffer)
						if zstr.count ~ buffer.text.count then
							if codec.id = Codec_factory.zstring_codec.id then
								assert ("same string_8", zstr.area.same_items (buffer.text.area, 0, 0, zstr.count))
							else
								assert ("same encoded string_8", zstr.as_encoded_8 (codec) ~ buffer.text)
							end
						else
							failed ("same count")
						end
					else
						failed ("Found matching codec")
					end
				end
			end
		end

	test_immutable_to_integer_or_natural
		-- STRING_CONVERSION_TEST_SET.test_immutable_to_integer_or_natural
		local
			value: IMMUTABLE_STRING_8; value_2: STRING_8; lower, upper: INTEGER
			number_list: EL_SPLIT_IMMUTABLE_STRING_8_LIST; csv_list: STRING
		do
			csv_list := "1a, 10, -10, 1.03"
			create number_list.make_shared_adjusted (csv_list, ',', {EL_SIDE}.Left)
			if attached number_list as list then
				from list.start until list.after loop
					value := list.item; value_2 := value
					lower := list.item_lower; upper := list.item_upper
					if value_2.is_natural then
						assert ("is natural", Convert_string.is_natural (value))
						assert ("same value", Convert_string.to_natural (value) = value_2.to_natural)
						if attached {NATURAL} Convert_string.substring_to_type (csv_list, lower, upper, {NATURAL}) as n then
							assert ("same value", n = value_2.to_natural)
						else
							failed ("convert substring to NATURAL")
						end
					else
						assert ("is not natural", not Convert_string.is_natural (value))
					end
					if value_2.is_integer then
						assert ("is integer", Convert_string.is_integer (value))
						assert ("same value", Convert_string.to_integer (value) = value_2.to_integer)
						if attached {INTEGER} Convert_string.substring_to_type (csv_list, lower, upper, {INTEGER}) as n then
							assert ("same value", n = value_2.to_integer)
						else
							failed ("convert substring to INTEGER")
						end
					else
						assert ("is not integer", not Convert_string.is_integer (value))
					end
					list.forth
				end
			end
		end

	test_number_formatting
		-- STRING_CONVERSION_TEST_SET.test_number_formatting
		note
			testing: "covers/{EL_FORMAT_ROUTINES}.internal_integer"
		local
			padding, formatted: STRING; width: INTEGER
			zero_padded: BOOLEAN; padding_character: CHARACTER
			pi: DOUBLE
		do
--			Integer formatting
			across << True, False >> as bool loop
				zero_padded := bool.item
				across 1 |..| 10 as n loop
					width := n.item
					if zero_padded then
						padding_character := '0'
						formatted := Format.zero_padded_integer (1, width)
					else
						padding_character := ' '
						formatted := Format.padded_integer (1, width)
					end
					create padding.make_filled (padding_character, width - 1)
					assert_same_string (Void, formatted, padding + "1")
				end
			end
--			Double formatting
			assert_same_string (Void, "3.142", Format.double ({DOUBLE_MATH}.Pi, 3, 3) )
		end

	test_python_escape
		-- STRING_CONVERSION_TEST_SET.test_python_escape
		local
			escaper: EL_STRING_ESCAPER [ZSTRING]
			str: STRING; zstr: STRING
		do
			create escaper.make (Escape_table.Python_1)
			str := "tab%Tquote'"
			zstr := str
			assert ("correct escape", escaper.escaped (str, False).same_string ("tab\tquote\'"))
			assert_same_string (Void, escaper.escaped (str, True), escaper.escaped (zstr, True))
		end

	test_substitution_marker_unescape
		note
			testing:	"covers/{ZSTRING}.unescape, covers/{ZSTRING}.unescaped",
			 	"covers/{ZSTRING}.make_from_zcode_area, covers/{EL_ZSTRING_UNESCAPER}.unescaped"
		local
			str: ZSTRING
		do
			str := "1 %%S 3"
			str.unescape (Substitution_mark_unescaper)
			assert_same_string ("has substitution marker", str, "1 %S 3")
		end

	test_unescape
		-- STRING_CONVERSION_TEST_SET.test_unescape
		note
			testing:	"[
				covers/{EL_ZSTRING_UNESCAPER}.unescape, covers/{EL_STRING_32_UNESCAPER}.unescape
			]"
		local
			str, unescaped: ZSTRING; str_32, unescaped_32: STRING_32
			unescaper: EL_ZSTRING_UNESCAPER; unescaper_32: EL_STRING_32_UNESCAPER
			escape_table_32: like new_escape_table
			escape_character: CHARACTER_32
		do
			across << ('\').to_character_32, 'л' >> as l_escape_character loop
				escape_character := l_escape_character.item
				create str_32.make (Text.Mixed_text.count)
				str_32.append_character (escape_character)
				str_32.append_character (escape_character)

				escape_table_32 := new_escape_table (escape_character)

				across Text.Mixed_text as character loop
					escape_table_32.search (character.item)
					if escape_table_32.found then
						str_32.append_character (escape_character)
					end
					str_32.append_character (character.item)
				end
				str_32 [str_32.index_of (' ', 1)] := escape_character
				str := str_32

				create unescaper.make (escape_table_32)
				create unescaper_32.make (escape_table_32)

				unescaped := unescaper.unescaped (str)
				unescaped_32 := unescaper_32.unescaped (str_32)
				assert_same_string ("unescape OK", unescaped, unescaped_32)
			end
		end

feature {NONE} -- Implementation

	escape_test (
		name: STRING; escaper: EL_STRING_ESCAPER [ZSTRING]; escaper_32: EL_STRING_ESCAPER [STRING_32]
	)
		local
			str_32, escaped_32: STRING_32; str, escaped: ZSTRING
			s: EL_STRING_32_ROUTINES
		do
			across << True, False >> as replace_space loop
				across Text.lines_32 as string loop
					str_32 := string.item.twin
					if replace_space.item then
						s.replace_character (str_32, ' ', '/')
					else
						s.replace_character (str_32, '+', '&')
					end
					str := str_32
					escaped := escaper.escaped (str, True)
					escaped_32 := escaper_32.escaped (str_32, True)
					assert_same_string (name + " escape OK", escaped, escaped_32)
				end
			end
		end

	new_escape_table (escape: CHARACTER_32): EL_ESCAPE_TABLE
		do
			create Result.make (escape, {STRING_32} "t:=%T, ь:=в, и:=N")
		end

feature {NONE} -- Constants

	Latin: STRING = "Latin"

	Substitution_mark_unescaper: EL_ZSTRING_UNESCAPER
		once
			create Result.make (Escape_table.Substitution)
		end

end