From 7b4a1a6c1f4665e73ef9fc53d70deffe3066ff61 Mon Sep 17 00:00:00 2001 From: Antranig Vartanian Date: Sun, 15 Jun 2025 03:48:15 +0400 Subject: [PATCH] init --- .gitattributes | 3 ++ Makefile | 26 +++++++++ README.md | 0 src/Logger.Mod | 124 +++++++++++++++++++++++++++++++++++++++++++ src/time.Mod | 45 ++++++++++++++++ tests/LoggerTest.Mod | 37 +++++++++++++ tests/expected.txt | 9 ++++ tests/test.awk | 52 ++++++++++++++++++ 8 files changed, 296 insertions(+) create mode 100644 .gitattributes create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/Logger.Mod create mode 100644 src/time.Mod create mode 100644 tests/LoggerTest.Mod create mode 100644 tests/expected.txt create mode 100644 tests/test.awk diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8e09bda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Set the language to Oberon +*.Mod linguist-language=Oberon +*.mod linguist-language=Oberon diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de2f1a4 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +.POSIX: + +SRC = ../src +TESTS = ../tests +BUILD = build + +OBJS = $(BUILD)/time.o $(BUILD)/Logger.o +LOGTEST = $(BUILD)/LoggerTest + +all: build + +build: + mkdir -p $(BUILD) + cd $(BUILD) && voc $(SRC)/time.Mod $(SRC)/Logger.Mod + +test: $(LOGTEST) + cd $(BUILD) \ + && ./LoggerTest > actual.txt \ + && awk -f $(TESTS)/test.awk actual.txt $(TESTS)/expected.txt + +$(LOGTEST): build + cd $(BUILD) \ + && voc $(SRC)/time.Mod $(SRC)/Logger.Mod $(TESTS)/LoggerTest.Mod -m + +clean: + rm -rf $(BUILD) diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/Logger.Mod b/src/Logger.Mod new file mode 100644 index 0000000..2a6daad --- /dev/null +++ b/src/Logger.Mod @@ -0,0 +1,124 @@ +MODULE Logger; + + IMPORT Out, time; + + CONST + ERROR* = 0; + WARN* = 1; + INFO* = 2; + DEBUG* = 3; + + TYPE + Logger* = POINTER TO LoggerDesc; + LoggerDesc = RECORD + level: INTEGER; + prefix: ARRAY 64 OF CHAR; + END; + + PROCEDURE New*(): Logger; + VAR self: Logger; + BEGIN + NEW(self); + self.level := INFO; + self.prefix[0] := 0X; + RETURN self + END New; + + PROCEDURE (self: Logger) SetLevel*(level: INTEGER); + BEGIN + self.level := level + END SetLevel; + + PROCEDURE (self: Logger) SetPrefix*(p: ARRAY OF CHAR); + VAR i: INTEGER; + BEGIN + i := 0; + WHILE (i < LEN(p)) & (p[i] # 0X) DO + self.prefix[i] := p[i]; INC(i) + END; + self.prefix[i] := 0X + END SetPrefix; + + PROCEDURE (self: Logger) ClearPrefix*; + BEGIN + self.prefix[0] := 0X + END ClearPrefix; + + PROCEDURE PrintTime; + VAR year, month, day, hour, min, sec: LONGINT; + BEGIN + time.Now(year, month, day, hour, min, sec); + + Out.Int(year, 0); Out.Char("-"); + IF month < 10 THEN Out.Char("0") END; Out.Int(month, 0); Out.Char("-"); + IF day < 10 THEN Out.Char("0") END; Out.Int(day, 0); Out.Char("T"); + IF hour < 10 THEN Out.Char("0") END; Out.Int(hour, 0); Out.Char(":"); + IF min < 10 THEN Out.Char("0") END; Out.Int(min, 0); Out.Char(":"); + IF sec < 10 THEN Out.Char("0") END; Out.Int(sec, 0); + Out.Char("Z") + END PrintTime; + + PROCEDURE PrintMsg(self: Logger; levelStr, msg: ARRAY OF CHAR); + BEGIN + Out.String("["); PrintTime(); Out.String("] "); + Out.String("["); Out.String(levelStr); Out.String("] "); + IF self.prefix[0] # 0X THEN + Out.String("["); Out.String(self.prefix); Out.String("] "); + END; + Out.String(msg); Out.Ln + END PrintMsg; + + PROCEDURE PrintMsgWithInt(self: Logger; levelStr, msg: ARRAY OF CHAR; n: INTEGER); + BEGIN + Out.String("["); PrintTime(); Out.String("] "); + Out.String("["); Out.String(levelStr); Out.String("] "); + IF self.prefix[0] # 0X THEN + Out.String("["); Out.String(self.prefix); Out.String("] "); + END; + Out.String(msg); Out.Int(n, 0); Out.Ln + END PrintMsgWithInt; + + (* ===== Logging Methods ===== *) + + + + PROCEDURE (self: Logger) Error*(msg: ARRAY OF CHAR); + BEGIN + IF self.level >= ERROR THEN PrintMsg(self, "ERROR", msg) END + END Error; + + PROCEDURE (self: Logger) ErrorInt*(msg: ARRAY OF CHAR; n: INTEGER); + BEGIN + IF self.level >= ERROR THEN PrintMsgWithInt(self, "ERROR", msg, n) END + END ErrorInt; + + PROCEDURE (self: Logger) Warn*(msg: ARRAY OF CHAR); + BEGIN + IF self.level >= WARN THEN PrintMsg(self, "WARN", msg) END + END Warn; + + PROCEDURE (self: Logger) WarnInt*(msg: ARRAY OF CHAR; n: INTEGER); + BEGIN + IF self.level >= WARN THEN PrintMsgWithInt(self, "WARN", msg, n) END + END WarnInt; + + PROCEDURE (self: Logger) Info*(msg: ARRAY OF CHAR); + BEGIN + IF self.level >= INFO THEN PrintMsg(self, "INFO", msg) END + END Info; + + PROCEDURE (self: Logger) InfoInt*(msg: ARRAY OF CHAR; n: INTEGER); + BEGIN + IF self.level >= INFO THEN PrintMsgWithInt(self, "INFO", msg, n) END + END InfoInt; + + PROCEDURE (self: Logger) Debug*(msg: ARRAY OF CHAR); + BEGIN + IF self.level >= DEBUG THEN PrintMsg(self, "DEBUG", msg) END + END Debug; + + PROCEDURE (self: Logger) DebugInt*(msg: ARRAY OF CHAR; n: INTEGER); + BEGIN + IF self.level >= DEBUG THEN PrintMsgWithInt(self, "DEBUG", msg, n) END + END DebugInt; +END Logger. diff --git a/src/time.Mod b/src/time.Mod new file mode 100644 index 0000000..f431eb9 --- /dev/null +++ b/src/time.Mod @@ -0,0 +1,45 @@ +MODULE time; +IMPORT SYSTEM; +TYPE + unxTime* = HUGEINT; + +PROCEDURE -Aincludesystime '#include '; (* for gettimeofday *) +PROCEDURE -Aincludetime '#include '; (* for localtime *) +PROCEDURE -Aincludesystypes '#include '; + +PROCEDURE -gettimeval "struct timeval tv; gettimeofday(&tv,0)"; +PROCEDURE -tvsec(): LONGINT "tv.tv_sec"; +PROCEDURE -tvusec(): LONGINT "tv.tv_usec"; +PROCEDURE -sectotm(s: LONGINT) "struct tm *time = localtime((time_t*)&s)"; +PROCEDURE -tmsec(): LONGINT "(LONGINT)time->tm_sec"; +PROCEDURE -tmmin(): LONGINT "(LONGINT)time->tm_min"; +PROCEDURE -tmhour(): LONGINT "(LONGINT)time->tm_hour"; +PROCEDURE -tmmday(): LONGINT "(LONGINT)time->tm_mday"; +PROCEDURE -tmmon(): LONGINT "(LONGINT)time->tm_mon"; +PROCEDURE -tmyear(): LONGINT "(LONGINT)time->tm_year"; + +PROCEDURE -unixtime(VAR tmtype: unxTime) "time(tmtype)"; +PROCEDURE -unixtimediff(endtime, starttime: unxTime): LONGREAL "(LONGREAL)difftime(endtime, starttime)"; + +PROCEDURE Now*(VAR year, month, day, hour, min, sec: LONGINT); +BEGIN + gettimeval; sectotm(tvsec()); + year := tmyear() + 1900; + month := tmmon()+1; + day := tmmday(); + hour := tmhour(); + min := tmmin(); + sec := tmsec(); +END Now; + +PROCEDURE unixTime*(VAR t: unxTime); +BEGIN + unixtime(t); +END unixTime; + +PROCEDURE unixTimeDiff*(endTime, startTime: unxTime): LONGREAL; +BEGIN + RETURN unixtimediff(endTime, startTime) +END unixTimeDiff; + +END time. diff --git a/tests/LoggerTest.Mod b/tests/LoggerTest.Mod new file mode 100644 index 0000000..04e8c7f --- /dev/null +++ b/tests/LoggerTest.Mod @@ -0,0 +1,37 @@ +MODULE LoggerTest; + + IMPORT Logger; + +VAR + log: Logger.Logger; + +BEGIN + (* Initialize logger instance *) + log := Logger.New(); + + (* Show default level (INFO) in action *) + log.Info("Logger initialized"); + log.InfoInt("Connected users: ", 42); + + (* Prefix usage *) + log.SetPrefix("unit-test"); + log.Warn("Warning with prefix"); + log.WarnInt("Sessions: ", 5); + + log.Error("Something went wrong"); + log.ErrorInt("Error code: ", -7); + + (* Switch to DEBUG level *) + log.SetLevel(Logger.DEBUG); + log.Debug("This is a debug message"); + log.DebugInt("File descriptor: ", 3); + + (* Clear prefix *) + log.ClearPrefix(); + log.Info("Prefix cleared"); + + (* Suppress all output *) + log.SetLevel(-1); + log.Error("This should NOT print"); + log.Debug("This should NOT print"); +END LoggerTest. diff --git a/tests/expected.txt b/tests/expected.txt new file mode 100644 index 0000000..83d5d64 --- /dev/null +++ b/tests/expected.txt @@ -0,0 +1,9 @@ +[2025-06-15T03:27:27Z] [INFO] Logger initialized +[2025-06-15T03:27:27Z] [INFO] Connected users: 42 +[2025-06-15T03:27:27Z] [WARN] [unit-test] Warning with prefix +[2025-06-15T03:27:27Z] [WARN] [unit-test] Sessions: 5 +[2025-06-15T03:27:27Z] [ERROR] [unit-test] Something went wrong +[2025-06-15T03:27:27Z] [ERROR] [unit-test] Error code: -7 +[2025-06-15T03:27:27Z] [DEBUG] [unit-test] This is a debug message +[2025-06-15T03:27:27Z] [DEBUG] [unit-test] File descriptor: 3 +[2025-06-15T03:27:27Z] [INFO] Prefix cleared diff --git a/tests/test.awk b/tests/test.awk new file mode 100644 index 0000000..8969d3d --- /dev/null +++ b/tests/test.awk @@ -0,0 +1,52 @@ +#!/usr/bin/awk -f + +# test.awk: Compare actual.txt with expected.txt, allowing [TIME] as a timestamp placeholder + +BEGIN { + passed = 1 + line = 0 + + # Load expected lines from second file (expected.txt) + while ((getline expectedLine < ARGV[2]) > 0) { + line++ + expected[line] = expectedLine + } + + delete ARGV[2] # remove expected.txt so AWK processes only actual.txt +} + +{ + line++ + actual = $0 + expectedLine = expected[line] + + # Match timestamp in the form [YYYY-MM-DDTHH:MM:SS] + if (match(actual, /\[([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})Z\] (.*)/)) { + ts_len = RLENGTH + msg = substr(actual, ts_len + 1) + } else { + printf("FAIL: Line %d: Invalid or missing timestamp: %s\n", line, actual) + passed = 0 + next + } + + # If expected line begins with [TIME], strip it + if (index(expectedLine, "[TIME] ") == 1) { + expectedLine = substr(expectedLine, 8) + } + + if (msg != expectedLine) { + printf("FAIL: Line %d:\n expected: %s\n actual: %s\n", line, expectedLine, msg) + passed = 0 + } +} + +END { + if (passed) { + print "PASS: all lines matched with valid timestamps" + exit 0 + } else { + print "FAIL: differences found" + exit 1 + } +}