1#!/usr/bin/env python3 2# Copyright (C) 2024 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License a 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16from python.generators.diff_tests.testing import DataPath 17from python.generators.diff_tests.testing import Csv 18from python.generators.diff_tests.testing import DiffTestBlueprint 19from python.generators.diff_tests.testing import TestSuite 20 21 22class GraphSearchTests(TestSuite): 23 24 def test_dfs_empty_table(self): 25 return DiffTestBlueprint( 26 trace=DataPath('counters.json'), 27 query=""" 28 INCLUDE PERFETTO MODULE graphs.search; 29 30 WITH foo AS ( 31 SELECT 0 as source_node_id, 0 AS dest_node_id 32 WHERE FALSE 33 ) 34 SELECT * FROM graph_reachable_dfs!( 35 foo, 36 (SELECT 0 AS node_id WHERE FALSE) 37 ) 38 """, 39 out=Csv(""" 40 "node_id","parent_node_id" 41 """)) 42 43 def test_dfs_one_node(self): 44 return DiffTestBlueprint( 45 trace=DataPath('counters.json'), 46 query=""" 47 INCLUDE PERFETTO MODULE graphs.search; 48 49 WITH foo AS ( 50 SELECT 5 AS source_node_id, 10 AS dest_node_id 51 UNION ALL 52 SELECT 10, 10 53 ) 54 SELECT * FROM graph_reachable_dfs!(foo, (SELECT 5 AS node_id)); 55 """, 56 out=Csv(""" 57 "node_id","parent_node_id" 58 5,"[NULL]" 59 10,5 60 """)) 61 62 def test_dfs_two_nodes(self): 63 return DiffTestBlueprint( 64 trace=DataPath('counters.json'), 65 query=""" 66 INCLUDE PERFETTO MODULE graphs.search; 67 68 CREATE PERFETTO TABLE foo AS 69 SELECT NULL AS source_node_id, NULL AS dest_node_id WHERE FALSE 70 UNION ALL 71 VALUES (10, 11) 72 UNION ALL 73 VALUES (0, 10); 74 75 SELECT * FROM graph_reachable_dfs!(foo, (SELECT 0 AS node_id)); 76 """, 77 out=Csv(""" 78 "node_id","parent_node_id" 79 0,"[NULL]" 80 10,0 81 11,10 82 """)) 83 84 def test_dfs_lengauer_tarjan_example(self): 85 return DiffTestBlueprint( 86 trace=DataPath('counters.json'), 87 query=""" 88 INCLUDE PERFETTO MODULE graphs.search; 89 90 CREATE PERFETTO TABLE foo AS 91 SELECT NULL AS source, NULL AS dest WHERE FALSE 92 UNION ALL 93 VALUES ('R', 'A'), ('R', 'B'), ('R', 'C'), ('A', 'D') 94 UNION ALL 95 VALUES ('B', 'A'), ('B', 'D'), ('B', 'E'), ('C', 'F') 96 UNION ALL 97 VALUES ('C', 'G'), ('D', 'L'), ('E', 'H'), ('F', 'I') 98 UNION ALL 99 VALUES ('G', 'I'), ('G', 'J'), ('H', 'E'), ('H', 'K') 100 UNION ALL 101 VALUES ('I', 'K'), ('J', 'I'), ('K', 'I'), ('K', 'R') 102 UNION ALL 103 VALUES ('L', 'H'); 104 105 WITH bar AS ( 106 SELECT 107 unicode(source) AS source_node_id, 108 unicode(dest) AS dest_node_id 109 FROM foo 110 ) 111 SELECT 112 char(node_id) AS node_id, 113 IIF( 114 parent_node_id IS NULL, 115 NULL, 116 char(parent_node_id) 117 ) AS parent_node_id 118 FROM graph_reachable_dfs!(bar, (SELECT unicode('R') AS node_id)) 119 ORDER BY node_id; 120 """, 121 out=Csv(""" 122 "node_id","parent_node_id" 123 "A","R" 124 "B","R" 125 "C","R" 126 "D","A" 127 "E","H" 128 "F","C" 129 "G","C" 130 "H","L" 131 "I","K" 132 "J","G" 133 "K","H" 134 "L","D" 135 "R","[NULL]" 136 """)) 137 138 def test_bfs_empty_table(self): 139 return DiffTestBlueprint( 140 trace=DataPath('counters.json'), 141 query=""" 142 INCLUDE PERFETTO MODULE graphs.search; 143 144 WITH foo AS ( 145 SELECT 0 as source_node_id, 0 AS dest_node_id 146 WHERE FALSE 147 ) 148 SELECT * FROM graph_reachable_bfs!( 149 foo, (SELECT 0 AS node_id WHERE FALSE) 150 ) 151 """, 152 out=Csv(""" 153 "node_id","parent_node_id" 154 """)) 155 156 def test_bfs_one_node(self): 157 return DiffTestBlueprint( 158 trace=DataPath('counters.json'), 159 query=""" 160 INCLUDE PERFETTO MODULE graphs.search; 161 162 WITH foo AS ( 163 SELECT 5 AS source_node_id, 10 AS dest_node_id 164 UNION ALL 165 SELECT 10, 10 166 ) 167 SELECT * FROM graph_reachable_bfs!(foo, (SELECT 5 AS node_id)); 168 """, 169 out=Csv(""" 170 "node_id","parent_node_id" 171 5,"[NULL]" 172 10,5 173 """)) 174 175 def test_bfs_two_nodes(self): 176 return DiffTestBlueprint( 177 trace=DataPath('counters.json'), 178 query=""" 179 INCLUDE PERFETTO MODULE graphs.search; 180 181 CREATE PERFETTO TABLE foo AS 182 SELECT NULL AS source_node_id, NULL AS dest_node_id WHERE FALSE 183 UNION ALL 184 VALUES (10, 11) 185 UNION ALL 186 VALUES (0, 10); 187 188 SELECT * FROM graph_reachable_bfs!(foo, (SELECT 0 AS node_id)); 189 """, 190 out=Csv(""" 191 "node_id","parent_node_id" 192 0,"[NULL]" 193 10,0 194 11,10 195 """)) 196 197 def test_bfs_lengauer_tarjan_example(self): 198 return DiffTestBlueprint( 199 trace=DataPath('counters.json'), 200 query=""" 201 INCLUDE PERFETTO MODULE graphs.search; 202 203 CREATE PERFETTO TABLE foo AS 204 SELECT NULL AS source, NULL AS dest WHERE FALSE 205 UNION ALL 206 VALUES ('R', 'A'), ('R', 'B'), ('R', 'C'), ('A', 'D') 207 UNION ALL 208 VALUES ('B', 'A'), ('B', 'D'), ('B', 'E'), ('C', 'F') 209 UNION ALL 210 VALUES ('C', 'G'), ('D', 'L'), ('E', 'H'), ('F', 'I') 211 UNION ALL 212 VALUES ('G', 'I'), ('G', 'J'), ('H', 'E'), ('H', 'K') 213 UNION ALL 214 VALUES ('I', 'K'), ('J', 'I'), ('K', 'I'), ('K', 'R') 215 UNION ALL 216 VALUES ('L', 'H'); 217 218 WITH bar AS ( 219 SELECT 220 unicode(source) AS source_node_id, 221 unicode(dest) AS dest_node_id 222 FROM foo 223 ) 224 SELECT 225 char(node_id) AS node_id, 226 IIF( 227 parent_node_id IS NULL, 228 NULL, 229 char(parent_node_id) 230 ) AS parent_node_id 231 FROM graph_reachable_bfs!(bar, (SELECT unicode('R') AS node_id)) 232 ORDER BY node_id; 233 """, 234 out=Csv(""" 235 "node_id","parent_node_id" 236 "A","R" 237 "B","R" 238 "C","R" 239 "D","A" 240 "E","B" 241 "F","C" 242 "G","C" 243 "H","E" 244 "I","F" 245 "J","G" 246 "K","H" 247 "L","D" 248 "R","[NULL]" 249 """)) 250 251 def test_next_sibling(self): 252 return DiffTestBlueprint( 253 trace=DataPath('counters.json'), 254 query=""" 255 INCLUDE PERFETTO MODULE graphs.search; 256 257 CREATE PERFETTO TABLE foo AS 258 SELECT 1 AS node_id, 0 AS node_parent_id, 1 AS sort_key 259 UNION ALL 260 SELECT 2 AS node_id, 1 AS node_parent_id, 2 AS sort_key 261 UNION ALL 262 SELECT 3 AS node_id, 1 AS node_parent_id, 1 AS sort_key; 263 264 SELECT * FROM graph_next_sibling!(foo); 265 """, 266 out=Csv(""" 267 "node_id","next_node_id" 268 1,"[NULL]" 269 3,2 270 2,"[NULL]" 271 """)) 272 273 def test_weight_bounded_dfs_floor(self): 274 return DiffTestBlueprint( 275 trace=DataPath('counters.json'), 276 query=""" 277 INCLUDE PERFETTO MODULE graphs.search; 278 279 CREATE PERFETTO TABLE foo AS 280 SELECT 0 AS source_node_id, 0 AS dest_node_id, 0 AS edge_weight 281 UNION ALL 282 VALUES (1, 2, 1) 283 UNION ALL 284 VALUES (1, 3, 1) 285 UNION ALL 286 VALUES (3, 4, 1) 287 UNION ALL 288 VALUES (3, 5, 0) 289 UNION ALL 290 VALUES (5, 6, 0); 291 292 CREATE PERFETTO TABLE roots AS 293 SELECT 0 AS root_node_id, 0 AS root_target_weight 294 UNION ALL 295 VALUES (1, 2) 296 UNION ALL 297 VALUES (3, 1) 298 UNION ALL 299 VALUES (2, 0); 300 301 SELECT * FROM graph_reachable_weight_bounded_dfs!(foo, roots, 1); 302 """, 303 out=Csv(""" 304 "root_node_id","node_id","parent_node_id" 305 0,0,"[NULL]" 306 1,1,"[NULL]" 307 1,2,1 308 1,3,1 309 1,4,3 310 3,3,"[NULL]" 311 3,4,3 312 3,5,3 313 3,6,5 314 2,2,"[NULL]" 315 """)) 316 317 def test_weight_bounded_dfs_ceiling(self): 318 return DiffTestBlueprint( 319 trace=DataPath('counters.json'), 320 query=""" 321 INCLUDE PERFETTO MODULE graphs.search; 322 323 CREATE PERFETTO TABLE foo AS 324 SELECT 0 AS source_node_id, 0 AS dest_node_id, 0 AS edge_weight 325 UNION ALL 326 VALUES (1, 2, 1) 327 UNION ALL 328 VALUES (1, 3, 1) 329 UNION ALL 330 VALUES (3, 4, 1) 331 UNION ALL 332 VALUES (3, 5, 0) 333 UNION ALL 334 VALUES (5, 6, 0); 335 336 CREATE PERFETTO TABLE roots AS 337 SELECT 0 AS root_node_id, 0 AS root_target_weight 338 UNION ALL 339 VALUES (1, 2) 340 UNION ALL 341 VALUES (3, 1) 342 UNION ALL 343 VALUES (2, 0); 344 345 SELECT * FROM graph_reachable_weight_bounded_dfs!(foo, roots, 0); 346 """, 347 out=Csv(""" 348 "root_node_id","node_id","parent_node_id" 349 0,0,"[NULL]" 350 1,1,"[NULL]" 351 1,2,1 352 1,3,1 353 3,3,"[NULL]" 354 3,4,3 355 3,5,3 356 3,6,5 357 2,2,"[NULL]" 358 """)) 359