FileLogHandlerTest.kt 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Chris Narkiewicz
  5. * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.nextcloud.client.logger
  21. import org.junit.Assert.assertEquals
  22. import org.junit.Assert.assertFalse
  23. import org.junit.Assert.assertTrue
  24. import org.junit.Before
  25. import org.junit.Test
  26. import java.io.File
  27. import java.nio.charset.Charset
  28. import java.nio.file.Files
  29. @Suppress("TooManyFunctions")
  30. class FileLogHandlerTest {
  31. private companion object {
  32. const val FILE_SIZE = 1024L
  33. const val MAX_FILE_SIZE = 20L
  34. const val THREE_LOG_FILES = 3
  35. const val EXPECTED_LINE_COUNT_6 = 6
  36. const val EXPECTED_LINE_COUNT_12 = 12
  37. }
  38. private lateinit var logDir: File
  39. private fun readLogFile(name: String): String {
  40. val logFile = File(logDir, name)
  41. val raw = Files.readAllBytes(logFile.toPath())
  42. return String(raw, Charset.forName("UTF-8"))
  43. }
  44. /**
  45. * Write raw content to file in log dir.
  46. *
  47. * @return size of written data in bytes
  48. */
  49. private fun writeLogFile(name: String, content: String): Int {
  50. val logFile = File(logDir, name)
  51. val rawContent = content.toByteArray(Charsets.UTF_8)
  52. Files.write(logFile.toPath(), rawContent)
  53. return rawContent.size
  54. }
  55. @Before
  56. fun setUp() {
  57. logDir = Files.createTempDirectory("logger-test-").toFile()
  58. }
  59. @Test
  60. fun `logs dir is created on open`() {
  61. // GIVEN
  62. // logs directory does not exist
  63. val nonexistingLogsDir = File(logDir, "subdir")
  64. assertFalse(nonexistingLogsDir.exists())
  65. // WHEN
  66. // file is opened
  67. val handler = FileLogHandler(nonexistingLogsDir, "log.txt", FILE_SIZE)
  68. handler.open()
  69. // THEN
  70. // directory is created
  71. assertTrue(nonexistingLogsDir.exists())
  72. }
  73. @Test
  74. fun `log test helpers`() {
  75. val filename = "test.txt"
  76. val expected = "Hello, world!"
  77. writeLogFile(filename, expected)
  78. val readBack = readLogFile(filename)
  79. assertEquals(expected, readBack)
  80. }
  81. @Test
  82. fun `rotate files`() {
  83. // GIVEN
  84. // log contains files
  85. writeLogFile("log.txt", "0")
  86. writeLogFile("log.txt.0", "1")
  87. writeLogFile("log.txt.1", "2")
  88. writeLogFile("log.txt.2", "3")
  89. val writer = FileLogHandler(logDir, "log.txt", FILE_SIZE)
  90. // WHEN
  91. // files are rotated
  92. writer.rotateLogs()
  93. // THEN
  94. // last file is removed
  95. // all remaining files are advanced by 1 step
  96. assertFalse(File(logDir, "log.txt").exists())
  97. assertEquals("0", readLogFile("log.txt.0"))
  98. assertEquals("1", readLogFile("log.txt.1"))
  99. assertEquals("2", readLogFile("log.txt.2"))
  100. }
  101. @Test
  102. fun `log file is rotated when crossed max size`() {
  103. // GIVEN
  104. // log file contains 10 bytes
  105. // log file limit is 20 bytes
  106. // log writer is opened
  107. writeLogFile("log.txt", "0123456789")
  108. val writer = FileLogHandler(logDir, "log.txt", MAX_FILE_SIZE)
  109. writer.open()
  110. // WHEN
  111. // writing 2nd log entry of 11 bytes
  112. writer.write("0123456789!") // 11 bytes
  113. // THEN
  114. // log file is closed and rotated
  115. val rotatedContent = readLogFile("log.txt.0")
  116. assertEquals("01234567890123456789!", rotatedContent)
  117. }
  118. @Test
  119. fun `log file is reopened after rotation`() {
  120. // GIVEN
  121. // log file contains 10 bytes
  122. // log file limit is 20 bytes
  123. // log writer is opened
  124. writeLogFile("log.txt", "0123456789")
  125. val writer = FileLogHandler(logDir, "log.txt", MAX_FILE_SIZE)
  126. writer.open()
  127. // WHEN
  128. // writing 2nd log entry of 11 bytes
  129. // writing another log entry
  130. // closing log
  131. writer.write("0123456789!") // 11 bytes
  132. writer.write("Hello!")
  133. writer.close()
  134. // THEN
  135. // current log contains last entry
  136. val lastEntry = readLogFile("log.txt")
  137. assertEquals("Hello!", lastEntry)
  138. }
  139. @Test
  140. fun `load log lines from files`() {
  141. // GIVEN
  142. // multiple log files exist
  143. // log files have lines
  144. var totalLogsSize = 0L
  145. totalLogsSize += writeLogFile("log.txt.2", "line1\nline2\nline3")
  146. totalLogsSize += writeLogFile("log.txt.1", "line4\nline5\nline6")
  147. totalLogsSize += writeLogFile("log.txt.0", "line7\nline8\nline9")
  148. totalLogsSize += writeLogFile("log.txt", "line10\nline11\nline12")
  149. // WHEN
  150. // log file is read including rotated content
  151. val writer = FileLogHandler(logDir, "log.txt", FILE_SIZE)
  152. val rawLogs = writer.loadLogFiles(THREE_LOG_FILES)
  153. // THEN
  154. // all files are loaded
  155. // lines are loaded in correct order
  156. // log files size is correctly reported
  157. assertEquals(EXPECTED_LINE_COUNT_12, rawLogs.lines.size)
  158. assertEquals(
  159. listOf(
  160. "line1", "line2", "line3",
  161. "line4", "line5", "line6",
  162. "line7", "line8", "line9",
  163. "line10", "line11", "line12"
  164. ),
  165. rawLogs.lines
  166. )
  167. assertEquals(totalLogsSize, rawLogs.logSize)
  168. }
  169. @Test
  170. fun `load log lines from files with gaps between rotated files`() {
  171. // GIVEN
  172. // multiple log files exist
  173. // log files have lines
  174. // some rotated files are deleted
  175. writeLogFile("log.txt", "line1\nline2\nline3")
  176. writeLogFile("log.txt.2", "line4\nline5\nline6")
  177. // WHEN
  178. // log file is read including rotated content
  179. val writer = FileLogHandler(logDir, "log.txt", FILE_SIZE)
  180. val lines = writer.loadLogFiles(THREE_LOG_FILES)
  181. // THEN
  182. // all files are loaded
  183. // log file size is non-zero
  184. assertEquals(EXPECTED_LINE_COUNT_6, lines.lines.size)
  185. assertTrue(lines.logSize > 0)
  186. }
  187. @Test(expected = IllegalArgumentException::class)
  188. fun `load log lines - negative count is illegal`() {
  189. // WHEN
  190. // requesting negative number of rotated files
  191. val writer = FileLogHandler(logDir, "log.txt", FILE_SIZE)
  192. val lines = writer.loadLogFiles(-1)
  193. // THEN
  194. // illegal argument exception
  195. }
  196. @Test
  197. fun `all log files are deleted`() {
  198. // GIVEN
  199. // log files exist
  200. val handler = FileLogHandler(logDir, "log.txt", MAX_FILE_SIZE)
  201. for (i in 0 until handler.maxLogFilesCount) {
  202. handler.rotateLogs()
  203. handler.open()
  204. handler.write("new log entry")
  205. handler.close()
  206. }
  207. assertEquals(handler.maxLogFilesCount, logDir.listFiles().size)
  208. // WHEN
  209. // files are deleted
  210. handler.deleteAll()
  211. // THEN
  212. // all files are deleted
  213. assertEquals(0, logDir.listFiles().size)
  214. }
  215. }