--- /dev/null
+insert ./__attempting.lib
+insert ./__caps_neg_empty.lib
+insert ./__init_clear_hostname.lib
+insert ./__servermsglogged.lib
+
+× JOIN0
+log 1 > JOIN :CHANNEL
+insert servermsglogged : +0 MSG :foo!~foobarbaz@baz.bar.foo JOIN CHANNEL
+
+× identifying-join-ch_test0
+insert JOIN0 : +0 CHANNEL #ch_test0
+log 1 $ users:me:user set to: [~foobarbaz]
+log 1 $ users:me:host set to: [baz.bar.foo]
+
+× join-empty
+insert servermsglogged : +0 MSG :foo.bar.baz 353 foo @ CHANNEL :foo
+log 1 $ channels:CHANNEL:exits cleared
+insert servermsglogged : +0 MSG :foo.bar.baz 366 foo CHANNEL :End of /NAMES list.
+log 1 $ channels:CHANNEL:user_ids set to: [me]
+
+× part0
+> /part
+log 1 > PART :CHANNEL
+insert servermsglogged : +0 MSG :foo!~foobarbaz@baz.bar.foo PART :CHANNEL
+log 1 $ channels:CHANNEL:exits:me set to: [P]
+log 1 $ channels:CHANNEL:user_ids cleared
+
+× part1
+log 1 $ channels:CHANNEL:exits:me cleared
+log 1 $ channels:CHANNEL cleared
+
+× part-ch_test0
+insert part0 : +0 CHANNEL #ch_test0
+log 3 $ foo!~foobarbaz@baz.bar.foo parts
+insert part1 : +1 CHANNEL #ch_test0
+
+× part-ch_test1
+insert part0 : +0 CHANNEL #ch_test1
+log 4 $ foo!~foobarbaz@baz.bar.foo parts
+insert part1 : +1 CHANNEL #ch_test1
+
+× conn-init
+insert attempting-foobarbaz : +1 §§ 6697
+log 2 $ - nickname: foo
+log 2 $ - realname: bar
+log 2 $ - username: baz
+log 2 $ - no password
+log 1 $ connection_state set to: [connected]
+log WIN_IDS $ CONNECTED
+log 1 > CAP LS :302
+log 1 > USER baz 0 * :bar
+log 1 > NICK :foo
+insert caps-neg-empty
+insert servermsglogged : +0 MSG :foo.bar.baz 001 foo :Welcome to the foo.bar.baz network
+log 1 $ users:me:nick set to: [?]
+log 1 $ users:me:nick set to: [foo]
+
+× privmsg_ch_test0
+> /privmsg #ch_test0 TXT
+log 1 > PRIVMSG #ch_test0 :TXT
+log 3 > [foo] TXT
+
+× disconnect
+log 1 $ connection_state set to: [EXIT_MSG]
+insert isupport-clear
+log 1 $ channels cleared
+log 1 $ connection_state set to: []
+log 2,3,4 $ DISCONNECTED
+log 1 $ users cleared
+
+× reconnect
+> /reconnect
+insert conn-init : +0 WIN_IDS 2,3,4
+insert identifying-join-ch_test0
+insert join-empty : +0 CHANNEL #ch_test0
+log 3 $ residents: foo
+
+× cmd_nick
+> /nick ?
+log 1 > NICK :?
+
+× ×--------------------------
+
+> /connect foo.bar.baz:6697 foo bar:baz
+insert init-clear-hostname : +1 HOSTNAME foo.bar.baz
+log 1 $ port set to: [6697]
+log 1 $ nick_wanted set to: [foo]
+log 1 $ user_wanted set to: [baz]
+log 1 $ realname set to: [bar]
+insert conn-init : +0 WIN_IDS 2
+
+# check inability to privmsg into channel not yet joined
+> /window 2
+> /privmsg #test msg_test0
+log 2 $ not sending, since not in channel
+
+# check join with minimum context (no topic, no other users etc.)
+> /join #ch_test0
+insert identifying-join-ch_test0
+insert join-empty : +0 CHANNEL #ch_test0
+log 3 $ residents: foo
+
+# check privmsg into channel from other window only works by direct addressing
+> /window 2
+> msg_test1
+log 2 # invalid prompt command: not prefixed by /
+insert privmsg_ch_test0 : +1 TXT msg_test1
+
+# check from within channel window privmsg works directly and indirectly
+> /window 3
+> msg_test2
+log 1 > PRIVMSG #ch_test0 :msg_test2
+log 3 > [foo] msg_test2
+insert privmsg_ch_test0 : +1 TXT msg_test2
+
+# check /part from minimal context, and that it only works from within channel window, and if already in channel
+> /window 2
+> /part
+log 2 # invalid prompt command: /part unknown
+> /window 3
+insert part-ch_test0
+> /part
+log 3 $ not in that channel
+
+# check /join without argument joins channel previously joined in same window
+> /join
+insert JOIN0 : +0 CHANNEL #ch_test0
+insert join-empty : +0 CHANNEL #ch_test0
+log 3 $ residents: foo
+
+# check cannot /join channel already joined, neither from its window nor from elsewhere
+> /join
+log 3 $ already in that channel
+> /join #ch_test0
+log 3 $ already in that channel
+> /window 2
+> /join #ch_test0
+log 2 $ already in that channel
+
+# check join to different channel name initiates new window, even if there's already a window for a channel that's been /parted
+> /window 3
+insert part-ch_test0
+> /join #ch_test1
+insert JOIN0 : +0 CHANNEL #ch_test1
+insert join-empty : +0 CHANNEL #ch_test1
+log 4 $ residents: foo
+
+# check /join into channel with other users, with multi-line 353, and @ rather than =
+> /join #ch_test0
+insert JOIN0 : +0 CHANNEL #ch_test0
+insert servermsglogged : +0 MSG :foo.bar.baz 353 foo = #ch_test0 :foo bar baz
+log 1 $ channels:#ch_test0:exits cleared
+log 1 $ users:1:nick set to: [?]
+log 1 $ users:1:nick set to: [bar]
+log 1 $ users:2:nick set to: [?]
+log 1 $ users:2:nick set to: [baz]
+insert servermsglogged : +0 MSG :foo.bar.baz 353 foo = #ch_test0 :oof rab zab
+log 1 $ users:3:nick set to: [?]
+log 1 $ users:3:nick set to: [oof]
+log 1 $ users:4:nick set to: [?]
+log 1 $ users:4:nick set to: [rab]
+log 1 $ users:5:nick set to: [?]
+log 1 $ users:5:nick set to: [zab]
+insert servermsglogged : +0 MSG :foo.bar.baz 366 foo #ch_test0 :End of /NAMES list.
+log 1 $ channels:#ch_test0:user_ids set to: [1], [2], [3], [4], [5], [me]
+log 3 $ residents: bar, baz, oof, rab, zab, foo
+
+# check /join into channel with topic set
+> /window 4
+insert part-ch_test1
+> /join
+insert JOIN0 : +0 CHANNEL #ch_test1
+insert servermsglogged : +0 MSG :foo.bar.baz 332 foo #ch_test1 :foo bar baz
+log 1 $ channels:#ch_test1:exits cleared
+insert servermsglogged : +0 MSG :foo.bar.baz 333 foo #ch_test1 bar!~bar@OLD.bar.bar 1234567890
+log 1 $ channels:#ch_test1:topic set to: [Topic(what='foo bar baz', who=NickUserHost(nick='bar', user='~bar', host='OLD.bar.bar'))]
+log 4 $ bar!~bar@OLD.bar.bar set topic: foo bar baz
+insert servermsglogged : +0 MSG :foo.bar.baz 353 foo @ #ch_test1 :foo bar
+insert servermsglogged : +0 MSG :foo.bar.baz 366 foo #ch_test1 :End of /NAMES list.
+log 1 $ channels:#ch_test1:user_ids set to: [1], [me]
+log 4 $ residents: bar, foo
+
+# check _observed_ topic change _does_ affect users database, and …
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar TOPIC #ch_test1 :foo bar baz
+log 1 $ users:1:user set to: [~bar]
+log 1 $ users:1:host set to: [bar.bar]
+log 1 $ channels:#ch_test1:topic set to: [Topic(what='foo bar baz', who=NickUserHost(nick='bar', user='~bar', host='bar.bar'))]
+
+# … is echoed into channel window _if_ either content or setter change
+log 4 $ bar!~bar@bar.bar set topic: foo bar baz
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar TOPIC #ch_test1 :foo bar baz
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar TOPIC #ch_test1 :foo foo baz
+log 1 $ channels:#ch_test1:topic set to: [Topic(what='foo foo baz', who=NickUserHost(nick='bar', user='~bar', host='bar.bar'))]
+log 4 $ bar!~bar@bar.bar set topic: foo foo baz
+
+# check effects of other users' messages (PRIVMSG and NOTICE)
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar PRIVMSG #ch_test0 :msg_test3 msg_test4
+log 3 < [bar] msg_test3 msg_test4
+insert servermsglogged : +0 MSG :oof!~oof@oof.oof NOTICE #ch_test0 :msg_test5 msg_test6
+log 1 $ users:3:user set to: [~oof]
+log 1 $ users:3:host set to: [oof.oof]
+log 3 < (oof) msg_test5 msg_test6
+
+# check part of user visible in other channel
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar PART :#ch_test0
+log 1 $ channels:#ch_test0:exits:1 set to: [P]
+log 1 $ channels:#ch_test0:user_ids set to: [2], [3], [4], [5], [me]
+log 3 $ bar!~bar@bar.bar parts
+log 1 $ channels:#ch_test0:exits:1 cleared
+
+# check part of user NOT visible in other channel
+insert servermsglogged : +0 MSG :oof!~oof@oof.oof PART :#ch_test0
+log 1 $ channels:#ch_test0:exits:3 set to: [P]
+log 1 $ channels:#ch_test0:user_ids set to: [2], [4], [5], [me]
+log 3 $ oof!~oof@oof.oof parts
+log 1 $ channels:#ch_test0:exits:3 cleared
+log 1 $ users:3 cleared
+
+# check other-user part with exit message
+insert servermsglogged : +0 MSG :zab!~zab@zab.zab PART #ch_test0 :goodbye world
+log 1 $ users:5:user set to: [~zab]
+log 1 $ users:5:host set to: [zab.zab]
+log 1 $ channels:#ch_test0:exits:5 set to: [Pgoodbye world]
+log 1 $ channels:#ch_test0:user_ids set to: [2], [4], [me]
+log 3 $ zab!~zab@zab.zab parts: goodbye world
+log 1 $ channels:#ch_test0:exits:5 cleared
+log 1 $ users:5 cleared
+
+# check re-join of user kept visible in other channel
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar JOIN :#ch_test0
+log 1 $ channels:#ch_test0:user_ids set to: [1], [2], [4], [me]
+log 3 $ bar!~bar@bar.bar joins
+
+# check re-join of user NOT kept visible in other channel
+insert servermsglogged : +0 MSG :oof!~oof@oof.oof JOIN :#ch_test0
+log 1 $ users:6:nick set to: [?]
+log 1 $ users:6:nick set to: [oof]
+log 1 $ users:6:user set to: [~oof]
+log 1 $ users:6:host set to: [oof.oof]
+log 1 $ channels:#ch_test0:user_ids set to: [1], [2], [4], [6], [me]
+log 3 $ oof!~oof@oof.oof joins
+
+# check renaming of user communicated into all windows of channels they're in – be it others, or oneself
+insert servermsglogged : +0 MSG :bar!~bar@bar.bar NICK :bazbaz
+log 1 $ users:1:nick set to: [bazbaz]
+log 3,4 $ bar!~bar@bar.bar renames bazbaz
+insert cmd_nick : +1 ? myself
+insert servermsglogged : +0 MSG :foo!~foobarbaz@baz.bar.foo NICK :myself
+log 1 $ users:me:nick set to: [myself]
+log 3,4 $ foo!~foobarbaz@baz.bar.foo renames myself
+log 1 $ nick_wanted set to: [myself]
+
+# reverting to foo to easen fragment re-use later on
+insert cmd_nick : +1 ? foo
+insert servermsglogged : +0 MSG :myself!~foobarbaz@baz.bar.foo NICK :foo
+log 1 $ users:me:nick set to: [foo]
+log 3,4 $ myself!~foobarbaz@baz.bar.foo renames foo
+log 1 $ nick_wanted set to: [foo]
+
+# check QUIT affecting user in all their channels, removing them from DB
+insert servermsglogged : +0 MSG :bazbaz!~bar@bar.bar QUIT :Client Quit
+log 1 $ users:1:exit_msg set to: [QClient Quit]
+log , $ bazbaz!~bar@bar.bar quits: Client Quit
+log 1 $ channels:#ch_test0:exits:1 set to: [QClient Quit]
+log 1 $ channels:#ch_test0:user_ids set to: [2], [4], [6], [me]
+log 3 $ bazbaz!~bar@bar.bar quits: Client Quit
+log 1 $ channels:#ch_test0:exits:1 cleared
+log 1 $ channels:#ch_test1:exits:1 set to: [QClient Quit]
+log 1 $ channels:#ch_test1:user_ids set to: [me]
+log 4 $ bazbaz!~bar@bar.bar quits: Client Quit
+log 1 $ channels:#ch_test1:exits:1 cleared
+log 1 $ users:1 cleared
+
+# check effects of own QUIT while present in one channel
+insert part-ch_test1
+> /disconnect
+log 1 > QUIT :ircplom says bye
+insert servermsglogged : +0 MSG :foo!~foobarbaz@baz.bar.foo QUIT :Client Quit
+log 1 $ users:me:exit_msg set to: [QClient Quit]
+log , $ foo!~foobarbaz@baz.bar.foo quits: Client Quit
+log 1 $ channels:#ch_test0:exits:me set to: [QClient Quit]
+log 1 $ channels:#ch_test0:user_ids set to: [2], [4], [6]
+log 3 $ foo!~foobarbaz@baz.bar.foo quits: Client Quit
+log 1 $ channels:#ch_test0:exits:me cleared
+insert servermsglogged : +0 MSG ERROR :Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]
+insert disconnect : +1 EXIT_MSG Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]
+
+# check /reconnect calling auto-rejoin on the one channel inhabited on previous leaving
+insert reconnect
+
+# check same mechanism holding on externally triggered disconnect
+servermsg 0 FAKE_IRC_CONN_ABORT_EXCEPTION
+insert disconnect : +1 EXIT_MSG broken: FAKE_IRC_CONN_ABORT_EXCEPTION
+log 1 $ will retry connecting in 1 seconds
+insert reconnect