summaryrefslogtreecommitdiff
path: root/tests/test-calendar-sync--normalize-line-endings.el
blob: 7f0830cce6e74841bba0842a8476bd6fdc476894 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
;;; test-calendar-sync--normalize-line-endings.el --- Tests for calendar-sync--normalize-line-endings  -*- lexical-binding: t; -*-

;;; Commentary:
;; Unit tests for calendar-sync--normalize-line-endings function.
;; Tests conversion of various line ending formats to Unix LF-only format.
;; Covers Normal, Boundary, and Error cases.
;;
;; The iCalendar format (RFC 5545) uses CRLF line endings (\r\n),
;; but Emacs and org-mode expect LF only (\n). This function ensures
;; consistent line endings throughout the parsing pipeline.

;;; Code:

(require 'ert)
(require 'calendar-sync)

;;; Normal Cases

(ert-deftest test-calendar-sync--normalize-line-endings-normal-crlf-to-lf ()
  "Test that CRLF line endings are converted to LF only.

Input: String with \\r\\n (Windows/DOS line endings)
Expected: String with \\n only (Unix line endings)"
  (let* ((input "line1\r\nline2\r\nline3\r\n")
         (expected "line1\nline2\nline3\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-normal-lf-unchanged ()
  "Test that LF-only content is returned unchanged.

Input: String with \\n only (already Unix format)
Expected: Same string (no modification needed)"
  (let* ((input "line1\nline2\nline3\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= input result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-normal-mixed-endings ()
  "Test that mixed line endings are normalized to LF only.

Input: String with both \\r\\n (CRLF) and \\n (LF)
Expected: String with \\n only everywhere"
  (let* ((input "line1\r\nline2\nline3\r\nline4\n")
         (expected "line1\nline2\nline3\nline4\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-normal-ics-vevent-block ()
  "Test normalization of realistic iCalendar VEVENT block with CRLF.

Input: VEVENT block with CRLF line endings (as per RFC 5545)
Expected: Same structure with LF only"
  (let* ((input "BEGIN:VEVENT\r\nSUMMARY:Test Event\r\nDTSTART:20251116T140000Z\r\nEND:VEVENT\r\n")
         (expected "BEGIN:VEVENT\nSUMMARY:Test Event\nDTSTART:20251116T140000Z\nEND:VEVENT\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

;;; Boundary Cases

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-empty-string ()
  "Test that empty string is handled correctly.

Input: Empty string
Expected: Empty string (no crash)"
  (let ((result (calendar-sync--normalize-line-endings "")))
    (should (string= "" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-no-line-endings ()
  "Test that string with no line endings is unchanged.

Input: Plain text with no \\r or \\n
Expected: Same string unchanged"
  (let* ((input "no line endings here")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= input result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-only-cr ()
  "Test that bare CR characters (old Mac format) are removed.

Input: String with \\r only (classic Mac OS line endings)
Expected: String with \\r removed (results in run-together text)"
  (let* ((input "line1\rline2\rline3\r")
         (expected "line1line2line3")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-cr-in-middle ()
  "Test that CR characters in middle of content are removed.

Input: String with \\r not followed by \\n (unusual but possible)
Expected: All \\r removed regardless of position"
  (let* ((input "line1\r\ntext\rwith\rmiddle\r\nline2")
         (expected "line1\ntextwithmiddle\nline2")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-multiple-cr ()
  "Test that multiple consecutive CR characters are all removed.

Input: String with \\r\\r or \\r\\r\\n sequences
Expected: All \\r characters removed"
  (let* ((input "line1\r\r\nline2\r\r\r\nline3")
         (expected "line1\nline2\nline3")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-single-line ()
  "Test normalization of single line with trailing CRLF.

Input: Single line of text ending with \\r\\n
Expected: Single line ending with \\n"
  (let* ((input "single line\r\n")
         (expected "single line\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-only-line-endings ()
  "Test string containing only line ending characters.

Input: String of only \\r\\n sequences
Expected: String of only \\n (CR stripped)"
  (let* ((input "\r\n\r\n\r\n")
         (expected "\n\n\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-unicode-content ()
  "Test normalization preserves Unicode characters.

Input: String with Unicode and CRLF line endings
Expected: Unicode preserved, only CR removed"
  (let* ((input "emoji 🎉\r\nchinese 中文\r\narabic العربية\r\n")
         (expected "emoji 🎉\nchinese 中文\narabic العربية\n")
         (result (calendar-sync--normalize-line-endings input)))
    (should (string= expected result))
    (should-not (string-match-p "\r" result))))

(ert-deftest test-calendar-sync--normalize-line-endings-boundary-very-long-string ()
  "Test normalization of large string with many line endings.

Input: String with 1000 lines with CRLF
Expected: Same content with LF only, performance acceptable"
  (let* ((line "This is line content with some text\r\n")
         (input (apply #'concat (make-list 1000 line)))
         (result (calendar-sync--normalize-line-endings input)))
    (should (= (length input) (+ (length result) 1000))) ; 1000 \r removed
    (should-not (string-match-p "\r" result))
    (should (string-match-p "^This is line content" result))))

;;; Error Cases

(ert-deftest test-calendar-sync--normalize-line-endings-error-nil-input ()
  "Test that nil input is handled gracefully.

Input: nil
Expected: nil (defensive programming, no crash)"
  (let ((result (calendar-sync--normalize-line-endings nil)))
    (should (null result))))

(ert-deftest test-calendar-sync--normalize-line-endings-error-non-string-input ()
  "Test that non-string input is returned unchanged.

Input: Integer (wrong type)
Expected: Same value returned (defensive, don't crash)"
  (let ((result (calendar-sync--normalize-line-endings 42)))
    (should (= 42 result))))

(ert-deftest test-calendar-sync--normalize-line-endings-error-symbol-input ()
  "Test that symbol input is handled gracefully.

Input: Symbol (wrong type)
Expected: Symbol returned unchanged"
  (let ((result (calendar-sync--normalize-line-endings 'some-symbol)))
    (should (eq 'some-symbol result))))

(provide 'test-calendar-sync--normalize-line-endings)
;;; test-calendar-sync--normalize-line-endings.el ends here