Merge "Make it possible to sort on simple custom columns"
[ninja.git] / modules / test / libraries / MockLivestatus.php
blob82131ef3b9dea45abf44ff740edb8aa68e0d80c3
1 <?php
2 /**
3 * Exception for MockLivestatus.
4 * Not expected to be fetched anywhere but in phpunit library
5 */
6 class MockLivestatus_Exception extends Exception {}
8 /**
9 * Local helper for MockLivestatus-library.
10 * Handles the stack for resolving livestatus filters on a given object.
12 class MockLivestatus_StateMachine {
13 /**
14 * Stack for the state of the state machine
16 public $stack;
17 /**
18 * The object to use as source for tests
20 public $object;
22 /**
23 * Initialize the state machine, with an empty stack and a object to work
24 * with
26 * @param $object an
27 * array with retrieved parameters
29 public function __construct($object) {
30 $this->stack = array ();
31 $this->object = $object;
34 /**
35 * Process a "Filter: args" line, and push the reuslt to the stack
37 * @param $args the
38 * "args" part of the filter line
39 * @throws MockLivestatus_Exception
41 public function process_Filter($args) {
42 if (preg_match('/^\s*([a-zA-Z_]+)\s+(!?)([<>=~]+)\s+(.*)$/', $args,
43 $matches)) {
44 $match_var = $matches[1];
45 $match_negate = $matches[2];
46 $match_op = $matches[3];
47 $match_value = $matches[4];
49 if (!isset($this->object[$match_var])) {
50 throw new MockLivestatus_Exception('Unknown field ' . $match_var);
53 $value = $this->object[$match_var];
55 $result = false;
56 switch ($match_op) {
57 case '=':/* equality */
58 $result = ($match_value == $value);
59 break;
60 case '~': /* match regular expression (substring match) */
61 $result = (false != preg_match('/' . $match_value . '/', $value));
62 break;
63 case '=~': /* equality ignoring case */
64 $result = (strtolower($match_value) == strtolower($value));
65 break;
66 case '~~': /* regular expression ignoring case */
67 $result = (false != preg_match('/' . $match_value . '/i',
68 $value));
69 break;
70 case '<': /* less than */
71 $result = ($match_value < $value);
72 break;
73 case '>': /* greater than */
74 $result = ($match_value > $value);
75 break;
76 case '<=': /* less or equal */
77 $result = ($match_value <= $value);
78 break;
79 case '>=': /* greater or equal */
80 $result = ($match_value >= $value);
81 break;
82 default:
83 throw new MockLivestatus_Exception(
84 'Unknown filter operator ' . $match_op);
87 if ($match_negate == '!') {
88 $result = !$result;
91 $this->stack[] = $result;
92 } else {
93 throw new MockLivestatus_Exception("Malformed filter: " . $args);
96 /**
97 * Process a "And: N" livestatus filter line, manipulates the stack
98 * accordingly
100 * @param $args the
101 * "N" part of the filter line, expected to be numeric
102 * @throws MockLivestatus_Exception
104 public function process_And($args) {
105 $result = true;
106 if (!is_numeric($args)) {
107 throw new MockLivestatus_Exception(
108 "And statement isn't numeric: " . $args);
110 for ($i = 0; $i < intval($args); $i++) {
111 $val = array_pop($this->stack);
112 if (!$val) {
113 $result = false;
116 $this->stack[] = $result;
119 * Process a "Or: N" livestatus filter line, manipulates the stack
120 * accordingly
122 * @param $args the
123 * "N" part of the filter line, expected to be numeric
124 * @throws MockLivestatus_Exception
126 public function process_Or($args) {
127 $result = false;
128 if (!is_numeric($args)) {
129 throw new MockLivestatus_Exception(
130 "Or statement isn't numeric: " . $args);
132 for ($i = 0; $i < intval($args); $i++) {
133 $val = array_pop($this->stack);
134 if ($val) {
135 $result = true;
138 $this->stack[] = $result;
141 * Process a "Negate:" livestatus filter line, negates the top of the stack
143 * @param $args an
144 * empty string (tested to be empty)
145 * @throws MockLivestatus_Exception
147 public function process_Negate($args) {
148 if (!empty($args)) {
149 throw new MockLivestatus_Exception(
150 '"Negate:" line with arguments isn\'t allowed');
152 $result = !array_pop($this->stack);
153 $this->stack[] = $result;
157 * Get the result from the stack, as anding all the lines that is left.
159 * @return boolean
161 public function get_result() {
162 foreach ($this->stack as $val) {
163 if (!$val) {
164 return false;
167 return true;
172 * A mock replacement for op5Livestatus, which works on the data array passed to
173 * the constructor.
174 * Useful for unit testing
176 class MockLivestatus {
178 * Storage for the mocked environment.
180 protected $data;
183 * An array of the previously requested columns
185 public $last_columns = false;
187 * Load the mocked livestatus environment
189 * @param $data Data
190 * to be available in the mocked environemnt, indexed by table,
191 * structure as livestatus result (but with names as keys)
193 public function __construct($data) {
194 $this->data = $data;
198 * Query the mocked livestatus environment.
200 * @param $table Table
201 * to search in
202 * @param $filter Filter,
203 * as an array, or multiline string
204 * @param $columns Columns
205 * to request
206 * @param $options Options
207 * (not used ATM)
208 * @throws MockLivestatus_Exception
209 * @return array, as op5Livestatus returns
211 public function query($table, $filter, $columns, $options = array()) {
213 /* Strip down $filter-var to make sure it's an array */
214 if (is_string($filter))
215 $filter = explode("\n", $filter);
216 if (empty($filter))
217 $filter = array ();
219 $processed_filter = array ();
220 foreach ($filter as $filterline) {
221 if (!empty($filterline)) {
222 $filterop = explode(':', $filterline, 2);
223 if (count($filterop) == 2) {
224 $processed_filter[] = array_map('trim', $filterop);
228 $table_data = $this->data[$table];
230 $this->last_columns = $columns;
231 if(is_array($this->last_columns)) {
232 sort($this->last_columns);
235 if (empty($columns)) {
236 $columns = array_keys($table_data[0]);
238 if (!is_array($columns)) {
239 throw new MockLivestatus_Exception(
240 'Unknown column definition: ' . var_dump($columns, false));
243 $objects = array ();
244 foreach ($table_data as $obj) {
245 $filter_sm = new MockLivestatus_StateMachine($obj);
246 foreach ($processed_filter as $fop) {
247 $filter_sm->{'process_' . $fop[0]}($fop[1]);
249 if ($filter_sm->get_result()) {
250 $this_obj = array ();
251 foreach ($columns as $col) {
252 if (array_key_exists($col, $obj)) {
253 $this_obj[] = $obj[$col];
254 } else {
255 throw new MockLivestatus_Exception(
256 'Unknown column ' . $col . ' for table ' . $table);
259 $objects[] = $this_obj;
263 return array ($columns,$objects,count($objects));