diff options
| author | EuAndreh <eu@euandre.org> | 2026-04-21 22:16:06 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2026-04-21 22:16:06 -0300 |
| commit | f478620978ae884aa5e7a0ddb742faa5cb23ae3a (patch) | |
| tree | 0e2c87b1e3d5d99aaccd99d24d47ca45d6d2c1fe | |
| parent | Remove Go code (diff) | |
| download | papod-f478620978ae884aa5e7a0ddb742faa5cb23ae3a.tar.gz papod-f478620978ae884aa5e7a0ddb742faa5cb23ae3a.tar.xz | |
m
| -rw-r--r-- | .gitignore | 24 | ||||
| -rw-r--r-- | Makefile | 215 | ||||
| -rw-r--r-- | deps.mk | 77 | ||||
| -rwxr-xr-x | mkdeps.sh | 49 | ||||
| -rw-r--r-- | src/labareda.edn | 0 | ||||
| -rwxr-xr-x | src/papod.bin.in | 5 | ||||
| -rw-r--r-- | src/papod.clj | 266 | ||||
| -rwxr-xr-x | tests/cli-opts.sh | 4 | ||||
| -rw-r--r-- | tests/fuzz/api.clj | 11 | ||||
| -rw-r--r-- | tests/integration.clj | 6 | ||||
| -rw-r--r-- | tests/lib.sh | 122 | ||||
| -rw-r--r-- | tests/unit.clj | 54 |
12 files changed, 608 insertions, 225 deletions
@@ -1,19 +1,13 @@ /doc/* !/doc/*.en.*.adoc +/doc/*.en.3.adoc /po/*/*.mo -/src/meta.go -/*.bin -/*.db* -/src/*.a +/src/*/ +/src/*.class +/tests/*.class +/tests/fuzz/*.class +/*.jar /src/*.bin -/src/*cgo* -/tests/*.a -/tests/*.bin -/tests/functional/*/*.a -/tests/functional/*/*.bin -/tests/fuzz/*/*.a -/tests/fuzz/*/*.bin -/tests/benchmarks/*/*.a -/tests/benchmarks/*/*.bin -/tests/benchmarks/*/*.txt -/tests/fuzz/corpus/ +/src/cli.edn +/schema.edn +/datomic.log @@ -7,32 +7,43 @@ NAME_UC = $(NAME) PREFIX = /usr BINDIR = $(PREFIX)/bin LIBDIR = $(PREFIX)/lib -GOLIBDIR = $(LIBDIR)/go INCLUDEDIR = $(PREFIX)/include SRCDIR = $(PREFIX)/src/$(NAME) SHAREDIR = $(PREFIX)/share LOCALEDIR = $(SHAREDIR)/locale MANDIR = $(SHAREDIR)/man +JAVADIR = $(SHAREDIR)/java EXEC = ./ ## Where to store the installation. Empty by default. DESTDIR = -LDLIBS = --static -lscrypt-kdf -GOCFLAGS = -I $(GOLIBDIR) -GOLDFLAGS = -L $(GOLIBDIR) -N = `nproc` +LDLIBS = +JAVA = java -client +CLASSPATH = `printf '$(JARPATH)/%s.jar\n' $(JARDEPS) | paste -sd:` +JARDEPS = \ + cracha \ + fiinha \ + jasm \ + labareda \ + peer \ + dtmc \ + base \ + clojure .SUFFIXES: -.SUFFIXES: .go .a .bin .bin-check .adoc .po .mo +.SUFFIXES: .in .clj .class .class-check .jar .jar-check .adoc .po .mo +.SUFFIXES: .sh .sh-check -.go.a: - go tool compile -I $(@D) $(GOCFLAGS) -o $@ -p $(*F) \ - `find $< $$(if [ $(*F) != main ]; then \ - echo src/$(NAME).go src/meta.go; fi) | uniq` - -.a.bin: - go tool link -L $(@D) $(GOLDFLAGS) -o $@ --extldflags '$(LDLIBS)' $< +.in: + sed \ + -e 's:@VERSION@:$(VERSION):g' \ + -e 's:@DATE@:$(DATE):g' \ + -e 's:@NAME@:$(NAME):g' \ + -e 's:@JAVADIR@:$(JAVADIR):g' \ + -e "s_@CLASSPATH@_$(CLASSPATH)_g" \ + < $< > $@ + if [ -x $< ]; then chmod +x $@; fi .adoc: asciidoctor -b manpage -o $@ $< @@ -46,39 +57,46 @@ all: include deps.mk -libs.a = $(libs.go:.go=.a) -mains.a = $(mains.go:.go=.a) -mains.bin = $(mains.go:.go=.bin) -functional/lib.a = $(functional/lib.go:.go=.a) -fuzz/lib.a = $(fuzz/lib.go:.go=.a) -benchmarks/lib.a = $(benchmarks/lib.go:.go=.a) +integration.sh-check = $(integration.sh:.sh=.sh-check) +fuzz.class-check = $(fuzz.clj:.clj=.class-check) + +inits = \ + src/$(NAME)__init.class \ + tests/unit__init.class \ + tests/integration__init.class \ + $(fuzzinit.class) \ + manpages.N.adoc = $(manpages.en.N.adoc) $(manpages.XX.N.adoc) -manpages.N = $(manpages.N.adoc:.adoc=) +manpages.N = $(manpages.N.adoc:.adoc=) doc/$(NAME).en.3 sources.mo = $(sources.po:.po=.mo) sources = \ - src/$(NAME).go \ - src/meta.go \ - src/main.go \ + src/$(NAME).bin.in \ + src/$(NAME).clj \ + src/labareda.edn \ derived-assets = \ - src/meta.go \ - $(libs.a) \ - $(mains.a) \ - $(mains.bin) \ - $(NAME).bin \ - $(manpages.XX.N.adoc) \ - $(manpages.N) \ - $(sources.mo) \ + $(inits) \ + $(NAME).jar \ + unit.jar \ + integration.jar \ + src/$(NAME).bin \ + schema.edn \ + src/cli.edn \ + $(manpages.XX.N.adoc) \ + $(manpages.N) \ + $(sources.mo) \ + doc/$(NAME).en.3.adoc \ side-assets = \ - *$(NAME).db* \ - tests/functional/*/*.go.db* \ - tests/fuzz/corpus/ \ - tests/benchmarks/*/main.txt \ - $(NAME).daemon.sock \ - $(NAME).commander.sock \ + src/$(NAME)/ \ + src/*.class \ + tests/*.class \ + tests/fuzz/*.class \ + datomic.log \ + $(NAME).daemon.socket \ + $(NAME).commander.socket \ @@ -87,47 +105,68 @@ side-assets = \ all: $(derived-assets) -$(libs.a): Makefile deps.mk -$(libs.a): src/$(NAME).go src/meta.go +$(inits): Makefile deps.mk + +$(integration.sh): $(NAME).jar +$(fuzzinit.class): $(NAME).jar + +src/$(NAME)__init.class: src/$(NAME).clj +tests/unit__init.class: tests/unit.clj $(NAME).jar +tests/integration__init.class: tests/integration.clj $(NAME).jar +$(inits): + rm -f $@ + JAR=`if [ $(@D) != 'src' ]; then echo $(NAME).jar:; fi`; \ + DIR=`echo $@ | cut -d/ -f1`; \ + NS=`echo $@ | cut -d/ -f2- | sed 's/__.*$$//' | tr / .`; \ + $(JAVA) -ea --class-path $$DIR:$$JAR$(CLASSPATH) clojure.main -e \ + "(binding [*compile-path* \"$$DIR\"] \ + (compile '$$NS))" -$(fuzz/lib.a): - go tool compile $(GOCFLAGS) -o $@ -p $(NAME) -d=libfuzzer \ - $*.go src/$(NAME).go src/meta.go +src/cli.edn: src/labareda.edn + labareda compile < src/labareda.edn > $@ -src/meta.go: Makefile - echo 'package $(NAME)' > $@ - echo 'const Version = "$(VERSION)"' >> $@ - echo 'const Name = "$(NAME)"' >> $@ - echo 'const LOCALEDIR = "$(LOCALEDIR)"' >> $@ +$(NAME).jar: src/$(NAME)__init.class src/cli.edn + cd src && jar -cvf ../$@ `find . -name '*.class'` cli.edn -$(NAME).bin: src/main.bin - ln -fs src/main.bin $@ +unit.jar: tests/unit__init.class +integration.jar: tests/integration__init.class +unit.jar integration.jar: + cd tests && jar -cvf ../$@ $**.class + +schema.edn: $(NAME).jar + $(JAVA) -ea --class-path $(NAME).jar:$(CLASSPATH) clojure.main -e \ + "(do (require '$(NAME) 'clojure.pprint) \ + (clojure.pprint/pprint @#'$(NAME)/schema))" > $@ $(manpages.XX.N.adoc): po/doc/po4a.cfg po4a --no-update --translate-only $@ po/doc/po4a.cfg +doc/$(NAME).en.3.adoc: $(NAME).jar + env CLASSPATH="$(CLASSPATH)" base $(NAME).jar > $@ + + + +src/schema.edn-check: schema.edn + diff -U10 src/schema.edn schema.edn -tests.bin-check = \ - tests/main.bin-check \ - $(functional/main.go:.go=.bin-check) \ +unit.jar-check: unit.jar +integration.jar-check: integration.jar +unit.jar-check integration.jar-check: + $(JAVA) -ea --class-path $*.jar:$(NAME).jar:$(CLASSPATH) $* -$(tests.bin-check): - $(EXEC)$*.bin -check-unit: $(tests.bin-check) +check-unit: unit.jar-check +check-unit: src/schema.edn-check -integration-tests = \ - tests/cli-opts.sh \ - tests/integration.sh \ +$(integration.sh-check): + env CLASSPATH="$(CLASSPATH)" \ + sh $*.sh -.PRECIOUS: $(integration-tests) -$(integration-tests): $(NAME).bin -$(integration-tests): ALWAYS - sh $@ -check-integration: $(integration-tests) +check-integration: integration.jar-check +check-integration: $(integration.sh-check) check-integration: fuzz @@ -139,33 +178,11 @@ check: check-unit check-integration FUZZSEC=1 -fuzz/main.bin-check = $(fuzz/main.go:.go=.bin-check) -$(fuzz/main.bin-check): - $(EXEC)$*.bin --test.fuzztime=$(FUZZSEC)s --test.parallel=$N \ - --test.fuzz='.*' --test.fuzzcachedir=tests/fuzz/corpus - -fuzz: $(fuzz/main.bin-check) - - - -benchmarks/main.bin-check = $(benchmarks/main.go:.go=.bin-check) -$(benchmarks/main.bin-check): - printf '%s\n' '$(EXEC)$*.bin' > $*.txt - LANG=POSIX.UTF-8 time -p $(EXEC)$*.bin 2>> $*.txt - printf '%s\n' '$*.txt' - -bench: $(benchmarks/main.bin-check) - - - -i18n-doc: - po4a po/doc/po4a.cfg - -i18n-code: - gotext src/$(NAME).go > po/$(NAME)/$(NAME).pot - po4a po/$(NAME)/po4a.cfg +$(fuzz.class-check): + $(JAVA) -ea --class-path tests:$(NAME).jar:$(CLASSPATH) \ + `echo $* | cut -d/ -f2- | tr / .` $(FUZZSEC) -i18n: i18n-doc i18n-code +fuzz: $(fuzz.class-check) @@ -179,12 +196,12 @@ clean: ## ensures that all installable artifacts are crafted beforehand. install: all mkdir -p \ - '$(DESTDIR)$(BINDIR)' \ - '$(DESTDIR)$(GOLIBDIR)' \ - '$(DESTDIR)$(SRCDIR)' \ + '$(DESTDIR)$(BINDIR)' \ + '$(DESTDIR)$(JAVADIR)' \ + '$(DESTDIR)$(SRCDIR)' \ - cp $(NAME).bin '$(DESTDIR)$(BINDIR)'/$(NAME) - cp src/$(NAME).a '$(DESTDIR)$(GOLIBDIR)' + cp src/$(NAME).bin '$(DESTDIR)$(BINDIR)'/$(NAME) + cp $(NAME).jar '$(DESTDIR)$(JAVADIR)' cp $(sources) '$(DESTDIR)$(SRCDIR)' instool '$(DESTDIR)$(MANDIR)' install man $(manpages.N) instool '$(DESTDIR)$(LOCALEDIR)' install mo $(sources.mo) @@ -194,17 +211,13 @@ install: all ## A dedicated test asserts that this is always true. uninstall: rm -rf \ - '$(DESTDIR)$(BINDIR)'/$(NAME) \ - '$(DESTDIR)$(GOLIBDIR)'/$(NAME).a \ - '$(DESTDIR)$(SRCDIR)' \ + '$(DESTDIR)$(BINDIR)'/$(NAME) \ + '$(DESTDIR)$(JAVADIR)'/$(NAME).jar \ + '$(DESTDIR)$(SRCDIR)' \ instool '$(DESTDIR)$(MANDIR)' uninstall man $(manpages.N) instool '$(DESTDIR)$(LOCALEDIR)' uninstall mo $(sources.mo) -## Run it locally. -run: all - rm -f $(NAME).daemon.sock $(NAME).commander.sock - ./$(NAME).bin ALWAYS: @@ -1,74 +1,15 @@ -libs.go = \ - src/papod.go \ - tests/benchmarks/join-leave/papod.go \ - tests/functional/multiple-channels-and-users/papod.go \ - tests/fuzz/api-check/papod.go \ - tests/papod.go \ - -mains.go = \ - src/main.go \ - tests/benchmarks/join-leave/main.go \ - tests/functional/multiple-channels-and-users/main.go \ - tests/fuzz/api-check/main.go \ - tests/main.go \ - manpages.en.N.adoc = \ doc/papod.en.0.adoc \ -manpages.XX.N.adoc = \ - doc/papod.de.0.adoc \ - doc/papod.eo.0.adoc \ - doc/papod.es.0.adoc \ - doc/papod.fr.0.adoc \ - doc/papod.pt.0.adoc \ - -sources.po = \ - po/papod/de.po \ - po/papod/eo.po \ - po/papod/es.po \ - po/papod/fr.po \ - po/papod/pt.po \ - -functional/lib.go = \ - tests/functional/multiple-channels-and-users/papod.go \ - -functional/main.go = \ - tests/functional/multiple-channels-and-users/main.go \ - -fuzz/lib.go = \ - tests/fuzz/api-check/papod.go \ - -fuzz/main.go = \ - tests/fuzz/api-check/main.go \ +integration.sh = \ + tests/integration.sh \ -benchmarks/lib.go = \ - tests/benchmarks/join-leave/papod.go \ +fuzz.clj = \ + tests/fuzz/api.clj \ -benchmarks/main.go = \ - tests/benchmarks/join-leave/main.go \ +fuzzinit.class = \ + tests/fuzz/api__init.class \ -src/main.a: src/main.go -src/papod.a: src/papod.go -tests/benchmarks/join-leave/main.a: tests/benchmarks/join-leave/main.go -tests/benchmarks/join-leave/papod.a: tests/benchmarks/join-leave/papod.go -tests/functional/multiple-channels-and-users/main.a: tests/functional/multiple-channels-and-users/main.go -tests/functional/multiple-channels-and-users/papod.a: tests/functional/multiple-channels-and-users/papod.go -tests/fuzz/api-check/main.a: tests/fuzz/api-check/main.go -tests/fuzz/api-check/papod.a: tests/fuzz/api-check/papod.go -tests/main.a: tests/main.go -tests/papod.a: tests/papod.go -src/main.bin: src/main.a -tests/benchmarks/join-leave/main.bin: tests/benchmarks/join-leave/main.a -tests/functional/multiple-channels-and-users/main.bin: tests/functional/multiple-channels-and-users/main.a -tests/fuzz/api-check/main.bin: tests/fuzz/api-check/main.a -tests/main.bin: tests/main.a -src/main.bin-check: src/main.bin -tests/benchmarks/join-leave/main.bin-check: tests/benchmarks/join-leave/main.bin -tests/functional/multiple-channels-and-users/main.bin-check: tests/functional/multiple-channels-and-users/main.bin -tests/fuzz/api-check/main.bin-check: tests/fuzz/api-check/main.bin -tests/main.bin-check: tests/main.bin -src/main.a: src/$(NAME).a -tests/benchmarks/join-leave/main.a: tests/benchmarks/join-leave/$(NAME).a -tests/functional/multiple-channels-and-users/main.a: tests/functional/multiple-channels-and-users/$(NAME).a -tests/fuzz/api-check/main.a: tests/fuzz/api-check/$(NAME).a -tests/main.a: tests/$(NAME).a +tests/integration.sh-check: tests/integration.sh +tests/fuzz/api__init.class: tests/fuzz/api.clj +tests/fuzz/api.class-check: tests/fuzz/api__init.class @@ -1,49 +1,24 @@ #!/bin/sh -set -eu +set -euo pipefail export LANG=POSIX.UTF-8 -libs() { - find src tests -name '*.go' | - grep -Ev '/(main|meta)\.go$' | - grep -Ev '/_cgo_(import|gotypes)\.go$' | - grep -Ev '\.cgo1\.go$' +integrations() { + find tests/ -name '*.sh' -type f -perm -111 } -mains() { - find src tests -name '*.go' | grep '/main\.go$' -} - -docs() { - find doc/*.en.*.adoc -} - -xdocs() { - for l in `find po/doc/*.po | xargs -I% basename % .po`; do - docs | sed 's|/\(.*\)\.en\.\(.*\)$|/\1.'"$l"'.\2|' - done -} - -pos() { - find po/ -name '*.po' | grep -Ev '^po/(doc|tests)/' +fuzzs() { + find tests/fuzz/*.clj } -libs | varlist 'libs.go' -mains | varlist 'mains.go' -docs | varlist 'manpages.en.N.adoc' -xdocs | varlist 'manpages.XX.N.adoc' -pos | varlist 'sources.po' +find doc/*.en.*.adoc | varlist 'manpages.en.N.adoc' -find tests/functional/*/*.go -not -name main.go | varlist 'functional/lib.go' -find tests/functional/*/main.go | varlist 'functional/main.go' -find tests/fuzz/*/*.go -not -name main.go | varlist 'fuzz/lib.go' -find tests/fuzz/*/main.go | varlist 'fuzz/main.go' -find tests/benchmarks/*/*.go -not -name main.go | varlist 'benchmarks/lib.go' -find tests/benchmarks/*/main.go | varlist 'benchmarks/main.go' +integrations | varlist 'integration.sh' +fuzzs | varlist 'fuzz.clj' +fuzzs | sed 's/\.clj$/__init.class/' | varlist 'fuzzinit.class' -{ libs; mains; } | sort | sed 's/^\(.*\)\.go$/\1.a:\t\1.go/' -mains | sort | sed 's/^\(.*\)\.go$/\1.bin:\t\1.a/' -mains | sort | sed 's/^\(.*\)\.go$/\1.bin-check:\t\1.bin/' -mains | sort | sed 's|^\(.*\)/main\.go$|\1/main.a:\t\1/$(NAME).a|' +integrations | sed 's/^\(.*\)\.sh$/\1.sh-check:\t\1.sh/' +fuzzs | sed 's/^\(.*\)\.clj$/\1__init.class:\t\1.clj/' +fuzzs | sed 's/^\(.*\)\.clj$/\1.class-check:\t\1__init.class/' diff --git a/src/labareda.edn b/src/labareda.edn new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/labareda.edn diff --git a/src/papod.bin.in b/src/papod.bin.in new file mode 100755 index 0000000..1a25c3b --- /dev/null +++ b/src/papod.bin.in @@ -0,0 +1,5 @@ +#!/bin/sh +set -euo pipefail + +export CLASSPATH="@JAVADIR@/@NAME@.jar:@CLASSPATH@${CLASSPATH:+:}${CLASSPATH:-}" +exec java -client @NAME@ "$@" diff --git a/src/papod.clj b/src/papod.clj new file mode 100644 index 0000000..e8aa5e3 --- /dev/null +++ b/src/papod.clj @@ -0,0 +1,266 @@ +(ns + ^{:sections ["bleh"]} + papod + (:require [base :refer [def- defconst- third]] + [clojure.set :as set] + [clojure.string :as string]) + (:import (java.net StandardProtocolFamily UnixDomainSocketAddress) + (java.nio.channels ServerSocketChannel) + (java.nio.file Files))) + + + +(def- schema + []) + +(defconst- +delimiter+ + "\r\n") + +(defconst- +separator+ + \space) + +(defn- set-of-chars + [min max] + {:pre [(char? min) + (char? max)] + :post [(every? char? %)]} + (->> [(int min) (inc (int max))] + (apply range) + (map char) + (into #{}))) + +(defconst- +digits+ + (set-of-chars \0 \9)) + +(defconst- +letters+ + (set/union + (set-of-chars \A \Z) + (set-of-chars \a \z))) + +(defn- digit? + [c] + (+digits+ c)) + +(defn- letter? + [c] + (+letters+ c)) + +(defn- get-raw-message + [input] + {:pre [(string? input)] + :post [(map? %) + (string? (:input %)) + (#{:needs-more :accepting} (:status %)) + (let [input'old input + {:keys [status input raw-message]} %] + (if (= :needs-more status) + (and (= input'old input) + (nil? raw-message)) + (or (false? raw-message) + (and (= input'old (str raw-message + +delimiter+ + input)) + (nil? (string/index-of raw-message +delimiter+))))))]} + (let [index (string/index-of input +delimiter+)] + (if-not index + {:status :needs-more + :input input} + (let [payload (subs input 0 index) + rest (subs input (+ index (count +delimiter+)))] + {:status :accepting + :input rest + :raw-message (and (not (string/blank? payload)) + payload)})))) + +(defn- valid-err? + [[x offset {:keys [message type data] :as err}]] + (and (nil? x) + (nil? offset) + (map? err) + (string? message) + (keyword? type) + (map? data))) + +(defn- valid-prefix? + [x] +;; FIXME + true) + +(defn- parse-prefix-components + [s] + {:pre [] + :post []} +;; FIXME + ()) + +(defn- parse-prefix + [s] + {:pre [(string/starts-with? s ":")] + :post [(let [[prefix offset err] %] + (if err + (valid-err? %)) + (valid-prefix? %))]} + (let [end (string/index-of s +separator+)] + (cond + (= 1 (count s)) + [nil nil {:message "Empty prefix and empty message" + :type :bad-empty-prefix-and-message + :data {}}] + (= 1 end) + [nil nil {:message "Empty prefix" + :type :bad-empty-prefix + :data {}}] + (nil? end) + [nil nil {:message "Missing prefix separator" + :type :no-prefix-separator + :data {}}] + :else + (parse-prefix-components s)))) + +(defn- parse-command + [s] + {:pre [(not (string/blank? s))] + :post [(let [[command offset err] %] + (if err + (valid-err? %) + (and (string? command) + (not (string/blank? command)) + (pos? offset) + (= offset (count command)) + (nil? err))))]} + (loop [chars [] + index 0] + (if (or (= index (count s)) + (= +separator+ (nth s index))) + [(apply str chars) (count chars)] + (let [c (nth s index)] + (if-not (letter? c) + [nil nil {:message "Bad char for non-numerical command" + :data {:c c}}] + (recur (conj chars c) + (inc index))))))) + +(defconst- params-re + (java.util.regex.Pattern/compile (str +separator+))) + +(defn- parse-params + [s] + {:pre [(string? s)] + :post [(nil? (third %)) + (vector? (first %)) + (every? string? (first %)) + (= (count s) (second %))]} + (if (zero? (count s)) + [[] 0] + (loop [[part & rest] (string/split s params-re) + out []] + (cond + (not part) + [out (count s)] + (string/starts-with? part ":") + [(conj out + (apply str + (concat [part] rest))) + (count s)] + :else + (recur rest (conj out part)))))) + +(defn- valid-message? + [{:keys [prefix command params] :as message}] + (and (map? message) + (or (nil? prefix) + (valid-prefix? prefix)) + (string? command) + (vector? params) + (every? string? params))) + +(defn- parse-message + [raw-message] + {:pre [(not (string/blank? raw-message))] + :post [(let [[message err] %] + (if err + (valid-err? [nil nil err]) + (valid-message? message)))]} + (let [[prefix offset1 err] (if (string/starts-with? raw-message ":") + (parse-prefix raw-message) + [nil 0])] + (if err + [nil err] + (let [[command offset2 err] (parse-command (subs raw-message offset1))] + (if err + [nil err] + (let [[params _ err] (parse-params (subs raw-message + (+ offset1 offset2)))] + (if err + [nil err] + [{:prefix prefix + :command command + :params params} + nil]))))))) + +(defn- send-replies! + [replies w] + ;; FIXME + ) + +(defn- replies-for! + [{} components] + ;; wait-for + ) + +(defn- handle-message! + [message w components] + (send-replies! (replies-for! message components) + w)) + +(defn- process-message! + [raw-message w components] + (let [[message err] (parse-message raw-message)] + (if err + (:log :bad-message err) + (handle-message! message w components)))) + +(defn- process-input! + [input w components] + (let [{:keys [status input raw-message]} (get-raw-message input)] + (if (= status :needs-more) + input + (do + (process-message! raw-message w components) + (recur input w components))))) + +(defn- db + [datomic] + :db) + +(defconst- +buffer-size+ + 1024) + +(defconst- +charset+ + "UTF-8") + +(defn- client-loop! + [socket components] + (let [r (.getInputStream socket) + w (.getOutputStream socket) + b (make-array Byte/TYPE +buffer-size+)] + (loop [acc ""] + (let [n (.read r b)] + (when (pos? n) + (recur (process-input! (str acc (String. b +charset+)) + w + components))))))) + +(defconst- +socket-path+ + "papod.socket") + +(defn- start + [components] + (let [address (UnixDomainSocketAddress/of +socket-path+) + server-channel (ServerSocketChannel/open StandardProtocolFamily/UNIX)] + (Files/deleteIfExists address) + (.bind server-channel address) + (while true + (let [client (.accept server-channel)] + (.start (Thread/ofVirtual) + #(client-loop! client components)))))) diff --git a/tests/cli-opts.sh b/tests/cli-opts.sh deleted file mode 100755 index fcb62ca..0000000 --- a/tests/cli-opts.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -set -eu - -exit diff --git a/tests/fuzz/api.clj b/tests/fuzz/api.clj new file mode 100644 index 0000000..d389c13 --- /dev/null +++ b/tests/fuzz/api.clj @@ -0,0 +1,11 @@ +(ns fuzz.api + (:require [base]) + (:gen-class)) + + + +(defn -main + [& args] + (base/run-fuzz-loop (System/currentTimeMillis) + (base/duration-ms-from-args args) + (fn [_i] (base/uuidv7)))) diff --git a/tests/integration.clj b/tests/integration.clj new file mode 100644 index 0000000..18811a2 --- /dev/null +++ b/tests/integration.clj @@ -0,0 +1,6 @@ +(ns integration + (:gen-class)) + + +(defn -main + [& _args]) diff --git a/tests/lib.sh b/tests/lib.sh new file mode 100644 index 0000000..649c61d --- /dev/null +++ b/tests/lib.sh @@ -0,0 +1,122 @@ +#!/bin/sh + + +export TMPDIR="${TMPDIR:-$XDG_RUNTIME_DIR}" + +end="\033[0m" +red="\033[0;31m" +green="\033[0;32m" +yellow="\033[0;33m" + +N= +OUT= +ERR= +STATUS= + +ERROR() { + # shellcheck disable=2059 + printf "${red}ERROR${end}" +} + +print_debug_info() { + # shellcheck disable=2016 + printf 'LINENO: %s\n$OUT: %s\n$ERR: %s\n' \ + "$N" "$OUT" "$ERR" >&2 +} + +assert_status() { + if [ "$STATUS" != "$1" ]; then + printf '\n%s: Bad status.\n\nexpected: %s\ngot: %s\n' \ + "$(ERROR)" "$1" "$STATUS" >&2 + print_debug_info + exit 1 + fi +} + +assert_usage() { + if ! grep -Fq 'Usage' "$1"; then + echo 'Expected to find "Usage" text, it was missing:' >&2 + cat "$1" >&2 + print_debug_info + exit 1 + fi +} + +assert_empty_stream() { + if [ -s "$2" ]; then + FMT='\n%s: Expected %s (%s) to be empty, but has content:\n%s\n' + # shellcheck disable=2059 + printf "$FMT" \ + "$(ERROR)" "$1" "$2" "$(cat "$2")" >&2 + print_debug_info + exit 1 + fi +} + +assert_empty_stdout() { + assert_empty_stream STDOUT "$OUT" +} + +assert_empty_stderr() { + assert_empty_stream STDERR "$ERR" +} + +assert_stream() { + if [ "$(cat "$2")" != "$3" ]; then + printf '\n%s: Bad %s (%s)\n\nexpected: %s\ngot: %s\n' \ + "$(ERROR)" "$1" "$2" "$3" "$(cat "$2")" >&2 + print_debug_info + exit 1 + fi +} + +assert_stdout() { + assert_stream STDOUT "$OUT" "$1" +} + +assert_stderr() { + assert_stream STDERR "$ERR" "$1" +} + +assert_grep_stream() { + if ! grep -qE "$3" "$2"; then + printf '\n%s: Bad %s (%s)\n\ngrepping: %s\nin:\n%s\n' \ + "$(ERROR)" "$1" "$2" "$3" "$(cat "$2")" >&2 + print_debug_info + exit 1 + fi +} + +assert_grep_stdout() { + assert_grep_stream STDOUT "$OUT" "$1" +} + +assert_grep_stderr() { + assert_grep_stream STDERR "$ERR" "$1" +} + +assert_fgrep_stream() { + if ! grep -Fq -- "$3" "$2"; then + printf '\n%s: Bad %s (%s)\n\ngrepping: %s\nin:\n%s\n' \ + "$(ERROR)" "$1" "$2" "$3" "$(cat "$2")" >&2 + print_debug_info + exit 1 + fi +} + +assert_fgrep_stdout() { + assert_fgrep_stream STDOUT "$OUT" "$1" +} + +assert_fgrep_stderr() { + assert_fgrep_stream STDERR "$ERR" "$1" +} + +testing() { + printf "${yellow}testing${end}: %s..." "$1" >&2 +} + +test_ok() { + # shellcheck disable=2059 + printf " ${green}OK${end}.\n" >&2 +} diff --git a/tests/unit.clj b/tests/unit.clj new file mode 100644 index 0000000..e92b389 --- /dev/null +++ b/tests/unit.clj @@ -0,0 +1,54 @@ +(ns unit + (:require [clojure.test :as t :refer [are deftest is testing]] + [papod]) + (:gen-class)) + + + +(def parse @#'papod/get-raw-message) +(deftest test_get-raw-message + (testing ":needs-more states" + (is (= (parse "") + {:input "" + :status :needs-more})) + (is (= (parse "some text") + {:input "some text" + :status :needs-more})) + (is (= (parse "newline\nonly") + {:input "newline\nonly" + :status :needs-more})) + (is (= (parse "CR\rLF\n\r") + {:input "CR\rLF\n\r" + :status :needs-more}))) + (testing "empty messages" + (is (= (parse " \r\nblah\r\n") + {:status :accepting + :input "blah\r\n" + :raw-message false}))) + (testing "input splitting" + (is (= (parse "BEFORE\r\nAFTER\r\nTAIL") + {:status :accepting + :input "AFTER\r\nTAIL" + :raw-message "BEFORE"})))) + +(def parse-message @#'papod/parse-message) +(deftest test_parse-message + (testing "only commands" + (is (= (parse-message "CMD") + [{:prefix nil + :command "CMD" + :params []} + nil])))) + +; (parse-message " CMD") + + +(defn -main + [& _args] + (binding [*out* *err*] + (let [{:keys [fail error] :as res} (t/run-tests 'unit) + status (if (zero? (+ fail error)) + 0 + 1)] + (prn res) + (System/exit status)))) |
