├── messages ├── en_US │ └── LC_MESSAGES │ │ ├── graphcore.mo │ │ └── graphcore.po └── graphcore.pot ├── print-command-doc ├── Makefile ├── update-lang.sh ├── src ├── utils.h ├── main.h ├── corecommands.h ├── clibase.h ├── mmap_pool.h └── digraph.h ├── proj └── graphcore.cbp ├── README.rst ├── test ├── graphcore.tb └── talkback.py ├── spec.rst └── LICENSE /messages/en_US/LC_MESSAGES/graphcore.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmde/graphcore/master/messages/en_US/LC_MESSAGES/graphcore.mo -------------------------------------------------------------------------------- /print-command-doc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # sed -n 's|^[ \t]*//c:[ ]*||p' src/main.cpp 4 | 5 | 6 | # (for i in $((echo help | ./graphcore | grep ^#) | (while read line; do echo $line | cut -d ' ' -f 2; done)); do echo help $i | ./graphcore ; done) | grep ^# | sed 's/^# //' 7 | 8 | COMMANDS=$((echo help | ./graphcore | grep ^#) | (while read line; do echo $line | cut -d ' ' -f 2; done)) 9 | 10 | echo; echo; echo "commands:"; echo; echo; 11 | for i in $COMMANDS; do 12 | echo -n " - $i"; echo _; 13 | done 14 | echo 15 | for i in $COMMANDS; do 16 | echo ".. _$i:" ; 17 | echo; 18 | echo $i ; 19 | echo; echo; 20 | echo '::'; 21 | echo; echo; 22 | echo help $i | ./graphcore | grep ^# | sed 's/^# /\t/' | 23 | (read firstline; echo " syntax: $firstline"; while read line; do echo " $line"; done); 24 | echo; 25 | done 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CCFLAGS=-Wall -Wstrict-overflow=3 -Wno-unused-variable -std=c++0x -ggdb -DSYSTEMPAGESIZE=$(shell getconf PAGESIZE) 2 | LDFLAGS=-lpthread -lreadline 3 | 4 | ifeq ($(STDERR_DEBUGGING),1) 5 | CCFLAGS+=-DSTDERR_DEBUGGING 6 | endif 7 | 8 | ifeq ($(USE_MMAP_POOL),1) 9 | CCFLAGS+=-DUSE_MMAP_POOL 10 | endif 11 | 12 | ifeq ($(DEBUG_COMMANDS),1) 13 | CCFLAGS+=-DDEBUG_COMMANDS 14 | endif 15 | 16 | all: Release Debug 17 | 18 | Release: graphcore 19 | Debug: graphcore.dbg 20 | 21 | graphcore: src/main.cpp src/*.h 22 | g++ $(CCFLAGS) -O3 -march=native src/main.cpp $(LDFLAGS) -ographcore 23 | 24 | graphcore.dbg: src/main.cpp src/*.h 25 | g++ $(CCFLAGS) -O0 -DDEBUG_COMMANDS -ggdb src/main.cpp $(LDFLAGS) -ographcore.dbg 26 | 27 | # updatelang: update the language files 28 | # running this will generate changes in the repository 29 | updatelang: # 30 | ./update-lang.sh 31 | 32 | test: Debug 33 | python test/talkback.py test/graphcore.tb ./graphcore.dbg 34 | -------------------------------------------------------------------------------- /update-lang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # this script: 3 | # - updates the translation template file $MSGDIR/$DOMAIN.pot 4 | # - updates .po files with new translatable strings in source code 5 | # - compiles .po files to .mo files 6 | 7 | # directory where localization files are placed 8 | MSGDIR=messages 9 | 10 | # textdomain 11 | DOMAIN=graphcore 12 | 13 | # find languages 14 | LANGUAGES=$(cd messages; find -mindepth 1 -maxdepth 1 -type d -execdir basename '{}' ';') 15 | 16 | SRC=$(find src -name '*.cpp'; find src -name '*.h') 17 | 18 | TMPPOT=$(mktemp) 19 | xgettext -d graphcore $SRC --keyword=_ -o - | sed "s/CHARSET/UTF-8/" > $TMPPOT && 20 | echo -n "merging new strings into template file $MSGDIR/$DOMAIN.pot " && 21 | msgmerge -U $MSGDIR/$DOMAIN.pot $TMPPOT && 22 | rm $TMPPOT && 23 | 24 | for LANG in $LANGUAGES; do 25 | echo -n "merging new strings into $LANG " && 26 | msgmerge -U $MSGDIR/$LANG/LC_MESSAGES/$DOMAIN.po $MSGDIR/$DOMAIN.pot && 27 | echo generating binary message catalog for $LANG && 28 | msgfmt -c -v -o $MSGDIR/$LANG/LC_MESSAGES/$DOMAIN.mo $MSGDIR/$LANG/LC_MESSAGES/$DOMAIN.po 29 | done 30 | 31 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | #ifdef STDERR_DEBUGGING 9 | #define dmsg(fmt, params...) dprint("%s " fmt, __func__, params) 10 | #else 11 | #define dmsg(x...) 12 | #endif 13 | 14 | class ScopedLock // XXX todo: might use std::lock_guard where supported 15 | { 16 | ScopedLock(const ScopedLock&); 17 | void operator= (const ScopedLock&); 18 | std::mutex& m; 19 | public: 20 | ScopedLock(std::mutex& m_): m(m_) 21 | { 22 | m.lock(); 23 | } 24 | ~ScopedLock() 25 | { 26 | m.unlock(); 27 | } 28 | }; 29 | 30 | #ifdef __linux__ 31 | inline std::deque 32 | parse_proc_self(const char *fname) 33 | { 34 | std::deque ret; 35 | std::ifstream f(fname); 36 | while(f.good()) 37 | { 38 | std::string col; 39 | f>>col; 40 | if(!f.good()) break; 41 | ret.push_back(col); 42 | } 43 | return ret; 44 | } 45 | 46 | inline long getRSSBytes() 47 | { 48 | return std::stol(parse_proc_self("/proc/self/stat")[23]) * SYSTEMPAGESIZE; 49 | } 50 | 51 | inline long getVirtBytes() 52 | { 53 | return std::stol(parse_proc_self("/proc/self/stat")[22]); 54 | } 55 | 56 | #else // non-linux 57 | 58 | inline long getRSSBytes() 59 | { 60 | return -1; 61 | } 62 | 63 | inline long getVirtBytes() 64 | { 65 | return -1; 66 | } 67 | 68 | #endif 69 | 70 | #endif //UTILS_H 71 | -------------------------------------------------------------------------------- /proj/graphcore.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 53 | 54 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | // Graph Processor core. 2 | // (c) Wikimedia Deutschland, written by Johannes Kroll in 2011, 2012 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #ifndef MAIN_H 18 | #define MAIN_H 19 | 20 | // print debugging and other info to stderr. enable with 'make STDERR_DEBUGGING=1'. 21 | #ifdef STDERR_DEBUGGING 22 | void dodprint(const char *fmt, ...); 23 | #define dprint dodprint 24 | #else 25 | #define dprint(x...) 26 | #endif 27 | 28 | #define U32MAX (0xFFFFFFFF) 29 | 30 | // calculate average successors/predecessors per node in the stats command. 31 | // this takes a little long, so it's disabled. 32 | // #define STATS_AVGNEIGHBORS 33 | 34 | // in add-arcs, use mark+remove to remove duplicates from data set instead of erase(). fast. 35 | #define DUPCHECK_MARKRM 36 | 37 | // in add-arcs, move duplicates upwards, then erase. slow, don't use. 38 | //#define DUPCHECK_MOVERM 39 | 40 | // in remove-arcs, use mark+remove instead of container's erase() method. 41 | //#define REMOVEARCS_MARKRM 42 | 43 | // use inplace_erase 44 | #define REMOVEARCS_INPLACE 45 | 46 | // in replace-*, use mark+remove instead of container's erase() method. 47 | #define REPLACENEIGHBORS_MARKRM 48 | 49 | typedef unordered_map MetaMap; 50 | 51 | double getTime(); 52 | 53 | #define DebugBreak() ({ __asm__ volatile ("int3"); }) 54 | 55 | #endif //MAIN_H 56 | -------------------------------------------------------------------------------- /src/corecommands.h: -------------------------------------------------------------------------------- 1 | // corecommands.h: shared definitions for graphcore commands. 2 | // (c) Wikimedia Deutschland, written by Johannes Kroll in 2011, 2012 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #ifndef CORECOMMANDS_H 18 | #define CORECOMMANDS_H 19 | 20 | #if !defined(CORECOMMANDS_BEGIN) || !defined(CORECOMMANDS_END) || !defined(CORECOMMAND) 21 | #error "You're doing it wrong." 22 | #endif 23 | 24 | CORECOMMANDS_BEGIN 25 | CORECOMMAND("help", ACCESS_READ, ccHelp(this)); 26 | CORECOMMAND("add-arcs", ACCESS_WRITE, ccAddArcs()); 27 | CORECOMMAND("remove-arcs", ACCESS_WRITE, ccRemoveArcs()); 28 | CORECOMMAND("replace-predecessors", ACCESS_WRITE, ccReplaceNeighbors()); 29 | CORECOMMAND("replace-successors", ACCESS_WRITE, ccReplaceNeighbors()); 30 | CORECOMMAND("traverse-predecessors",ACCESS_READ, ccListNeighbors()); 31 | CORECOMMAND("traverse-successors", ACCESS_READ, ccListNeighbors()); 32 | CORECOMMAND("traverse-neighbors", ACCESS_READ, ccListNeighbors()); 33 | CORECOMMAND("traverse-predecessors-withdepth",ACCESS_READ, ccListNeighbors_WithDepth()); 34 | CORECOMMAND("traverse-successors-withdepth", ACCESS_READ, ccListNeighbors_WithDepth()); 35 | CORECOMMAND("traverse-neighbors-withdepth", ACCESS_READ, ccListNeighbors_WithDepth()); 36 | CORECOMMAND("list-predecessors", ACCESS_READ, ccListNeighbors()); 37 | CORECOMMAND("list-successors", ACCESS_READ, ccListNeighbors()); 38 | CORECOMMAND("find-path", ACCESS_READ, ccFindPath()); 39 | CORECOMMAND("find-root", ACCESS_READ, ccFindPath()); 40 | CORECOMMAND("list-roots", ACCESS_READ, ccListNeighborless()); 41 | CORECOMMAND("list-leaves", ACCESS_READ, ccListNeighborless()); 42 | CORECOMMAND("stats", ACCESS_READ, ccStats()); 43 | 44 | #ifdef DEBUG_COMMANDS 45 | CORECOMMAND("list-by-tail", ACCESS_READ, ccListArcs()); 46 | CORECOMMAND("list-by-head", ACCESS_READ, ccListArcs()); 47 | CORECOMMAND("add-stuff", ACCESS_WRITE, ccAddStuff()); 48 | CORECOMMAND("rm-stuff", ACCESS_WRITE, ccRMStuff()); 49 | CORECOMMAND("malloc-stats", ACCESS_READ, ccMallocStats()); 50 | #endif 51 | 52 | CORECOMMAND("clear", ACCESS_WRITE, ccClear()); 53 | CORECOMMAND("shutdown", ACCESS_ADMIN, ccShutdown()); 54 | CORECOMMAND("quit", ACCESS_ADMIN, ccShutdown()); 55 | 56 | CORECOMMAND("protocol-version", ACCESS_ADMIN, ccProtocolVersion()); 57 | 58 | CORECOMMAND("set-meta", ACCESS_WRITE, ccSetMeta()); 59 | CORECOMMAND("get-meta", ACCESS_READ, ccGetMeta()); 60 | CORECOMMAND("remove-meta", ACCESS_WRITE, ccRemoveMeta()); 61 | CORECOMMAND("list-meta", ACCESS_READ, ccListMeta()); 62 | 63 | CORECOMMAND("dump-graph", ACCESS_ADMIN, ccDumpGraph()); 64 | CORECOMMAND("load-graph", ACCESS_ADMIN, ccLoadGraph()); 65 | 66 | CORECOMMAND("find-cycles", ACCESS_READ, ccFindCycles()); 67 | CORECOMMANDS_END 68 | 69 | 70 | #endif // CORECOMMANDS_H 71 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | GraphCore 2 | ============================================ 3 | 4 | Issue tracker 5 | ============= 6 | Please file bugs and feature requests on `Phabricator `_. 7 | 8 | Documentation 9 | ============= 10 | 11 | commands: 12 | 13 | 14 | - help_ 15 | - add-arcs_ 16 | - remove-arcs_ 17 | - replace-predecessors_ 18 | - replace-successors_ 19 | - traverse-predecessors_ 20 | - traverse-successors_ 21 | - traverse-neighbors_ 22 | - traverse-predecessors-withdepth_ 23 | - traverse-successors-withdepth_ 24 | - traverse-neighbors-withdepth_ 25 | - list-predecessors_ 26 | - list-successors_ 27 | - find-path_ 28 | - find-root_ 29 | - list-roots_ 30 | - list-leaves_ 31 | - stats_ 32 | - clear_ 33 | - shutdown_ 34 | - quit_ 35 | - protocol-version_ 36 | - set-meta_ 37 | - get-meta_ 38 | - remove-meta_ 39 | - list-meta_ 40 | - dump-graph_ 41 | - load-graph_ 42 | - find-cycles_ 43 | 44 | .. _help: 45 | 46 | help 47 | 48 | 49 | :: 50 | 51 | 52 | syntax: help [COMMAND] / help operators 53 | help: list commands 54 | help COMMAND: get help on COMMAND 55 | help operators: print help on operators 56 | 57 | .. _add-arcs: 58 | 59 | add-arcs 60 | 61 | 62 | :: 63 | 64 | 65 | syntax: add-arcs {:|<} 66 | read a data set of arcs and add them to the graph. empty line terminates the set. 67 | 68 | .. _remove-arcs: 69 | 70 | remove-arcs 71 | 72 | 73 | :: 74 | 75 | 76 | syntax: remove-arcs {:|<} 77 | read a data set of arcs and remove them from the graph. empty line terminates the set. 78 | 79 | .. _replace-predecessors: 80 | 81 | replace-predecessors 82 | 83 | 84 | :: 85 | 86 | 87 | syntax: replace-predecessors NODE {:|<} 88 | read data set of nodes and replace predecessors of NODE with given set. 89 | 90 | .. _replace-successors: 91 | 92 | replace-successors 93 | 94 | 95 | :: 96 | 97 | 98 | syntax: replace-successors NODE {:|<} 99 | read data set of nodes and replace successors of NODE with given set. 100 | 101 | .. _traverse-predecessors: 102 | 103 | traverse-predecessors 104 | 105 | 106 | :: 107 | 108 | 109 | syntax: traverse-predecessors NODE DEPTH [MAXRESULTS] 110 | list NODE and its predecessors recursively up to DEPTH. 111 | 112 | .. _traverse-successors: 113 | 114 | traverse-successors 115 | 116 | 117 | :: 118 | 119 | 120 | syntax: traverse-successors NODE DEPTH [MAXRESULTS] 121 | list NODE and its successors recursively up to DEPTH. 122 | 123 | .. _traverse-neighbors: 124 | 125 | traverse-neighbors 126 | 127 | 128 | :: 129 | 130 | 131 | syntax: traverse-neighbors NODE DEPTH [MAXRESULTS] 132 | list NODE and its neighbors recursively up to DEPTH. 133 | 134 | .. _traverse-predecessors-withdepth: 135 | 136 | traverse-predecessors-withdepth 137 | 138 | 139 | :: 140 | 141 | 142 | syntax: traverse-predecessors-withdepth NODE DEPTH 143 | list NODE and its predecessors recursively up to DEPTH. 144 | 145 | .. _traverse-successors-withdepth: 146 | 147 | traverse-successors-withdepth 148 | 149 | 150 | :: 151 | 152 | 153 | syntax: traverse-successors-withdepth NODE DEPTH 154 | list NODE and its successors recursively up to DEPTH. 155 | 156 | .. _traverse-neighbors-withdepth: 157 | 158 | traverse-neighbors-withdepth 159 | 160 | 161 | :: 162 | 163 | 164 | syntax: traverse-neighbors-withdepth NODE DEPTH 165 | list NODE and its neighbors recursively up to DEPTH. 166 | 167 | .. _list-predecessors: 168 | 169 | list-predecessors 170 | 171 | 172 | :: 173 | 174 | 175 | syntax: list-predecessors NODE [MAXRESULTS] 176 | list direct predecessors of NODE. 177 | 178 | .. _list-successors: 179 | 180 | list-successors 181 | 182 | 183 | :: 184 | 185 | 186 | syntax: list-successors NODE [MAXRESULTS] 187 | list direct successors of NODE. 188 | 189 | .. _find-path: 190 | 191 | find-path 192 | 193 | 194 | :: 195 | 196 | 197 | syntax: find-path X Y 198 | find the shortest path from node X to node Y. return data set of arcs representing the path. 199 | 200 | .. _find-root: 201 | 202 | find-root 203 | 204 | 205 | :: 206 | 207 | 208 | syntax: find-root X 209 | find the path from X to nearest root node. return data set of arcs representing the path. 210 | 211 | .. _list-roots: 212 | 213 | list-roots 214 | 215 | 216 | :: 217 | 218 | 219 | syntax: list-roots 220 | list root nodes (nodes without predecessors). 221 | 222 | .. _list-leaves: 223 | 224 | list-leaves 225 | 226 | 227 | :: 228 | 229 | 230 | syntax: list-leaves 231 | list leaf nodes (nodes without successors). 232 | 233 | .. _stats: 234 | 235 | stats 236 | 237 | 238 | :: 239 | 240 | 241 | syntax: stats 242 | print some statistics about the graph in the form of a name,value data set. 243 | when called as 'stats q', returns only values which can be gathered quickly (eg no avg neighbor count). 244 | ArcCount number of arcs 245 | AvgPredecessors average predecessors per node 246 | AvgSuccessors average successors per node 247 | DataInvalid nonzero if any obvious errors were found in graph data 248 | MaxNodeID greatest node ID 249 | MinNodeID lowest node ID 250 | NumDups number of duplicates found (must be zero) 251 | ProcRSS process resident set size in bytes 252 | ProcVirt process virt size in bytes 253 | 254 | .. _clear: 255 | 256 | clear 257 | 258 | 259 | :: 260 | 261 | 262 | syntax: clear 263 | clear the graph model. 264 | 265 | .. _shutdown: 266 | 267 | shutdown 268 | 269 | 270 | :: 271 | 272 | 273 | syntax: shutdown 274 | shutdown graphcore. 275 | 276 | .. _quit: 277 | 278 | quit 279 | 280 | 281 | :: 282 | 283 | 284 | syntax: quit 285 | shutdown graphcore. 286 | 287 | .. _protocol-version: 288 | 289 | protocol-version 290 | 291 | 292 | :: 293 | 294 | 295 | syntax: protocol-version 296 | print PROTOCOL_VERSION. for internal use only. 297 | 298 | .. _set-meta: 299 | 300 | set-meta 301 | 302 | 303 | :: 304 | 305 | 306 | syntax: set-meta NAME VALUE 307 | add or set an arbitrary text variable. 308 | variable names may contain alphabetic characters (a-z A-Z), digits (0-9), hyphens (-) and underscores (_), 309 | and must start with an alphabetic character, a hyphen or an underscore. 310 | 311 | .. _get-meta: 312 | 313 | get-meta 314 | 315 | 316 | :: 317 | 318 | 319 | syntax: get-meta NAME 320 | read a named text variable. 321 | 322 | .. _remove-meta: 323 | 324 | remove-meta 325 | 326 | 327 | :: 328 | 329 | 330 | syntax: remove-meta NAME 331 | remove the named variable. 332 | 333 | .. _list-meta: 334 | 335 | list-meta 336 | 337 | 338 | :: 339 | 340 | 341 | syntax: list-meta 342 | list all variables in this graph. 343 | 344 | .. _dump-graph: 345 | 346 | dump-graph 347 | 348 | 349 | :: 350 | 351 | 352 | syntax: dump-graph FILENAME 353 | save the graph to a file. 354 | 355 | .. _load-graph: 356 | 357 | load-graph 358 | 359 | 360 | :: 361 | 362 | 363 | syntax: load-graph FILENAME 364 | load graph from a dump file. 365 | 366 | .. _find-cycles: 367 | 368 | find-cycles 369 | 370 | 371 | :: 372 | 373 | 374 | syntax: find-cycles NODE DEPTH 375 | find cycles in subgraph by traversing successors of NODE with max depth DEPTH. 376 | cycle paths are separated by arcs with invalid node IDs: 4294967295,4294967295. 377 | 378 | -------------------------------------------------------------------------------- /messages/graphcore.pot: -------------------------------------------------------------------------------- 1 | # Message template file for the graphcore program. 2 | # Copyright (C) Wikimedia Deutschland, created by Johannes Kroll in 2011 3 | # This file is distributed under the same license as the graphcore package. 4 | # Johannes Kroll , 2011. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: graphcore\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2011-03-22 14:53+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: src/main.cpp:312 20 | msgid "number of arcs" 21 | msgstr "" 22 | 23 | #: src/main.cpp:313 24 | msgid "total RAM consumed by arc data, in KiB" 25 | msgstr "" 26 | 27 | #: src/main.cpp:344 28 | msgid "average predecessors per node" 29 | msgstr "" 30 | 31 | #: src/main.cpp:349 32 | msgid "average successors per node" 33 | msgstr "" 34 | 35 | #: src/main.cpp:352 36 | msgid "lowest node ID" 37 | msgstr "" 38 | 39 | #: src/main.cpp:353 40 | msgid "greatest node ID" 41 | msgstr "" 42 | 43 | #: src/main.cpp:354 44 | msgid "number of duplicates found (must be zero)" 45 | msgstr "" 46 | 47 | #: src/main.cpp:355 48 | msgid "nonzero if any obvious errors were found in graph data" 49 | msgstr "" 50 | 51 | #: src/main.cpp:635 52 | msgid " Syntax: " 53 | msgstr "" 54 | 55 | #: src/main.cpp:730 56 | msgid "couldn't open output file\n" 57 | msgstr "" 58 | 59 | #: src/main.cpp:740 60 | msgid "couldn't open input file\n" 61 | msgstr "" 62 | 63 | #: src/main.cpp:860 64 | msgid "operators not available for this return type." 65 | msgstr "" 66 | 67 | #: src/main.cpp:877 68 | msgid "no such command" 69 | msgstr "" 70 | 71 | #: src/main.cpp:879 72 | msgid "return type mismatch" 73 | msgstr "" 74 | 75 | #: src/main.cpp:952 76 | msgid "output redirection not possible for this command.\n" 77 | msgstr "" 78 | 79 | #: src/main.cpp:966 80 | msgid "no such command." 81 | msgstr "" 82 | 83 | #: src/main.cpp:1011 src/main.cpp:1020 84 | #, c-format 85 | msgid "error reading data set (line %u)\n" 86 | msgstr "" 87 | 88 | #: src/main.cpp:1025 89 | #, c-format 90 | msgid "invalid node ID in line %d\n" 91 | msgstr "" 92 | 93 | #: src/main.cpp:1046 src/main.cpp:1333 94 | msgid " NODE" 95 | msgstr "" 96 | 97 | #: src/main.cpp:1046 98 | msgid " DEPTH" 99 | msgstr "" 100 | 101 | #: src/main.cpp:1051 102 | msgid "list NODE and its neighbors recursively up to DEPTH." 103 | msgstr "" 104 | 105 | #: src/main.cpp:1052 106 | msgid "list NODE and its predecessors recursively up to DEPTH." 107 | msgstr "" 108 | 109 | #: src/main.cpp:1053 110 | msgid "list NODE and its successors recursively up to DEPTH." 111 | msgstr "" 112 | 113 | #: src/main.cpp:1057 114 | msgid "list direct neighbors of NODE." 115 | msgstr "" 116 | 117 | #: src/main.cpp:1058 118 | msgid "list direct predecessors of NODE." 119 | msgstr "" 120 | 121 | #: src/main.cpp:1059 122 | msgid "list direct successors of NODE." 123 | msgstr "" 124 | 125 | #: src/main.cpp:1086 126 | msgid "Node not found.\n" 127 | msgstr "" 128 | 129 | #: src/main.cpp:1091 130 | #, c-format 131 | msgid "%zu nodes, %fs%s\n" 132 | msgstr "" 133 | 134 | #: src/main.cpp:1116 135 | msgid "list leaf nodes (nodes without successors)." 136 | msgstr "" 137 | 138 | #: src/main.cpp:1118 139 | msgid "list root nodes (nodes without predecessors)." 140 | msgstr "" 141 | 142 | #: src/main.cpp:1131 143 | msgid "leaf nodes" 144 | msgstr "" 145 | 146 | #: src/main.cpp:1131 147 | msgid "root nodes" 148 | msgstr "" 149 | 150 | #: src/main.cpp:1151 151 | msgid " [COMMAND] / " 152 | msgstr "" 153 | 154 | #: src/main.cpp:1151 155 | msgid " operators" 156 | msgstr "" 157 | 158 | #: src/main.cpp:1152 159 | msgid "" 160 | "help: list commands\n" 161 | "# help COMMAND: get help on COMMAND\n" 162 | "# help operators: print help on operators" 163 | msgstr "" 164 | 165 | #: src/main.cpp:1166 166 | msgid "" 167 | "# Operators can be used to combine the output of two commands into one\n" 168 | "# data-set. They are used with infox syntax:\n" 169 | "# \n" 170 | "# \n" 171 | "# \n" 172 | "# This way, a composite command is formed. Note that if either operant\n" 173 | "# fails, the composite command also fails.\n" 174 | "# \n" 175 | "# The following operators are currently specified:\n" 176 | "# \n" 177 | "# intersection (&&):\n" 178 | "# The intersection operator takes two operants, both of wich must\n" 179 | "# return a set of nodes. The result of the composite command is a set of\n" 180 | "# nodes that contains only the nodes that are in both, the result of the\n" 181 | "# left operand, and the result of the right. If and only if either\n" 182 | "# operant returns NONE, the result is NONE. \n" 183 | "# \n" 184 | "# subtraction (&&!):\n" 185 | "# The subtraction operator takes two operants, both of\n" 186 | "# which must return a set of nodes. The result of the composite command is\n" 187 | "# a set of nodes that contains only the nodes that are in the result of\n" 188 | "# the left operand but not inthe result of the right operant. If and only\n" 189 | "# if the left operant returns NONE, the result is NONE. If the right\n" 190 | "# operant returns NONE, the result is the result of the left operant.\n" 191 | "\n" 192 | msgstr "" 193 | 194 | #: src/main.cpp:1196 195 | #, c-format 196 | msgid "%s: no such command.\n" 197 | msgstr "" 198 | 199 | #: src/main.cpp:1205 200 | #, c-format 201 | msgid "available commands%s\n" 202 | msgstr "" 203 | 204 | #: src/main.cpp:1227 205 | msgid "" 206 | "print some statistics about the graph in the form of a name,value data set.\n" 207 | msgstr "" 208 | 209 | #: src/main.cpp:1228 210 | msgid "names and their meanings:" 211 | msgstr "" 212 | 213 | #: src/main.cpp:1264 214 | msgid "" 215 | "read a data set of arcs and add them to the graph. empty line terminates the " 216 | "set." 217 | msgstr "" 218 | 219 | #: src/main.cpp:1296 220 | msgid "" 221 | "read a data set of arcs and remove them from the graph. empty line " 222 | "terminates the set." 223 | msgstr "" 224 | 225 | #: src/main.cpp:1338 226 | msgid "read data set of nodes and replace predecessors of NODE with given set." 227 | msgstr "" 228 | 229 | #: src/main.cpp:1339 230 | msgid "read data set of nodes and replace successors of NODE with given set." 231 | msgstr "" 232 | 233 | #: src/main.cpp:1369 234 | msgid "internal error: Digraph::replaceNeighbors() failed.\n" 235 | msgstr "" 236 | 237 | #: src/main.cpp:1384 238 | msgid "clear the graph model." 239 | msgstr "" 240 | 241 | #: src/main.cpp:1414 242 | msgid "shutdown the graph processor." 243 | msgstr "" 244 | 245 | #: src/main.cpp:1423 246 | #, c-format 247 | msgid "shutting down pid %d.\n" 248 | msgstr "" 249 | 250 | #: src/main.cpp:1447 251 | msgid "" 252 | "find the path from X to nearest root node. return data set of arcs " 253 | "representing the path." 254 | msgstr "" 255 | 256 | #: src/main.cpp:1449 257 | msgid "" 258 | "find the shortest path from node X to node Y. return data set of arcs " 259 | "representing the path." 260 | msgstr "" 261 | 262 | #: src/main.cpp:1482 263 | #, c-format 264 | msgid "%zu nodes visited, path length %zu%s\n" 265 | msgstr "" 266 | 267 | #: src/main.cpp:1502 268 | msgid "debugging: list N arcs starting from INDEX, " 269 | msgstr "" 270 | -------------------------------------------------------------------------------- /messages/en_US/LC_MESSAGES/graphcore.po: -------------------------------------------------------------------------------- 1 | # Message template file for the graphcore program. 2 | # Copyright (C) Wikimedia Deutschland, created by Johannes Kroll in 2011 3 | # This file is distributed under the same license as the graphcore package. 4 | # Johannes Kroll , 2011. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: graphcore\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2011-03-22 14:53+0100\n" 11 | "PO-Revision-Date: 2011-02-28 06:20+0100\n" 12 | "Last-Translator: Your name \n" 13 | "Language-Team: English\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | #: src/main.cpp:312 20 | msgid "number of arcs" 21 | msgstr "" 22 | 23 | #: src/main.cpp:313 24 | msgid "total RAM consumed by arc data, in KiB" 25 | msgstr "" 26 | 27 | #: src/main.cpp:344 28 | msgid "average predecessors per node" 29 | msgstr "" 30 | 31 | #: src/main.cpp:349 32 | msgid "average successors per node" 33 | msgstr "" 34 | 35 | #: src/main.cpp:352 36 | msgid "lowest node ID" 37 | msgstr "" 38 | 39 | #: src/main.cpp:353 40 | msgid "greatest node ID" 41 | msgstr "" 42 | 43 | #: src/main.cpp:354 44 | msgid "number of duplicates found (must be zero)" 45 | msgstr "" 46 | 47 | #: src/main.cpp:355 48 | msgid "nonzero if any obvious errors were found in graph data" 49 | msgstr "" 50 | 51 | #: src/main.cpp:635 52 | msgid " Syntax: " 53 | msgstr "" 54 | 55 | #: src/main.cpp:730 56 | msgid "couldn't open output file\n" 57 | msgstr "" 58 | 59 | #: src/main.cpp:740 60 | msgid "couldn't open input file\n" 61 | msgstr "" 62 | 63 | #: src/main.cpp:860 64 | msgid "operators not available for this return type." 65 | msgstr "" 66 | 67 | #: src/main.cpp:877 68 | msgid "no such command" 69 | msgstr "" 70 | 71 | #: src/main.cpp:879 72 | msgid "return type mismatch" 73 | msgstr "" 74 | 75 | #: src/main.cpp:952 76 | msgid "output redirection not possible for this command.\n" 77 | msgstr "" 78 | 79 | #: src/main.cpp:966 80 | msgid "no such command." 81 | msgstr "" 82 | 83 | #: src/main.cpp:1011 src/main.cpp:1020 84 | #, c-format 85 | msgid "error reading data set (line %u)\n" 86 | msgstr "" 87 | 88 | #: src/main.cpp:1025 89 | #, c-format 90 | msgid "invalid node ID in line %d\n" 91 | msgstr "" 92 | 93 | #: src/main.cpp:1046 src/main.cpp:1333 94 | msgid " NODE" 95 | msgstr "" 96 | 97 | #: src/main.cpp:1046 98 | msgid " DEPTH" 99 | msgstr "" 100 | 101 | #: src/main.cpp:1051 102 | msgid "list NODE and its neighbors recursively up to DEPTH." 103 | msgstr "" 104 | 105 | #: src/main.cpp:1052 106 | msgid "list NODE and its predecessors recursively up to DEPTH." 107 | msgstr "" 108 | 109 | #: src/main.cpp:1053 110 | msgid "list NODE and its successors recursively up to DEPTH." 111 | msgstr "" 112 | 113 | #: src/main.cpp:1057 114 | msgid "list direct neighbors of NODE." 115 | msgstr "" 116 | 117 | #: src/main.cpp:1058 118 | msgid "list direct predecessors of NODE." 119 | msgstr "" 120 | 121 | #: src/main.cpp:1059 122 | msgid "list direct successors of NODE." 123 | msgstr "" 124 | 125 | #: src/main.cpp:1086 126 | msgid "Node not found.\n" 127 | msgstr "" 128 | 129 | #: src/main.cpp:1091 130 | #, c-format 131 | msgid "%zu nodes, %fs%s\n" 132 | msgstr "" 133 | 134 | #: src/main.cpp:1116 135 | msgid "list leaf nodes (nodes without successors)." 136 | msgstr "" 137 | 138 | #: src/main.cpp:1118 139 | msgid "list root nodes (nodes without predecessors)." 140 | msgstr "" 141 | 142 | #: src/main.cpp:1131 143 | msgid "leaf nodes" 144 | msgstr "" 145 | 146 | #: src/main.cpp:1131 147 | msgid "root nodes" 148 | msgstr "" 149 | 150 | #: src/main.cpp:1151 151 | msgid " [COMMAND] / " 152 | msgstr "" 153 | 154 | #: src/main.cpp:1151 155 | msgid " operators" 156 | msgstr "" 157 | 158 | #: src/main.cpp:1152 159 | msgid "" 160 | "help: list commands\n" 161 | "# help COMMAND: get help on COMMAND\n" 162 | "# help operators: print help on operators" 163 | msgstr "" 164 | 165 | #: src/main.cpp:1166 166 | msgid "" 167 | "# Operators can be used to combine the output of two commands into one\n" 168 | "# data-set. They are used with infox syntax:\n" 169 | "# \n" 170 | "# \n" 171 | "# \n" 172 | "# This way, a composite command is formed. Note that if either operant\n" 173 | "# fails, the composite command also fails.\n" 174 | "# \n" 175 | "# The following operators are currently specified:\n" 176 | "# \n" 177 | "# intersection (&&):\n" 178 | "# The intersection operator takes two operants, both of wich must\n" 179 | "# return a set of nodes. The result of the composite command is a set of\n" 180 | "# nodes that contains only the nodes that are in both, the result of the\n" 181 | "# left operand, and the result of the right. If and only if either\n" 182 | "# operant returns NONE, the result is NONE. \n" 183 | "# \n" 184 | "# subtraction (&&!):\n" 185 | "# The subtraction operator takes two operants, both of\n" 186 | "# which must return a set of nodes. The result of the composite command is\n" 187 | "# a set of nodes that contains only the nodes that are in the result of\n" 188 | "# the left operand but not inthe result of the right operant. If and only\n" 189 | "# if the left operant returns NONE, the result is NONE. If the right\n" 190 | "# operant returns NONE, the result is the result of the left operant.\n" 191 | "\n" 192 | msgstr "" 193 | 194 | #: src/main.cpp:1196 195 | #, c-format 196 | msgid "%s: no such command.\n" 197 | msgstr "" 198 | 199 | #: src/main.cpp:1205 200 | #, c-format 201 | msgid "available commands%s\n" 202 | msgstr "" 203 | 204 | #: src/main.cpp:1227 205 | msgid "" 206 | "print some statistics about the graph in the form of a name,value data set.\n" 207 | msgstr "" 208 | 209 | #: src/main.cpp:1228 210 | msgid "names and their meanings:" 211 | msgstr "" 212 | 213 | #: src/main.cpp:1264 214 | msgid "" 215 | "read a data set of arcs and add them to the graph. empty line terminates the " 216 | "set." 217 | msgstr "" 218 | 219 | #: src/main.cpp:1296 220 | msgid "" 221 | "read a data set of arcs and remove them from the graph. empty line " 222 | "terminates the set." 223 | msgstr "" 224 | 225 | #: src/main.cpp:1338 226 | msgid "read data set of nodes and replace predecessors of NODE with given set." 227 | msgstr "" 228 | 229 | #: src/main.cpp:1339 230 | msgid "read data set of nodes and replace successors of NODE with given set." 231 | msgstr "" 232 | 233 | #: src/main.cpp:1369 234 | msgid "internal error: Digraph::replaceNeighbors() failed.\n" 235 | msgstr "" 236 | 237 | #: src/main.cpp:1384 238 | msgid "clear the graph model." 239 | msgstr "" 240 | 241 | #: src/main.cpp:1414 242 | msgid "shutdown the graph processor." 243 | msgstr "" 244 | 245 | #: src/main.cpp:1423 246 | #, c-format 247 | msgid "shutting down pid %d.\n" 248 | msgstr "" 249 | 250 | #: src/main.cpp:1447 251 | msgid "" 252 | "find the path from X to nearest root node. return data set of arcs " 253 | "representing the path." 254 | msgstr "" 255 | 256 | #: src/main.cpp:1449 257 | msgid "" 258 | "find the shortest path from node X to node Y. return data set of arcs " 259 | "representing the path." 260 | msgstr "" 261 | 262 | #: src/main.cpp:1482 263 | #, c-format 264 | msgid "%zu nodes visited, path length %zu%s\n" 265 | msgstr "" 266 | 267 | #: src/main.cpp:1502 268 | msgid "debugging: list N arcs starting from INDEX, " 269 | msgstr "" 270 | -------------------------------------------------------------------------------- /test/graphcore.tb: -------------------------------------------------------------------------------- 1 | # script to be used with talkback.py for testing graphcore 2 | 3 | # build small graph ################################################# 4 | > add-arcs: 5 | > 1,11 6 | > 1,12 7 | > 1,13 8 | > 2,21 9 | > 21,12 10 | > 13,21 11 | > 21,211 12 | > 13 | > 14 | = OK. 15 | 16 | # check stats 17 | > stats 18 | ~ ^OK\..*:$ 19 | * 20 | = ArcCount,7 21 | * 22 | = 23 | 24 | # check successors ################################################# 25 | > list-successors 1 26 | ~ ^OK\..*:$ 27 | = 11 28 | = 12 29 | = 13 30 | = 31 | 32 | > traverse-successors 1 1 33 | ~ ^OK\..*:$ 34 | = 1 35 | = 11 36 | = 12 37 | = 13 38 | = 39 | 40 | > traverse-successors 1 2 41 | ~ ^OK\..*:$ 42 | = 1 43 | = 11 44 | = 12 45 | = 13 46 | = 21 47 | = 48 | 49 | > traverse-successors 1 3 50 | ~ ^OK\..*:$ 51 | = 1 52 | = 11 53 | = 12 54 | = 13 55 | = 21 56 | = 211 57 | = 58 | 59 | # check predecessors ################################################# 60 | > traverse-predecessors 1 5 61 | ~ ^OK\..*:$ 62 | = 1 63 | = 64 | 65 | > list-predecessors 1 66 | ~ ^OK\..*:$ 67 | = 68 | 69 | > traverse-predecessors 211 0 70 | ~ ^OK\..*:$ 71 | = 211 72 | = 73 | 74 | > traverse-predecessors 666 1 75 | ~ ^NONE\. 76 | 77 | > traverse-predecessors 1 0 78 | ~ ^OK\..*:$ 79 | = 1 80 | = 81 | 82 | > traverse-predecessors 211 1 83 | ~ ^OK\..*:$ 84 | = 211 85 | = 21 86 | = 87 | 88 | > traverse-predecessors 211 2 89 | ~ ^OK\..*:$ 90 | = 211 91 | = 21 92 | = 2 93 | = 13 94 | = 95 | 96 | # test pathes ################################################# 97 | > find-path 211 2 98 | ~ ^NONE\. 99 | 100 | > find-path 1 2 101 | ~ ^NONE\. 102 | 103 | > find-path 11 11 104 | ~ ^OK\..*:$ 105 | = 106 | 107 | > find-path 1 211 108 | ~ ^OK\..*:$ 109 | = 1,13 110 | = 13,21 111 | = 21,211 112 | = 113 | 114 | > find-path 2 211 115 | ~ ^OK\..*:$ 116 | = 2,21 117 | = 21,211 118 | = 119 | 120 | # test find-root ################################################# 121 | > find-root 1 122 | ~ ^OK\..*:$ 123 | = 124 | 125 | > find-root 11 126 | ~ ^OK\..*:$ 127 | = 1,11 128 | = 129 | 130 | > find-root 211 131 | ~ ^OK\..*:$ 132 | = 2,21 133 | = 21,211 134 | = 135 | 136 | # test list-roots ################################################# 137 | > list-roots 138 | ~ ^OK\..*:$ 139 | = 1 140 | = 2 141 | = 142 | 143 | # (fixed) 144 | > list-leaves 145 | ~ ^OK\..*:$ 146 | = 11 147 | = 12 148 | = 211 149 | = 150 | 151 | # test set operations ################################################# 152 | > list-roots && traverse-predecessors 211 5 153 | ~ ^OK\..*:$ 154 | = 1 155 | = 2 156 | = 157 | 158 | > list-roots && traverse-predecessors 13 5 159 | ~ ^OK\..*:$ 160 | = 1 161 | = 162 | 163 | > traverse-successors 21 5 && traverse-successors 2 5 164 | ~ ^OK\..*:$ 165 | = 12 166 | = 21 167 | = 211 168 | = 169 | 170 | > traverse-successors 1 5 &&! traverse-successors 2 5 171 | ~ ^OK\..*:$ 172 | = 1 173 | = 11 174 | = 13 175 | = 176 | 177 | #(fixed) 178 | # returns NONE, should return empty set 179 | > traverse-successors 13 5 &&! traverse-successors 1 5 180 | ~ ^OK\..*:$ 181 | = 182 | 183 | #(fixed) 184 | > traverse-successors 21 5 &&! list-leaves 185 | ~ ^OK\..*:$ 186 | = 21 187 | = 188 | 189 | # test operations with NONE ########################################### 190 | # (fixed) 191 | # returns NONE, should return empty set 192 | > list-roots && list-leaves 193 | ~ ^OK\..*:$ 194 | = 195 | 196 | # (fixed) 197 | # && should return NONE *only* of one of the operants return NONE. 198 | # &&! should return NONE if the left operant is NONE. NONE on the right side is treated like an empty set. 199 | # (this crap needs to be documented somewhere..) 200 | 201 | # J: list-*-nonrecursive never returns NONE, so these tests don't work as expected: 202 | ; > list-roots && list-successors 666 203 | ; ~ ^NONE\..*$ 204 | ; 205 | ; > list-successors 666 && list-roots 206 | ; ~ ^NONE\..*$ 207 | 208 | # J: these work: 209 | > list-roots && traverse-successors 666 1 210 | ~ ^NONE\..*$ 211 | 212 | > traverse-successors 666 1 && list-roots 213 | ~ ^NONE\..*$ 214 | 215 | > list-roots &&! list-predecessors 666 216 | ~ ^OK\..*:$ 217 | = 1 218 | = 2 219 | = 220 | 221 | > traverse-successors 666 1 &&! list-roots 222 | ~ ^NONE\..*$ 223 | 224 | ; > list-roots &&! list-predecessors 666 225 | 226 | # insert dupes ################################################# 227 | > add-arcs: 228 | > 1,11 229 | > 2,21 230 | > 231 | ~ ^OK\.(.*[^:])?$ 232 | 233 | > stats 234 | ~ ^OK\..*:$ 235 | * 236 | = ArcCount,7 237 | * 238 | = 239 | 240 | > list-successors 1 241 | ~ ^OK\..*:$ 242 | = 11 243 | = 12 244 | = 13 245 | = 246 | 247 | # test remove arcs ################################################# 248 | > remove-arcs: 249 | > 666,777 250 | > 21,12 251 | > 252 | ~ ^OK\.(.*[^:])?$ 253 | 254 | > stats 255 | ~ ^OK\..*:$ 256 | * 257 | = ArcCount,6 258 | * 259 | = 260 | 261 | > list-successors 21 262 | ~ ^OK\..*:$ 263 | = 211 264 | = 265 | 266 | # test bad data ################################################# 267 | > add-arcs: 268 | > 1,2,3 269 | > 270 | ~ ^ERROR!.*$ 271 | 272 | #(fixed) 273 | # gets read as an empty line, should cause ERROR 274 | > add-arcs: 275 | > , 276 | > 277 | ~ ^ERROR!.*$ 278 | 279 | > add-arcs: 280 | > 1, 281 | > 282 | ~ ^ERROR!.*$ 283 | 284 | > add-arcs: 285 | > ,1 286 | > 287 | ~ ^ERROR!.*$ 288 | 289 | > add-arcs: 290 | > -1,-2 291 | > 292 | ~ ^ERROR!.*$ 293 | 294 | > add-arcs: 295 | > a,b 296 | > 297 | ~ ^ERROR!.*$ 298 | 299 | > add-arcs: 300 | > 1,0 301 | > 302 | ~ ^ERROR!.*$ 303 | 304 | # (fixed) 305 | # slurp dataset before erroring out! 306 | > add-arcs: 307 | > 1,11 308 | > a,b 309 | > 1,12 310 | > 311 | ~ ^ERROR!.*$ 312 | 313 | #check if remaining lines got slurped: 314 | > stats 315 | ~ ^OK\..*:$ 316 | * 317 | = 318 | 319 | 320 | # test clear ################################################# 321 | > clear 322 | ~ ^OK\.(.*[^:])?$ 323 | 324 | > stats 325 | ~ ^OK\..*:$ 326 | * 327 | = ArcCount,0 328 | * 329 | = 330 | 331 | > list-roots 332 | ~ ^OK\..*:$ 333 | = 334 | 335 | # test circles ################################################# 336 | > add-arcs: 337 | > 1,2 338 | > 2,3 339 | > 3,1 340 | > 4,4 341 | > 4,1 342 | > 343 | ~ ^OK\.(.*[^:])?$ 344 | 345 | > list-roots 346 | ~ ^OK\..*:$ 347 | = 348 | 349 | > find-root 2 350 | ~ ^NONE\.(.*[^:])?$ 351 | 352 | > find-root 4 353 | ~ ^NONE\.(.*[^:])?$ 354 | 355 | > find-path 2 1 356 | ~ ^OK\..*:$ 357 | = 2,3 358 | = 3,1 359 | = 360 | 361 | > find-path 2 4 362 | ~ ^NONE\.(.*[^:])?$ 363 | 364 | > traverse-successors 3 1 365 | ~ ^OK\..*:$ 366 | = 3 367 | = 1 368 | = 369 | 370 | > traverse-successors 2 6 371 | ~ ^OK\..*:$ 372 | = 2 373 | = 3 374 | = 1 375 | = 376 | 377 | > traverse-successors 4 6 378 | ~ ^OK\..*:$ 379 | = 4 380 | = 1 381 | = 2 382 | = 3 383 | = 384 | 385 | > traverse-predecessors 4 6 386 | ~ ^OK\..*:$ 387 | = 4 388 | = 389 | 390 | # test replace ################################################# 391 | > replace-successors 4: 392 | > 393 | ~ ^OK\.(.*[^:])?$ 394 | 395 | > list-by-head 0 396 | * 397 | = 398 | 399 | > list-by-tail 0 400 | * 401 | = 402 | 403 | > traverse-successors 4 2 404 | ~ ^NONE\.(.*[^:])?$ 405 | 406 | > list-predecessors 1 407 | ~ ^OK\..*:$ 408 | = 3 409 | = 410 | 411 | > replace-predecessors 4: 412 | > 2 413 | > 3 414 | > 415 | ~ ^OK\.(.*[^:])?$ 416 | 417 | > traverse-predecessors 4 1 418 | ~ ^OK\..*:$ 419 | = 4 420 | = 2 421 | = 3 422 | = 423 | 424 | > traverse-successors 2 1 425 | ~ ^OK\..*:$ 426 | = 2 427 | = 3 428 | = 4 429 | = 430 | 431 | # invalid commands ################################################# 432 | > bla bla 433 | ~ ^FAILED!(.*[^:])?$ 434 | 435 | > list-successors 436 | ~ ^FAILED!(.*[^:])?$ 437 | 438 | > traverse-successors 1 439 | ~ ^FAILED!(.*[^:])?$ 440 | 441 | > traverse-successors 1 1 1 442 | ~ ^FAILED!(.*[^:])?$ 443 | 444 | > traverse-successors -1 -1 445 | ~ ^FAILED!(.*[^:])?$ 446 | 447 | > traverse-successors 1.2 2.3 448 | ~ ^FAILED!(.*[^:])?$ 449 | 450 | > traverse-successors x y 451 | ~ ^FAILED!(.*[^:])?$ 452 | 453 | # (fixed) 454 | # slurp dataset before erroring out! 455 | > foo: 456 | > 1,11 457 | > 1,12 458 | > 459 | ~ ^FAILED!.*$ 460 | 461 | #check if remaining lines got slurped: 462 | > stats 463 | ~ ^OK\..*:$ 464 | * 465 | = 466 | 467 | 468 | # quit ################################################# 469 | > shutdown 470 | ~ ^OK\.(.*[^:])?$ 471 | 472 | # close output 473 | . 474 | 475 | # check return code 476 | ? 0 477 | -------------------------------------------------------------------------------- /test/talkback.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys, os, re 5 | import subprocess, signal 6 | import time, optparse 7 | 8 | comment_pattern = re.compile("^[;#] *(.*)$") 9 | send_pattern = re.compile("^(?:[>]|send) *(.*)$") 10 | expect_pattern = re.compile("^(?:[=]|expect) *(.*)$") 11 | match_pattern = re.compile("^(?:[~]|match) *(.*)$") 12 | skip_pattern = re.compile("^(?:[*]|skip) *(.*)$") 13 | code_pattern = re.compile("^(?:[?]|returns) *(.*)$") 14 | signal_pattern = re.compile("^(?:[!]|sig|signal) *(.*)$") 15 | close_pattern = re.compile("^(?:[.]|eof|close) *(.*)$") 16 | 17 | sigterm_delay = 1 # wait 1 sec for process to terminate 18 | 19 | 20 | class TalkbackException(Exception): 21 | def __init__(self, message, line = None): 22 | 23 | Exception.__init__(self, message, line) 24 | self.line = line 25 | self.message = message 26 | 27 | def __str__(self): 28 | msg = self.message 29 | 30 | if self.line is not None: 31 | msg = "on line %d: %s" % ( self.line, msg ) 32 | 33 | return msg 34 | 35 | class ExpectationFailed(TalkbackException): 36 | def __init__(self, message, line = None): 37 | TalkbackException.__init__(self, message, line) 38 | 39 | class ScriptException(TalkbackException): 40 | def __init__(self, message, line = None): 41 | TalkbackException.__init__(self, message, line) 42 | 43 | 44 | class Talkback: 45 | def __init__( self, proc, linesep = None, quiet = False ): 46 | self.proc = proc 47 | 48 | if linesep is None: 49 | linesep = os.linesep 50 | 51 | self.linesep = linesep 52 | self.quiet = quiet 53 | 54 | self.from_child = proc.stdout 55 | self.to_child = proc.stdin 56 | 57 | def echo(self, s): 58 | if not self.quiet: 59 | print s 60 | 61 | def send(self, s, line = None): 62 | try: 63 | self.to_child.write(s + self.linesep) 64 | 65 | except ValueError: 66 | if self.proc.poll() is None: 67 | raise ExpectationFailed( "client no longer accepts input! client process still running with pid %d" % self.proc.pid, line ) 68 | else: 69 | raise ExpectationFailed( "client no longer accepts input, client process terminated unexpectedly with code %d" % self.proc.returncode, line ) 70 | 71 | self.echo( "> " + s ) 72 | 73 | def recv(self, line = None): 74 | try: 75 | s = self.from_child.readline() 76 | except ValueError, e: 77 | raise IOError( "can't read from client's stdout pipe: %s" % e ) 78 | 79 | if s is None or s == "": 80 | if self.proc.poll() is None: 81 | raise ExpectationFailed( "unexpected EOF! client process still running with pid %d" % self.proc.pid, line ) 82 | else: 83 | raise ExpectationFailed( "unexpected EOF, client process terminated unexpectedly with code %d" % self.proc.returncode, line ) 84 | 85 | s = s.strip() 86 | 87 | self.echo( "< " + s ) 88 | 89 | return s 90 | 91 | def expect(self, s, skip = False, line = None): 92 | while True: 93 | t = self.recv(line = line) 94 | 95 | if t == s: 96 | return True 97 | 98 | if not skip: 99 | raise ExpectationFailed( "expected `%s`, found `%s`" % (s, t), line ) 100 | 101 | def match(self, s, skip = False, line = None): 102 | p = re.compile(s) 103 | 104 | while True: 105 | t = self.recv(line = line) 106 | 107 | if p.search(t): 108 | return True 109 | 110 | if not skip: 111 | raise ExpectationFailed( "expected match on pattern `%s`, found `%s`" % (s, t), line ) 112 | 113 | def terminate(self, code = None, skip = False, line = None): 114 | if self.proc.poll() is None and skip: 115 | 116 | while True: 117 | t = self.from_child.readline() #XXX: this never times out! 118 | if t == "" or t is None or t == False: 119 | break 120 | 121 | self.echo( "< " + t.strip() ) 122 | 123 | if not skip: 124 | t = self.from_child.readline() #assert that there is no input left 125 | if t != "" and t is not None and t != False: 126 | raise ExpectationFailed( "EOF expected, found `%s`" % t.strip(), line ) 127 | 128 | if self.proc.poll() is None: 129 | time.sleep( 0.1 ) 130 | 131 | if self.proc.poll() is None: 132 | time.sleep( sigterm_delay ) 133 | 134 | if self.proc.poll() is None: 135 | raise ExpectationFailed( "client process didn't terminate when expected! sending SIGHUP.", line ) 136 | 137 | if self.proc.returncode is not None: 138 | if code is not None and self.proc.returncode != code: 139 | raise ExpectationFailed( "expected return code %d, found %d" % (code, self.proc.returncode), line ) 140 | 141 | self.echo( "returns %d" % code ) 142 | 143 | def signal(self, sig, line = None): 144 | sig = sig.lowercase() 145 | 146 | if sig == "hup" or sig == "sighup": 147 | self.child.send_signal(signal.SIGHUP) 148 | 149 | elif sig == "term" or sig == "sigterm": 150 | self.child.terminate() 151 | 152 | elif sig == "kill" or sig == "sigkill": 153 | self.child.terminate() 154 | 155 | else: 156 | try: 157 | sig = int( sig ) 158 | self.send_signal( sig ) 159 | 160 | except ValueError: 161 | raise ScriptException("bad signal: %s" % sig, line ) 162 | 163 | self.echo( "signal %s" % sig ) 164 | 165 | def run(self, script): 166 | skip = False 167 | line = 0 168 | 169 | while True: 170 | s = script.readline() 171 | line = line + 1 172 | 173 | if s is None or s == "": 174 | break 175 | 176 | s = s.strip() 177 | if s == "": 178 | self.echo("") 179 | continue 180 | 181 | if comment_pattern.match(s): 182 | self.echo(s) 183 | continue 184 | 185 | m = code_pattern.match(s) 186 | if m: 187 | try: 188 | code = int( m.group(1) ) 189 | self.terminate( code, skip, line ) 190 | continue 191 | 192 | except ValueError: 193 | raise ScriptException("bad return code expectation: %s" % m.group(1), line ) 194 | 195 | if close_pattern.match(s): 196 | self.to_child.close() 197 | self.echo( "close" ) 198 | continue 199 | 200 | m = signal_pattern.match(s) 201 | if m: 202 | sig = m.group(1) 203 | 204 | self.signal( sig, line ) 205 | continue 206 | 207 | m = skip_pattern.match(s) 208 | if m: 209 | skip = True 210 | continue 211 | 212 | if self.proc.poll() is not None: 213 | raise ExpectationFailed("client process terminated unexpectedly with code %d!" % self.proc.returncode, line ) 214 | 215 | m = send_pattern.match(s) 216 | if m: 217 | self.send( m.group(1), line ) 218 | #skip = False 219 | continue 220 | 221 | m = expect_pattern.match(s) 222 | if m: 223 | self.expect( m.group(1), skip, line ) 224 | skip = False 225 | continue 226 | 227 | m = match_pattern.match(s) 228 | if m: 229 | self.match( m.group(1), skip, line ) 230 | skip = False 231 | continue 232 | 233 | raise ScriptException("unknown instruction: %s" % s, line ) 234 | 235 | 236 | if __name__=="__main__": 237 | parser = optparse.OptionParser( usage= "USAGE: talkpack [options...]