From: Christian Heller Date: Mon, 22 Dec 2025 04:24:04 +0000 (+0100) Subject: Treat Channel.modes_* as Completables, mark dictionary access in MsgParseExpectation... X-Git-Url: https://plomlompom.com/repos/booking/%22https:/validator.w3.org/day?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=ircplom Treat Channel.modes_* as Completables, mark dictionary access in MsgParseExpectation command paths. --- diff --git a/src/ircplom/client.py b/src/ircplom/client.py index 744bee0..1bb742f 100644 --- a/src/ircplom/client.py +++ b/src/ircplom/client.py @@ -13,8 +13,8 @@ from uuid import UUID, uuid4 from ircplom.db_primitives import ( Clearable, Completable, CompletableStringsSet, DbLinked, DbLinking, Dict, DictItem, UpdatingAttrsMixin, UpdatingCompletable, - UpdatingCompletableStringsOrdered, UpdatingCompletableStringsSet, - UpdatingDict, UpdatingMixin) + UpdatingCompletableDict, UpdatingCompletableStringsOrdered, + UpdatingCompletableStringsSet, UpdatingDict, UpdatingMixin) from ircplom.events import ( AffectiveEvent, CrashingException, ExceptionEvent, QueueMixin) from ircplom.irc_conn import ( @@ -135,7 +135,7 @@ class Channel: exits: Dict[str] modes_listy: Dict[tuple[str, ...]] modes_valued: Dict[str] - modes_toggled: str = '' + modes_toggled: Iterable[str] @dataclass @@ -190,8 +190,9 @@ class _Channel(UpdatingAttrsMixin, _DbLinked, Channel): user_ids: UpdatingCompletableStringsSet topic: _UpdatingCompletableTopic exits: UpdatingDict[str] - modes_listy: UpdatingDict[tuple[str, ...]] - modes_valued: UpdatingDict[str] + modes_listy: UpdatingCompletableDict[tuple[str, ...]] + modes_valued: UpdatingCompletableDict[str] + modes_toggled: UpdatingCompletableStringsSet def _id_from_nick(self, nick: str, create_if_none: bool) -> str: user_id = self._db.users.id_for_nickuserhost(NickUserHost(nick), @@ -228,10 +229,12 @@ class _Channel(UpdatingAttrsMixin, _DbLinked, Channel): self.exits[user.id_] = msg self.user_ids.completable_remove(user.id_, on_complete=True) del self.exits[user.id_] + self.modes_listy.completed = None for c in [c for c in self._db.get_membership_modes().keys() if user.id_ in self.modes_listy.get(c, tuple())]: self.modes_listy[c] = tuple(uid for uid in self.modes_listy[c] if uid != user.id_) + self.modes_listy.complete() self._db.users.purge() def set_modes(self, modeset: str, args_str='') -> None: @@ -263,6 +266,9 @@ class _Channel(UpdatingAttrsMixin, _DbLinked, Channel): except AssertionError: raise ImplementationFail( # pylint: disable=raise-missing-from f'channel mode setting {modeset} on args: {args_str}') + modes_attrs = self.modes_listy, self.modes_valued, self.modes_toggled + for attr in modes_attrs: + attr.completed = None for do_add, char, arg in todos: if char in modes['A'] + prefix_modes: char_modes = list(self.modes_listy.get(char, tuple())) @@ -276,10 +282,11 @@ class _Channel(UpdatingAttrsMixin, _DbLinked, Channel): elif char in self.modes_valued.keys(): del self.modes_valued[char] else: # char in chanmodes['D'] - self.modes_toggled\ - = ''.join(sorted(set((self.modes_toggled + char) if do_add - else (c for c in self.modes_toggled - if c != char)))) + getattr(self.modes_toggled, + f'completable_{"add" if do_add else "remove"}' + )(char, False) + for attr in modes_attrs: + attr.complete() class _SetNickuserhostMixin: @@ -517,6 +524,11 @@ class _ClientDb(Clearable, UpdatingAttrsMixin, SharedClientDbFields, DbLinking if path not in self._updates_cache\ or val != self._updates_cache[path]: self._updates_cache[path] = val + if val in (None, Dict()): + for cache_path in [p for p in self._updates_cache + if len(p) > len(path) + and p[:len(path)] == path]: + del self._updates_cache[cache_path] return [(path, val)] return [] @@ -533,23 +545,19 @@ class _ClientDb(Clearable, UpdatingAttrsMixin, SharedClientDbFields, DbLinking else None) if isinstance(parent, Dict) else getattr(parent, last_step)) - # treat anything without UpdatingMixin as single endnode to return + # ignore Completables if not .completed + if isinstance(val_at_path, Completable): + if val_at_path.completed is None: + return [] + val_at_path = val_at_path.completed + + # treat any non-Dict without UpdatingMixin as single endnode to return # (NB: this would include None, as a deletion signal) - if not isinstance(val_at_path, UpdatingMixin): + if not isinstance(val_at_path, (Dict, UpdatingMixin)): return update_unless_cached(path, val_at_path) - # for completable, only return its .completed - if isinstance(val_at_path, Completable): - if val_at_path.completed is not None: - return update_unless_cached(path, val_at_path.completed) - return [] - # for empty Dict, return one as clearing signal if isinstance(val_at_path, Dict) and not val_at_path.keys(): - for cache_path in [p for p in self._updates_cache - if len(p) > len(path) - and p[:len(path)] == path]: - del self._updates_cache[cache_path] return update_unless_cached(path, Dict()) # if node at path has children, (only) return _their_ endnode updates @@ -562,6 +570,12 @@ class _ClientDb(Clearable, UpdatingAttrsMixin, SharedClientDbFields, DbLinking for key in sub_items: p = path + (key,) updates += self.into_endnode_updates(p) + + # signal absence of any _former_ sub_items siblings + for cache_path in [p for p in self._updates_cache + if p[:-1] == path + and p[-1] not in sub_items]: + updates += update_unless_cached(cache_path, None) return updates def clear(self) -> None: @@ -850,13 +864,16 @@ class Client(ABC, ClientQueueMixin): for task, args in [t for t in ret['_tasks'].items() if t[0].verb == verb]: path = list(task.path) - node: Any = (ret[path.pop(0)] - if task.path and path[0].isupper() else self) - for step in path: - key = ret[step] if step.isupper() else step - node = (node[key] if isinstance(node, Dict) - else (node(key) if callable(node) - else getattr(node, key))) + node: Any = (ret[path.pop(0)[1]] + if task.path and path[0][1].isupper() else self) + for is_dict_key, step_name in path: + if is_dict_key: + if step_name.isupper(): + step_name = ret[step_name] + node = node[step_name] + else: + node = (node(step_name) if callable(node) + else getattr(node, step_name)) for arg in args: if task.verb == 'setattr': setattr(node, arg, ret[arg]) diff --git a/src/ircplom/client_tui.py b/src/ircplom/client_tui.py index 5051080..5541a4c 100644 --- a/src/ircplom/client_tui.py +++ b/src/ircplom/client_tui.py @@ -297,6 +297,7 @@ class _UpdatingChannel(_UpdatingNode, _DbLinked, Channel): exits: _UpdatingDict[str] modes_listy: _UpdatingDict[tuple[str, ...]] modes_valued: _UpdatingDict[str] + modes_toggled: set[str] def prefix_for(self, user_id: str) -> str: 'Construct prefixes string for user of user_id.' diff --git a/src/ircplom/db_primitives.py b/src/ircplom/db_primitives.py index 3cffcab..c4cec3a 100644 --- a/src/ircplom/db_primitives.py +++ b/src/ircplom/db_primitives.py @@ -88,7 +88,7 @@ class Dict(Clearable, Generic[DictItem]): return tuple((val for val in self._dict.values())) def get(self, key: str, default: DictItem) -> DictItem: - 'Wrapper around dict.get(key, default), excluding None returns.' + 'Wrapper around dict.get(key, default), sans implicit None defaults.' return self._dict.get(key, default) @@ -139,17 +139,11 @@ class CompletableStringsSet(_CompletableStringsCollection, Set): def _on_set(self, m_name: str, item: str, on_complete: bool, exists: bool ) -> None: - method = getattr(self._collected, m_name) + assert (self.completed is not None) == on_complete + if (item in self._collected) == exists: + getattr(self._collected, m_name)(item) if on_complete: - assert self.completed is not None - assert (item in self.completed) == exists - assert (item in self._collected) == exists - method(item) self.complete() - else: - assert self.completed is None - assert (item in self._collected) == exists - method(item) def completable_add(self, item: str, on_complete: bool) -> None: 'Put item into collection.' @@ -187,6 +181,15 @@ class _CompletableStringsOrdered(Clearable, _CompletableStringsCollection): self.complete() +class _CompletableDict(Completable, Dict[DictItem]): + completed: Optional[Dict[DictItem]] = None + + def complete(self) -> None: + self.completed: Dict[DictItem] = Dict() + for k, v in self._dict.items(): + self.completed[k] = v + + class UpdatingMixin: 'Ensures update trigger subclasses can call on any data changes.' @@ -259,7 +262,11 @@ class UpdatingCompletableStringsOrdered(UpdatingCompletable, 'Clearable, updating, completable tuple of strings.' -_DbType = TypeVar('_DbType') +class UpdatingCompletableDict(UpdatingCompletable, _CompletableDict[DictItem]): + 'Updating, completable Dict.' + + +_DbType = TypeVar('_DbType', bound='DbLinking') _Same = TypeVar('_Same') diff --git a/src/ircplom/msg_parse_expectations.py b/src/ircplom/msg_parse_expectations.py index 302044c..c5ba502 100644 --- a/src/ircplom/msg_parse_expectations.py +++ b/src/ircplom/msg_parse_expectations.py @@ -25,14 +25,24 @@ _MsgTokenValidatorDict = dict[_MsgTokenWithStr, Callable[[Any], bool]] class _Command(NamedTuple): verb: str - path: tuple[str, ...] + path: tuple[tuple[bool, str], ...] @classmethod def from_(cls, input_: str) -> Self: - 'Split by first "_" into verb, path (split into steps tuple by ".").' + 'Split by first "_" into verb, path (split into steps tuple).' verb, path_str = input_.split('_', maxsplit=1) - return cls(verb, tuple(step for step in path_str.split('.') - if path_str)) + is_dict_key = False + cur_tok = '' + steps = [] + for c in path_str + '.': + if c in '.[]': + if cur_tok: + steps += [(is_dict_key, cur_tok)] + cur_tok = '' + is_dict_key = c == '[' + continue + cur_tok += c + return cls(verb, tuple(steps)) class _Code(NamedTuple): @@ -162,26 +172,26 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '001', # RPL_WELCOME _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY), bonus_tasks=('do_autojoin:',)), _MsgParseExpectation( '002', # RPL_YOURHOST _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '003', # RPL_CREATED _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '004', # RPL_MYINFO _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, _MsgToken.ANY, _MsgToken.ANY, @@ -191,51 +201,51 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '250', # RPL_STATSDLINE / RPL_STATSCONN _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '251', # RPL_LUSERCLIENT _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '252', # RPL_LUSEROP _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, _MsgToken.ANY)), _MsgParseExpectation( '253', # RPL_LUSERUNKNOWN _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, _MsgToken.ANY)), _MsgParseExpectation( '254', # RPL_LUSERCHANNELS _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, _MsgToken.ANY)), _MsgParseExpectation( '255', # RPL_LUSERME _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '265', # RPL_LOCALUSERS _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '265', # RPL_LOCALUSERS _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, _MsgToken.ANY, _MsgToken.ANY)), @@ -243,12 +253,12 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '266', # RPL_GLOBALUSERS _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), _MsgParseExpectation( '266', # RPL_GLOBALUSERS _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, _MsgToken.ANY, _MsgToken.ANY)), @@ -256,7 +266,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '375', # RPL_MOTDSTART already implied by 1st 372 _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY)), # various login stuff @@ -264,28 +274,28 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '005', # RPL_ISUPPORT _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.LIST, 'do_db.set_isupport_from_rpl:isupport'), _MsgToken.ANY)), # comment _MsgParseExpectation( '372', # RPL_MOTD _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.ANY, 'do_db.motd.append:line'))), _MsgParseExpectation( '376', # RPL_ENDOFMOTD _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY), # comment bonus_tasks=('do_db.motd.complete:',)), _MsgParseExpectation( '396', # RPL_VISIBLEHOST _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), - (_MsgToken.SERVER, 'setattr_db.users.me:host'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), + (_MsgToken.SERVER, 'setattr_db.users[me]:host'), _MsgToken.ANY)), # comment # SASL @@ -293,22 +303,22 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '900', # RPL_LOGGEDIN _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), - (_MsgToken.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), + (_MsgToken.NICK_USER_HOST, 'setattr_db.users[me]:nickuserhost'), (_MsgToken.ANY, 'setattr_db:sasl_account'), _MsgToken.ANY)), # comment _MsgParseExpectation( '903', # RPL_SASLSUCCESS _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.ANY, 'setattr_db:sasl_auth_state')), bonus_tasks=('do_caps.end_negotiation:',)), _MsgParseExpectation( '904', # ERR_SASLFAIL _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.ANY, 'setattr_db:sasl_auth_state')), bonus_tasks=('do_caps.end_negotiation:',)), @@ -323,14 +333,14 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('NEW', ':subverb'), (_MsgToken.LIST, ':items'))), _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('DEL', ':subverb'), (_MsgToken.LIST, ':items'))), @@ -343,7 +353,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('ACK', ':subverb'), (_MsgToken.LIST, ':items'))), @@ -356,7 +366,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('NAK', ':subverb'), (_MsgToken.LIST, ':items'))), @@ -376,13 +386,13 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('LS', ':subverb'), (_MsgToken.LIST, ':items'))), _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('LS', ':subverb'), ('*', ':tbc'), (_MsgToken.LIST, ':items'))), @@ -403,14 +413,14 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('LIST', ':subverb'), ('*', ':tbc'), (_MsgToken.LIST, ':items'))), _MsgParseExpectation( 'CAP', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), ('LIST', ':subverb'), (_MsgToken.LIST, ':items'))), @@ -426,7 +436,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '432', # ERR_ERRONEOUSNICKNAME _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), _MsgToken.ANY, # bad one probably fails our NICKNAME tests _MsgToken.ANY)), # comment @@ -454,46 +464,47 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '332', # RPL_TOPIC _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.CHANNEL, ':CHAN'), - (_MsgToken.ANY, 'setattr_db.channels.CHAN.topic:what'))), + (_MsgToken.ANY, 'setattr_db.channels[CHAN].topic:what'))), _MsgParseExpectation( '333', # RPL_TOPICWHOTIME _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.CHANNEL, ':CHAN'), (_MsgToken.NICK_USER_HOST, - f'{_TOK_SKIPNUH},setattr_db.channels.CHAN.topic:who'), + f'{_TOK_SKIPNUH},setattr_db.channels[CHAN].topic:who'), (_MsgToken.ANY, ':timestamp')), - bonus_tasks=('doafter_db.channels.CHAN.topic.complete:',)), + bonus_tasks=('doafter_db.channels[CHAN].topic.complete:',)), _MsgParseExpectation( '353', # RPL_NAMREPLY _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), '@', (_MsgToken.CHANNEL, ':CHANNEL'), - (_MsgToken.LIST, 'do_db.channels.CHANNEL.add_from_namreply:names'))), + (_MsgToken.LIST, 'do_db.channels[CHANNEL].add_from_namreply:names'))), _MsgParseExpectation( '353', # RPL_NAMREPLY _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), '=', (_MsgToken.CHANNEL, ':CHANNEL'), - (_MsgToken.LIST, 'do_db.channels.CHANNEL.add_from_namreply:names'))), + (_MsgToken.LIST, 'do_db.channels[CHANNEL].add_from_namreply:names'))), _MsgParseExpectation( '366', # RPL_ENDOFNAMES _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.CHANNEL, ':CHAN'), _MsgToken.ANY), # comment - bonus_tasks=('doafter_db.channels.CHAN.user_ids.complete:',)), + bonus_tasks=('doafter_db.channels[CHAN].modes_listy.complete:', + 'doafter_db.channels[CHAN].user_ids.complete:')), _MsgParseExpectation( 'JOIN', - (_MsgToken.NICK_USER_HOST, 'do_db.channels.CHANNEL.join_user:user'), + (_MsgToken.NICK_USER_HOST, 'do_db.channels[CHANNEL].join_user:user'), ((_MsgToken.CHANNEL, ':CHANNEL'),)), _MsgParseExpectation( @@ -513,7 +524,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( '401', # ERR_NOSUCKNICK _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.NICKNAME, ':missing'), _MsgToken.ANY)), # comment @@ -526,7 +537,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'NOTICE', _MsgToken.SERVER, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), (_MsgToken.ANY, ':content')), bonus_tasks=('do_db.notice:content',)), @@ -540,7 +551,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'NOTICE', (_MsgToken.NICK_USER_HOST, ':sender'), - ((_MsgToken.NICKNAME, 'setattr_db.users.me:target'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:target'), (_MsgToken.ANY, ':content')), bonus_tasks=('do_db.notice:content,sender',)), @@ -554,7 +565,7 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'PRIVMSG', (_MsgToken.NICK_USER_HOST, ':sender'), - ((_MsgToken.NICKNAME, 'setattr_db.users.me:target'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:target'), (_MsgToken.ANY, ':content')), bonus_tasks=('do_db.privmsg:content,sender',)), _MsgParseExpectation( @@ -587,34 +598,34 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [ _MsgParseExpectation( 'MODE', - (_MsgToken.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'), - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), - (_MsgToken.ANY, 'setattr_db.users.me:modes'))), + (_MsgToken.NICK_USER_HOST, 'setattr_db.users[me]:nickuserhost'), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), + (_MsgToken.ANY, 'setattr_db.users[me]:modes'))), _MsgParseExpectation( 'MODE', _MsgToken.NICKNAME, - ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'), - (_MsgToken.ANY, 'setattr_db.users.me:modes'))), + ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'), + (_MsgToken.ANY, 'setattr_db.users[me]:modes'))), _MsgParseExpectation( 'MODE', _MsgToken.SERVER, ((_MsgToken.CHANNEL, ':CHANNEL'), - (_MsgToken.ANY, 'do_db.channels.CHANNEL.set_modes:modeset'))), + (_MsgToken.ANY, 'do_db.channels[CHANNEL].set_modes:modeset'))), _MsgParseExpectation( 'MODE', _MsgToken.SERVER, ((_MsgToken.CHANNEL, ':CHANNEL'), (_MsgToken.ANY, ':modeset'), (_MsgToken.ANY, ':args')), - bonus_tasks=('do_db.channels.CHANNEL.set_modes:modeset,args',)), + bonus_tasks=('do_db.channels[CHANNEL].set_modes:modeset,args',)), _MsgParseExpectation( 'TOPIC', - (_MsgToken.NICK_USER_HOST, 'setattr_db.channels.CHAN.topic:who'), + (_MsgToken.NICK_USER_HOST, 'setattr_db.channels[CHAN].topic:who'), ((_MsgToken.CHANNEL, ':CHAN'), - (_MsgToken.ANY, 'setattr_db.channels.CHAN.topic:what')), - bonus_tasks=('doafter_db.channels.CHAN.topic.complete:',)), + (_MsgToken.ANY, 'setattr_db.channels[CHAN].topic:what')), + bonus_tasks=('doafter_db.channels[CHAN].topic.complete:',)), _MsgParseExpectation( 'QUIT', diff --git a/src/tests/channel_modes.test b/src/tests/channel_modes.test index 114fe2a..c29e957 100644 --- a/src/tests/channel_modes.test +++ b/src/tests/channel_modes.test @@ -11,20 +11,6 @@ insert ./lib/user-set-to × servermsg-mode insert servermsglogged [% (MSG)=:foo.bar.baz%MODE%#ch_win3%(ARGS)] -× prefix-log -log 1 $ channels:#ch_win3:modes_listy:(PREFIX) (NEW_IDS) -log 3 $ (NICK) (VERB) (PREFIX) - -× set-prefix -insert servermsg-mode [% (ARGS)=(MODESET)%(NICK)] -insert prefix-log - -× prefix+ -insert set-prefix [(MODESET)=+(CHAR) (VERB)=gains] - -× prefix- -insert set-prefix [(MODESET)=-(CHAR) (VERB)=loses] - × msg-prefixed-long insert servermsglogged [% (MSG)=:(NICK)!~(NICK)(NICK)@(NICK).(NICK)%PRIVMSG%#ch_win3%:(NICK)%(NICK)%(NICK)] insert user-set-to range=1: [(USER_USER)=~(NICK)(NICK) (USER_HOST)=(NICK).(NICK)] @@ -42,14 +28,14 @@ insert isupport-set [(KEY)=CHANMODES (VALUE)=lmn,k,xy,abcABC] # check /join into channel with many other users, with multi-line 353, and membership prefixes insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=3 (RESIDENT_NAMES)=foo%@baz%+oof] insert user-set-to range=:1 [(USER_ID)=1 (USER_NICK)=baz] -log 1 $ channels:#ch_win3:modes_listy:o set to: [1] insert user-set-to range=:1 [(USER_ID)=2 (USER_NICK)=oof] -log 1 $ channels:#ch_win3:modes_listy:v set to: [2] insert servermsglogged [% (MSG)=:foo.bar.baz%353%foo%=%#ch_win3%:+rab%zab] insert user-set-to range=:1 [(USER_ID)=3 (USER_NICK)=rab] -log 1 $ channels:#ch_win3:modes_listy:v set to: [2], [3] insert user-set-to range=:1 [(USER_ID)=4 (USER_NICK)=zab] -insert join-channel-1-end-of-names [% (WIN_ID)=3 (RESIDENT_IDS)=[1],%[2],%[3],%[4],%[me] (RESIDENT_NICKS)=@baz,%+oof,%+rab,%zab,%foo] +insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=3] +log 1 $ channels:#ch_win3:modes_listy:o set to: [1] +log 1 $ channels:#ch_win3:modes_listy:v set to: [2], [3] +insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=3 (RESIDENT_IDS)=[1],%[2],%[3],%[4],%[me] (RESIDENT_NICKS)=@baz,%+oof,%+rab,%zab,%foo] # check (presence/absence of) membership prefixes mirrored in message display insert privmsg-win [% (WIN_ID)=3 (TXT)=foo%foo%foo] @@ -59,19 +45,23 @@ insert msg-prefixed-long [(USER_ID)=3 (NICK)=rab (PREFIX)=+ PRIVMSG=NOTICE [+rab insert msg-prefixed-long [(USER_ID)=4 (NICK)=zab (PREFIX)=] # check server giving and taking membership prefixes -insert prefix- [(CHAR)=o (PREFIX)=o (NICK)=baz (NEW_IDS)=emptied] -insert prefix- [% (CHAR)=v (PREFIX)=v (NICK)=rab (NEW_IDS)=set%to:%[2]] -insert servermsg-mode [% (ARGS)=-v+oo%:oof%zab%baz] -insert prefix-log [% (PREFIX)=v (VERB)=loses (NICK)=oof (NEW_IDS)=emptied] -insert prefix-log [% (PREFIX)=o (VERB)=gains (NICK)=zab (NEW_IDS)=set%to:%[4]] -insert prefix-log [% (PREFIX)=o (VERB)=gains (NICK)=baz (NEW_IDS)=set%to:%[1],%[4]] -insert prefix+ [% (CHAR)=v (PREFIX)=v (NICK)=oof (NEW_IDS)=set%to:%[2]] -insert prefix+ [% (CHAR)=o (PREFIX)=o (NICK)=foo (NEW_IDS)=set%to:%[1],%[4],%[me]] +insert servermsg-mode [% (ARGS)=-o%baz] +log 1 $ channels:#ch_win3:modes_listy:o emptied +log 3 $ baz loses o +insert servermsg-mode [% (ARGS)=-v%rab] +log 1 $ channels:#ch_win3:modes_listy:v set to: [2] +log 3 $ rab loses v +insert servermsg-mode [% (ARGS)=-v+oov%:oof%zab%baz%foo] +log 1 $ channels:#ch_win3:modes_listy:o set to: [1], [4] +log 3 $ baz gains o +log 3 $ zab gains o +log 1 $ channels:#ch_win3:modes_listy:v set to: [me] +log 3 $ foo gains v +log 3 $ oof loses v # re-check membership prefix mirorring in message display after re-distribution -insert privmsg-win [% (WIN_ID)=3 (TXT)=foo%foo%foo [foo]=[@foo]] +insert privmsg-win [% (WIN_ID)=3 (TXT)=foo%foo%foo [foo]=[+foo]] insert msg-prefixed-short [(USER_ID)=1 (NICK)=baz (PREFIX)=@] -insert msg-prefixed-short [(USER_ID)=2 (NICK)=oof (PREFIX)=+] insert msg-prefixed-short [(USER_ID)=3 (NICK)=rab (PREFIX)= PRIVMSG=NOTICE [rab]=(rab)] insert msg-prefixed-short [(USER_ID)=4 (NICK)=zab (PREFIX)=@] @@ -79,7 +69,7 @@ insert msg-prefixed-short [(USER_ID)=4 (NICK)=zab (PREFIX)=@] insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=4 (RESIDENT_NAMES)=foo%baz] insert join-channel-1-end-of-names [% (WIN_ID)=4 (RESIDENT_IDS)=[1],%[me] (RESIDENT_NICKS)=baz,%foo] insert part-no-msg-other [% (USER_ID)=1 (NICK)=baz (REMAINING_IDS)=[2],%[3],%[4],%[me]] -log 1 $ channels:#ch_win3:modes_listy:o set to: [4], [me] +log 1 $ channels:#ch_win3:modes_listy:o set to: [4] insert servermsglogged [% (MSG)=:baz!~bazbaz@baz.baz%JOIN%:#ch_win3] log 1 $ channels:#ch_win3:user_ids set to: [1], [2], [3], [4], [me] log 3 $ baz!~bazbaz@baz.baz joins @@ -103,34 +93,27 @@ log 1 $ channels:#ch_win3:modes_listy:l set to: [foo] insert servermsg-mode [% (ARGS)=+l%:bar] log 1 $ channels:#ch_win3:modes_listy:l set to: [bar], [foo] insert servermsg-mode [% (ARGS)=+l-l%:baz%foo] -log 1 $ channels:#ch_win3:modes_listy:l set to: [bar], [baz], [foo] log 1 $ channels:#ch_win3:modes_listy:l set to: [bar], [baz] # check server setting type-B modes insert servermsg-mode [% (ARGS)=+k%:password] log 1 $ channels:#ch_win3:modes_valued:k set to: [password] insert servermsg-mode [% (ARGS)=-k%:*] -log 1 $ channels:#ch_win3:modes_valued:k deleted +log 1 $ channels:#ch_win3:modes_valued cleared # check server setting type-C modes insert servermsg-mode [% (ARGS)=+x%:foo] log 1 $ channels:#ch_win3:modes_valued:x set to: [foo] -insert servermsg-mode [% (ARGS)=+x%:bar] +insert servermsg-mode [% (ARGS)=+xy%:bar%zab] log 1 $ channels:#ch_win3:modes_valued:x set to: [bar] -insert servermsg-mode [% (ARGS)=+y%:zab] log 1 $ channels:#ch_win3:modes_valued:y set to: [zab] insert servermsg-mode [(ARGS)=-x] log 1 $ channels:#ch_win3:modes_valued:x deleted # check server setting type-D modes insert servermsg-mode [(ARGS)=+aBc] -log 1 $ channels:#ch_win3:modes_toggled set to: [a] -log 1 $ channels:#ch_win3:modes_toggled set to: [Ba] -log 1 $ channels:#ch_win3:modes_toggled set to: [Bac] +log 1 $ channels:#ch_win3:modes_toggled set to: [B], [a], [c] insert servermsg-mode [(ARGS)=-cba] -log 1 $ channels:#ch_win3:modes_toggled set to: [Ba] log 1 $ channels:#ch_win3:modes_toggled set to: [B] insert servermsg-mode [(ARGS)=-B+cb] -log 1 $ channels:#ch_win3:modes_toggled set to: [] -log 1 $ channels:#ch_win3:modes_toggled set to: [c] -log 1 $ channels:#ch_win3:modes_toggled set to: [bc] +log 1 $ channels:#ch_win3:modes_toggled set to: [b], [c] diff --git a/src/tests/channels.test b/src/tests/channels.test index b33fbcb..759e0a9 100644 --- a/src/tests/channels.test +++ b/src/tests/channels.test @@ -117,7 +117,8 @@ insert servermsglogged [% (MSG)=:foo.bar.baz%332%foo%#ch_win4%:foo%bar%baz] insert servermsglogged [% (MSG)=:foo.bar.baz%333%foo%#ch_win4%baz!~bazbaz@OLD.baz.baz%1234567890] insert topic-set-to [% baz.baz=OLD.baz.baz (WIN_ID)=4 (TOPIC)=foo%bar%baz] insert join-channel-0-1-list-residents [% (WIN_ID)=4 (RESIDENT_NAMES)=foo%baz] -insert join-channel-1-end-of-names [% (WIN_ID)=4 (RESIDENT_IDS)=[2],%[me] (RESIDENT_NICKS)=baz,%foo] +insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=4] +insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=4 (RESIDENT_IDS)=[2],%[me] (RESIDENT_NICKS)=baz,%foo] # check _observed_ topic change _does_ affect users database, and … insert servermsglogged [% (MSG)=:baz!~bazbaz@baz.baz%TOPIC%#ch_win4%:foo%bar%baz] diff --git a/src/tests/isupports.test b/src/tests/isupports.test index ea1a5ae..c04d99a 100644 --- a/src/tests/isupports.test +++ b/src/tests/isupports.test @@ -78,18 +78,20 @@ insert join-empty [(WIN_ID)=6 #ch_win=$ch_win] # test effect of PREFIX insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=7 (RESIDENT_NAMES)=foo%@bar%+baz%=quux] insert user-set-to range=:1 [(USER_ID)=1 (USER_NICK)=bar] -log 1 $ channels:#ch_win7:modes_listy:o set to: [1] insert user-set-to range=:1 [(USER_ID)=2 (USER_NICK)=baz] -log 1 $ channels:#ch_win7:modes_listy:v set to: [2] insert user-set-to range=:1 [(USER_ID)=3 (USER_NICK)==quux] -insert join-channel-1-end-of-names [% (WIN_ID)=7 (RESIDENT_IDS)=[1],%[2],%[3],%[me] (RESIDENT_NICKS)=@bar,%+baz,%=quux,%foo] +insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=7] +log 1 $ channels:#ch_win7:modes_listy:o set to: [1] +log 1 $ channels:#ch_win7:modes_listy:v set to: [2] +insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=7 (RESIDENT_IDS)=[1],%[2],%[3],%[me] (RESIDENT_NICKS)=@bar,%+baz,%=quux,%foo] insert isupport-set [(KEY)=PREFIX (VALUE)=(vE)+=] insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=8 (RESIDENT_NAMES)=foo%@bar%+baz%=quux] insert user-set-to range=:1 [(USER_ID)=4 (USER_NICK)=@bar] -log 1 $ channels:#ch_win8:modes_listy:v set to: [2] insert user-set-to range=:1 [(USER_ID)=5 (USER_NICK)=quux] +insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=8] +log 1 $ channels:#ch_win8:modes_listy:v set to: [2] log 1 $ channels:#ch_win8:modes_listy:E set to: [5] -insert join-channel-1-end-of-names [% (WIN_ID)=8 (RESIDENT_IDS)=[2],%[4],%[5],%[me] (RESIDENT_NICKS)=+baz,%@bar,%=quux,%foo] +insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=8 (RESIDENT_IDS)=[2],%[4],%[5],%[me] (RESIDENT_NICKS)=+baz,%@bar,%=quux,%foo] # test effect of USERLEN insert join-empty [(WIN_ID)=9] diff --git a/src/tests/lib/join-channel b/src/tests/lib/join-channel index 6dfdb7b..926e5a7 100644 --- a/src/tests/lib/join-channel +++ b/src/tests/lib/join-channel @@ -14,5 +14,6 @@ insert join-channel-0-1-list-residents × join-channel-1-end-of-names insert servermsglogged [% (MSG)=:foo.bar.baz%366%foo%#ch_win(WIN_ID)%:End%of%/NAMES%list.] +log 1 $ channels:#ch_win(WIN_ID):modes_listy cleared log 1 $ channels:#ch_win(WIN_ID):user_ids set to: (RESIDENT_IDS) log (WIN_ID) $ residents: (RESIDENT_NICKS) diff --git a/src/tests/test.test b/src/tests/test.test index 897a791..47043ff 100644 --- a/src/tests/test.test +++ b/src/tests/test.test @@ -115,8 +115,9 @@ log 3 $ foo1!~foofoo@foo.foo renames foo # join channel with other user insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=4 (RESIDENT_NAMES)=foo%@baz] insert user-set-to range=:1 [(USER_ID)=1 (USER_NICK)=baz] +insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=4] log 1 $ channels:#ch_win4:modes_listy:o set to: [1] -insert join-channel-1-end-of-names [% (WIN_ID)=4 (RESIDENT_IDS)=[1],%[me] (RESIDENT_NICKS)=@baz,%foo] +insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=4 (RESIDENT_IDS)=[1],%[me] (RESIDENT_NICKS)=@baz,%foo] # process non-self channel JOIN insert servermsglogged [% (MSG)=:bar!~barbar@bar.bar%JOIN%:#ch_win4]