;;; websocket-functional-test.el --- Simple functional testing

;; Copyright (c) 2013, 2016  Free Software Foundation, Inc.

;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Usage: emacs -batch -Q -L . -l websocket-functional-test.el
;;
;; Note: this functional tests requires that you have python with the
;; Tornado web server.  See http://www.tornadoweb.org/en/stable/ for
;; information on aquiring.

(require 'tls)   ;; tests a particular bug we had on emacs 23
(setq debug-on-error t)
(require 'websocket)
(eval-when-compile (require 'cl))

;;;;;;;;;;;;;;;;;;;;;;;
;; Local server test ;;
;;;;;;;;;;;;;;;;;;;;;;;

(message "Testing with local server")

(setq websocket-debug t)

(defvar wstest-server-buffer (get-buffer-create "*wstest-server*"))
(defvar wstest-server-name "wstest-server")
(defvar wstest-server-proc
  (start-process wstest-server-name wstest-server-buffer
                 "python" "testserver.py" "--log_to_stderr" "--logging=debug"))
(sleep-for 1)

(defvar wstest-msgs nil)
(defvar wstest-closed nil)

(message "Opening the websocket")

(defvar wstest-ws
  (websocket-open
   "ws://127.0.0.1:9999"
   :on-message (lambda (_websocket frame)
                 (push (websocket-frame-text frame) wstest-msgs)
                 (message "ws frame: %S" (websocket-frame-text frame))
                 (error "Test error (expected)"))
   :on-close (lambda (_websocket) (setq wstest-closed t))))

(defun wstest-pop-to-debug ()
  "Open websocket log buffer. Not used in testing. Just for debugging."
  (interactive)
  (pop-to-buffer (websocket-get-debug-buffer-create wstest-ws)))

(sleep-for 0.1)
(assert (websocket-openp wstest-ws))

(assert (null wstest-msgs))

(websocket-send-text wstest-ws "你好")

(sleep-for 0.1)
(assert (equal (car wstest-msgs) "You said: 你好"))
(setf (websocket-on-error wstest-ws) (lambda (_ws _type _err)))
(websocket-send-text wstest-ws "Hi after error!")
(sleep-for 0.1)
(assert (equal (car wstest-msgs) "You said: Hi after error!"))

(websocket-close wstest-ws)
(assert (null (websocket-openp wstest-ws)))

(if (not (eq system-type 'windows-nt))
    ; Windows doesn't have support for the SIGSTP signal, so we'll just kill
    ; the process.
    (stop-process wstest-server-proc))
(kill-process wstest-server-proc)

;; Make sure the processes are closed.  This happens asynchronously,
;; so let's wait for it.
(sleep-for 1)
(assert (null (process-list)) t)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Remote server test, with wss ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; echo.websocket.org has an untrusted certificate, for the test to
;; proceed, we need to disable trust checking.
(setq tls-checktrust nil)

(when (>= (string-to-number (substring emacs-version 0 2)) 24)
  (message "Testing with wss://echo.websocket.org")
  (when (eq system-type 'windows-nt)
    (message "Windows users must have gnutls DLLs in the emacs bin directory."))
  (setq wstest-ws
        (websocket-open
         "wss://echo.websocket.org"
         :on-open (lambda (_websocket)
                    (message "Websocket opened"))
         :on-message (lambda (_websocket frame)
                       (push (websocket-frame-text frame) wstest-msgs)
                       (message "ws frame: %S" (websocket-frame-text frame)))
         :on-close (lambda (_websocket)
                     (message "Websocket closed")
                     (setq wstest-closed t)))
        wstest-msgs nil)
  (sleep-for 0.3)
  (assert (websocket-openp wstest-ws))
  (sleep-for 0.6)
  (assert (eq 'open (websocket-ready-state wstest-ws)))
  (assert (null wstest-msgs))
  (websocket-send-text wstest-ws "Hi!")
  (sleep-for 1)
  (assert (equal (car wstest-msgs) "Hi!"))
  (websocket-close wstest-ws))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Local client and server ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(message "Testing with emacs websocket server.")
(message "If this does not pass, make sure your firewall allows the connection.")
(setq wstest-closed nil)
(let ((server-conn (websocket-server
                    9998
                    :host 'local
                    :on-message (lambda (ws frame)
                                  (message "Server received text!")
                                  (websocket-send-text
                                   ws (websocket-frame-text frame)))
                    :on-open (lambda (_websocket) "Client connection opened!")
                    :on-close (lambda (_websocket)
                                (setq wstest-closed t)))))
  (setq wstest-msgs nil
        wstest-ws
        (websocket-open
         "ws://localhost:9998"
         :on-message (lambda (_websocket frame)
                       (message "ws frame: %S" (websocket-frame-text frame))
                       (push
                        (websocket-frame-text frame) wstest-msgs))))

  (assert (websocket-openp wstest-ws))
  (websocket-send-text wstest-ws "你好")
  (sleep-for 0.3)
  (assert (equal (car wstest-msgs) "你好"))
  (websocket-server-close server-conn))
(assert wstest-closed)
(websocket-close wstest-ws)

(sleep-for 1)
(assert (null (process-list)) t)
(message "\nAll tests passed!\n")
