Current section
Files
Jump to
Current section
Files
lib/ex_flow/test_graph_generator.ex
defmodule ExFlow.TestGraphGenerator do
@moduledoc """
Generates test graphs with configurable sizes for performance testing.
"""
alias ExFlow.Core.Graph, as: FlowGraph
@doc """
Generates a graph with the specified number of nodes and edges.
## Options
- `:node_count` - Number of nodes to generate (default: 100)
- `:edge_density` - Average edges per node (default: 2.0)
- `:layout` - Layout strategy (:grid, :random, :circular) (default: :grid)
"""
def generate(opts \\ []) do
node_count = Keyword.get(opts, :node_count, 100)
edge_density = Keyword.get(opts, :edge_density, 2.0)
layout = Keyword.get(opts, :layout, :grid)
graph = FlowGraph.new()
# Generate nodes
{graph, node_ids} = generate_nodes(graph, node_count, layout)
# Generate edges
graph = generate_edges(graph, node_ids, edge_density)
{:ok, graph}
end
defp generate_nodes(graph, count, layout) do
positions = calculate_positions(count, layout)
{graph, node_ids} =
Enum.reduce(1..count, {graph, []}, fn i, {acc_graph, ids} ->
node_id = "node-#{i}"
node_type = if rem(i, 2) == 0, do: :task, else: :agent
position = Enum.at(positions, i - 1)
case FlowGraph.add_node(acc_graph, node_id, node_type, %{position: position}) do
{:ok, new_graph} -> {new_graph, [node_id | ids]}
{:error, _} -> {acc_graph, ids}
end
end)
{graph, Enum.reverse(node_ids)}
end
defp calculate_positions(count, :grid) do
# Grid layout with ~20 nodes per row
cols = ceil(:math.sqrt(count * 1.5))
spacing_x = 200
spacing_y = 150
for i <- 0..(count - 1) do
row = div(i, cols)
col = rem(i, cols)
%{x: col * spacing_x + 100, y: row * spacing_y + 100}
end
end
defp calculate_positions(count, :random) do
# Random positions in a 4000x3000 area
for _ <- 1..count do
%{x: :rand.uniform(4000), y: :rand.uniform(3000)}
end
end
defp calculate_positions(count, :circular) do
# Circular layout
radius = count * 5
center_x = 2000
center_y = 1500
for i <- 0..(count - 1) do
angle = 2 * :math.pi() * i / count
%{
x: round(center_x + radius * :math.cos(angle)),
y: round(center_y + radius * :math.sin(angle))
}
end
end
defp generate_edges(graph, node_ids, edge_density) do
target_edge_count = round(length(node_ids) * edge_density)
# Create edges by connecting each node to a few random other nodes
Enum.reduce(1..target_edge_count, graph, fn i, acc_graph ->
source = Enum.random(node_ids)
target = Enum.random(node_ids)
# Don't create self-loops
if source == target do
acc_graph
else
edge_id = "edge-#{i}"
case FlowGraph.add_edge(acc_graph, edge_id, source, "out", target, "in") do
{:ok, new_graph} -> new_graph
{:error, _} -> acc_graph
end
end
end)
end
@doc """
Generates a stress test graph with many nodes and edges.
"""
def stress_test_graph do
generate(node_count: 500, edge_density: 3.0, layout: :grid)
end
@doc """
Generates a large graph for performance testing.
"""
def large_graph do
generate(node_count: 1000, edge_density: 2.5, layout: :grid)
end
@doc """
Generates a small test graph for quick testing.
"""
def small_graph do
generate(node_count: 50, edge_density: 2.0, layout: :grid)
end
end