diff --git a/spec/wibox/test_utils.lua b/spec/wibox/test_utils.lua index a5bf5efd..2869ebdc 100644 --- a/spec/wibox/test_utils.lua +++ b/spec/wibox/test_utils.lua @@ -39,21 +39,128 @@ end say:set("assertion.widget_fit.positive", "Offering (%s, %s) to widget and expected (%s, %s), but got (%s, %s)") assert:register("assertion", "widget_fit", widget_fit, "assertion.widget_fit.positive", "assertion.widget_fit.positive") +local function draw_layout_comparison(actual, expected) + local lines = { + "Widget layout does not match:\n", + } + + local left, right = {}, {} + + local function times(char, len) + local chars = {} + for _ = 1, len do + table.insert(chars, char) + end + return table.concat(chars, "") + end + + local function wrap_line(width, x, line) + -- Two columns occupied by the `|` characters + local buffer = width - 2 - #line + local buffer_left = math.floor(buffer / 2) + local buffer_right = buffer - buffer_left + + return string.format( + "%s|%s%s%s|", + times(" ", x), times(" ", buffer_left), line, times(" ", buffer_right) + ) + end + + local function draw_widget(widget) + local height = widget._height + local width = widget._width + local x = widget._matrix.x0 + -- Two lines removed for the horizontal lines, + -- three for the lines with text + local fixed = 5 + local buffer_top = math.floor((height - fixed) / 2) + local buffer_bottom = height - fixed - buffer_top + + local widget_lines = {times("-", width)} + for _ = 1, buffer_top do + table.insert(widget_lines, wrap_line(width, x, " ")) + end + + table.insert(widget_lines, wrap_line(width, x, widget._name or "Widget")) + table.insert(widget_lines, wrap_line(width, x, string.format( + "Size %d,%d", + widget._width, widget._height + ))) + table.insert(widget_lines, wrap_line(width, x, string.format( + "Pos %d,%d", + widget._matrix.x0, widget._matrix.y0 + ))) + + for _ = 1, buffer_bottom do + table.insert(widget_lines, wrap_line(width, x, " ")) + end + + table.insert(widget_lines, times("-", width)) + + return widget_lines + end + + local max_len = 0 + + for _, widget in ipairs(actual) do + for _, line in ipairs(draw_widget(widget)) do + max_len = math.max(max_len, #line) + table.insert(left, line) + end + end + + for _, widget in ipairs(expected) do + for _, line in ipairs(draw_widget(widget)) do + table.insert(right, line) + end + end + + table.insert(lines, string.format( + "%-"..max_len.."s %-"..max_len.."s", + "Actual:", "Expected:" + )) + + for i = 1, math.max(#left, #right) do + table.insert(lines, string.format("%-"..max_len.."s %s", left[i] or "", right[i] or "")) + end + + -- Force one additional newline. Due to how the error message has to be set + -- up, we want to force `say`'s trailing quote into a new line. + table.insert(lines, "") + + return table.concat(lines, "\n") +end + local function widget_layout(state, arguments) if #arguments ~= 3 then error("Have " .. #arguments .. " arguments, but need 3") end local widget = arguments[1] - local given = arguments[2] + local width_actual, height_actual = arguments[2][1], arguments[2][2] local expected = arguments[3] - local children = widget.layout and widget:layout({ "fake context" }, given[1], given[2]) or {} - local fits = true - if #children ~= #expected then - fits = false - else - for i = 1, #children do + if not widget.layout then + arguments[1] = "Method `layout` is missing. Is this even a widget?" + return false + end + + local children = widget:layout({"fake context"}, width_actual, height_actual) + + if type(children) ~= "table" then + arguments[1] = string.format( + "Expected table from widget:layout(), got %s", + type(children) + ) + return + end + + local num_children = #children + + local fits = num_children == #expected + -- If the numbers don't match, we can skip checking them individually + if fits then + for i = 1, num_children do local child, exp = children[i], expected[i] if child._widget ~= exp._widget or child._width ~= exp._width or @@ -69,15 +176,13 @@ local function widget_layout(state, arguments) return true end - -- For proper error message, mess with the arguments - arguments[1] = expected - arguments[2] = children - arguments[3] = given[1] - arguments[4] = given[2] - + arguments[1] = draw_layout_comparison(children, expected) return false end -say:set("assertion.widget_layout.positive", "Expected:\n%s\nbut got:\n%s\nwhen offering (%s, %s) to widget") + +-- Hack the API by using the first argument as arbitrary string. +-- This gives actual flexibility in the error message. +say:set("assertion.widget_layout.positive", "%s") assert:register("assertion", "widget_layout", widget_layout,