From patchwork Sat Oct 17 16:57:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Orling X-Patchwork-Id: 101140 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87755CCF2CA for ; Sat, 17 Oct 2026 16:57:22 +0000 (UTC) Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.343.1792256235179934955 for ; Sat, 17 Oct 2026 09:57:15 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=DjJ9uwrT; spf=pass (domain: gmail.com, ip: 209.85.214.175, mailfrom: ticotimo@gmail.com) Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2e8da6ef7ddso3904125ad.3 for ; Sat, 17 Oct 2026 09:57:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792256234; x=1792861034; darn=lists.yoctoproject.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=+5a6VHycAPs5b0bQ2HsTtIP03p39Xiv4tUYlbwHIS6I=; b=DjJ9uwrThHMKx/+LmlQqdltdPm2geZ37YyRLG/Z07OJE+4cKvRRYFT2pRVtkoNp87d duPrx5N+9lPrs/r99DuYKh0We5OV/+vOe4Oqcf93oziTKB7yTzrglzs5YDK8eEIF/34i RNxJDv/tnnra45JfhZkqdiZSID7axJJlzONHn01oYSc2RX/cfvybfn8n+DoulSoXf7EZ hEbHJwegg/JAFhuGnDruw9It2knj0YrOKkLW0Lb7MYBClcI3IfxK6qasRmOSUi3sIxWj Z/bdcRDmTEbm7MZtktPOr5P8nk4Pw4QD3dZdDnD0YcBcs7gRlCI5OflEyZLchzNSNw0k JoEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792256234; x=1792861034; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=+5a6VHycAPs5b0bQ2HsTtIP03p39Xiv4tUYlbwHIS6I=; b=zDg8nBg6oxTc8+BT/dJJS9tHxWn8dFo8ihMV/CrzimAvsoEaW+y/kWTMWV7gOciopP HN/0ultcwVKF99MByEp7n5NSSNPdQwSBna+Y1kWfILDh9evMQcgFDPQjns0C977ZWst8 5zEQ+Jl0IMb8tNZK0gpUnzPieK7qVayhqmMlcU4r3alfXJGETtEwXD76TNkTt9iDqDNV sLnrTLDJFuGTSfp0KI5V8kVQRjjRczl/HTzsSqHkwddB0o0BfQA0oKn7p90FS6UeOMv2 0UduqqVJID1TcE5Q/o+YZJtsKM6AWz4VxCRk0Q2gOMk8s76HNMVJRHVy/Sb3iKDzxb4H u06w== X-Gm-Message-State: AFq9FYLeC/IvUr/sb9LfGqkdNuK58hak0nwZsOhjATAzK+C/PFzkSsP7 z4BC3en48BkzZyRwXc+dqV9fx0rF1fgZwFYOuPMCXjdD0fusv4gfAKEmogG0nw== X-Gm-Gg: AYBFou2fF3UzOCwTtOpbL5SfYxUi4BDJOqJN3qQu0HpXbp8VjSjE7zEQYgNM4yr57/Y yCIDxKOL7WytPizXLLbEakG6F3w/BeInRQbR1Qy7ShHJMmQ53MJF/UkH+9Dh02Iz2TacmWNB+Xa Qi7sG0eMSny8MO+1fFL4QjiPZNNyx1qfRMoXGuSAkXQRBJhX0LkCZd6Fpboy8WVE6Xt6+/2qK0a 2vBicim3QEPIwXpKT3n1qJBXxiT/MMXukGwMZxyfwpU0bvwBURPf/Ft3vi1xq99DZXPSCUVhCW8 msRzS+/pP0kfgl4tlXKO58VLpSSiJmmH/thLf8c5W3XCvOJ+N39Com+RfV3s09bSu3opU616yP9 3UQyGTBmOn3yZqB7lgF8UTBwdUnptK6SGrTSrWrgSgZ9YRStwGXuiNohppv7gJFdb0ZwbSOK72e ypcZNsU6KQ33y6KottZCG9ZJrcHUrrPKFw72CtSyHEy7ZHFZnIVoZogpUop7IKa/b+47YkcDsy7 b79ppQxRytN2tgw9Vq+HF1eARu5uTg= X-Received: by 2002:a17:902:c405:b0:2e2:e3aa:7a35 with SMTP id d9443c01a7336-2e9a6e41184mr29097455ad.36.1792256234323; Sat, 17 Oct 2026 09:57:14 -0700 (PDT) Received: from localhost.localdomain ([69.9.132.159]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2e97e5d7efcsm22402035ad.5.2026.10.17.09.57.10 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sat, 17 Oct 2026 09:57:12 -0700 (PDT) From: Tim Orling X-Google-Original-From: Tim Orling To: yocto-patches@lists.yoctoproject.org Cc: Tim Orling Subject: [layerindex-web][PATCH 1/3] layerindex/views: BranchCompareView: fix to_branch check Date: Sat, 17 Oct 2026 09:57:01 -0700 Message-ID: <20261017165703.70617-1-tim.orling@konsulko.com> X-Mailer: git-send-email 2.56.0 MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sat, 17 Oct 2026 16:57:22 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5036 The to_branch lookup was guarded by from_branch_id instead of to_branch_id, so a request with only from_branch set tried to fetch a Branch with id None and returned a 404 instead of the empty form. AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling --- layerindex/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layerindex/views.py b/layerindex/views.py index 4f8e64e..73a4f6a 100644 --- a/layerindex/views.py +++ b/layerindex/views.py @@ -1927,7 +1927,7 @@ class BranchCompareView(FormView): from_branch = get_object_or_404(Branch, id=from_branch_id) context['from_branch'] = from_branch to_branch = None - if from_branch_id is not None: + if to_branch_id is not None: to_branch = get_object_or_404(Branch, id=to_branch_id) context['to_branch'] = to_branch if from_branch and to_branch: From patchwork Sat Oct 17 16:57:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tim Orling X-Patchwork-Id: 101141 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 86472CCF2C1 for ; Sat, 17 Oct 2026 16:57:22 +0000 (UTC) Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.344.1792256237647988105 for ; Sat, 17 Oct 2026 09:57:17 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=gNlmIbFW; spf=pass (domain: gmail.com, ip: 209.85.214.174, mailfrom: ticotimo@gmail.com) Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2d6e954afbdso3154705ad.2 for ; Sat, 17 Oct 2026 09:57:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792256237; x=1792861037; darn=lists.yoctoproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=8wm9D5pauQn6BW4QYJe97LRVa6Fhbs6wxLAwXmHeAvk=; b=gNlmIbFW0cKaw/fQqs8CQ7OxHHUS53pos95YzH2e7KuJ+OLSMcZ+cqw7FiCjM2eVud A+ImrQH/wAoyC/QSK2WeIz6z9rWGL+ZjP7uCaatJg9/zh4HIdJuorLjO2t1EuK/jmwd0 cireJEJswB9HxEaS1G6Ns4jTBNgfAdGsZ0twNbs+nVttP5nxLkf6WIZFITFjk83DNoBs QFikqmBFPZJthFy1QNtBvNVixObV9FcAAtPZwDA5s2qHJiT7xrw80vSVYcDjOIyhz5IW qUxMfU8rNIs6Xm+gWoVDbIaMU5OXo7LVzQYG6PvjsL3eOEjSP6S/lRoLuL6MalNKf98m m/8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792256237; x=1792861037; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=8wm9D5pauQn6BW4QYJe97LRVa6Fhbs6wxLAwXmHeAvk=; b=yt1l9WYShIgVz1pZPl9BqUqPZewtSBjBOph+hqSb9YEe55Ln/GAgqalyiWdZ0E19P0 MvgS9gY1WqREg9tfrYfiPCssSTPPEZO3qYgwmtwui5k61i8Qn8J9GUbXsAO0vszsjTN4 b3AqcnKiiKN6AJJUWSOivvN2AKyY4y4+qQryhUS+wlzwncZZlot3aUTnUX3Pu9ObZKOy c5oGiamLT922CmGYUTebLFTw6YeYkAXQ2icTu9vWYwEO2eyPeOOnmrn8CFkVDbq1Cp1+ TzfmTXAdMuTMbEyosQe7WzkvMPvtZBeaIn1vj5zYJ8l2OXd5S6iJgVBFO89qhy56GvE0 CJoA== X-Gm-Message-State: AFq9FYLWmEmOa+9bTvuvKy8pI/Uge0FV9E5fHzYKQYlQvG8X6R2qXxpy nTdeYOv8GQczbUFfQUpjm4UusPB3BTWOWg5m/Ji3PRrc749PThGtBGF5hPEteA== X-Gm-Gg: AYBFou2XIsyXRzzZpbUHQnXQFg9dfLA3i3jhRhogjKADoEN3HlwlSXGL1Zgwvc9fG3R Zp3WIpKSrJq9uwCMYZ67LevBUTdHkYJTuc+/8dzNLJSsHz0h1PG2MZ/C8MPgwNGqRPEuacfHk/c +E7HGv4DYer2QJF0HbUryHQwj3zrQ3WHzEL1x2rKhVKsMi+yGym5tmA7vID8E2O8OIlJjJ3lbTF FLQ4ihg9tnGT2DRvMXBhpHimyPTGwWMgZ4xqq3Wbxfulopbi0XBbLnn+jm0fhkp2vfhJgLvaZTq 73bDGD1fiWXV1mjeQtq4LEW3/9m64vtK/1p+Lul6CTr4BqTx6EHkuSjGEDpJg/xuq14oJgIoHpk 6gDa2Pw2likO6h/N1hYA5luJNUQMEtARE+RafimrsPgGwkjp6EJ5qD2ssDkJcH3mFBDpnF94uJe F0rFG4j239JY4VBLur/GSTFpbi9Vu76aqGlZdJ72mZ9FbGadc0faiQxkY80bVXfj5vyBB64HP0g z+UONM/Vl4Mn1FNKOlZ52l2zQZoObY= X-Received: by 2002:a17:902:e949:b0:2e2:d02d:a173 with SMTP id d9443c01a7336-2e9a6f59777mr28720925ad.49.1792256236956; Sat, 17 Oct 2026 09:57:16 -0700 (PDT) Received: from localhost.localdomain ([69.9.132.159]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2e97e5d7efcsm22402035ad.5.2026.10.17.09.57.15 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sat, 17 Oct 2026 09:57:16 -0700 (PDT) From: Tim Orling X-Google-Original-From: Tim Orling To: yocto-patches@lists.yoctoproject.org Cc: Tim Orling Subject: [layerindex-web][PATCH 2/3] branchcompare.html: don't submit layer checkboxes Date: Sat, 17 Oct 2026 09:57:02 -0700 Message-ID: <20261017165703.70617-2-tim.orling@konsulko.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261017165703.70617-1-tim.orling@konsulko.com> References: <20261017165703.70617-1-tim.orling@konsulko.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sat, 17 Oct 2026 16:57:22 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5037 The selected layers are submitted via the hidden 'layers' field, which is filled in from the checkboxes when the layer dialog is closed. The checkboxes themselves were also named 'l', so every comparison URL carried a redundant l= parameter for each layer that the view never used. Drop the name so only 'layers' is submitted. AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling --- templates/layerindex/branchcompare.html | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/layerindex/branchcompare.html b/templates/layerindex/branchcompare.html index 83561fd..c9fe5f5 100644 --- a/templates/layerindex/branchcompare.html +++ b/templates/layerindex/branchcompare.html @@ -45,7 +45,6 @@ X-Patchwork-Id: 101142 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5E54FCCF2C4 for ; Sat, 17 Oct 2026 16:57:32 +0000 (UTC) Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.346.1792256242519619274 for ; Sat, 17 Oct 2026 09:57:22 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=oQiim6Av; spf=pass (domain: gmail.com, ip: 209.85.214.174, mailfrom: ticotimo@gmail.com) Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2e95bd1c14cso6763265ad.2 for ; Sat, 17 Oct 2026 09:57:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792256242; x=1792861042; darn=lists.yoctoproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=VpLo3/AAnt/GG89GZORS4fQcT+goZrlo5Fzhwz8Re3M=; b=oQiim6Av6PjIFJvVYVhWkmc/jk7iiDTJIpn4qxdB2pGVw4+3AsNq5JxZUm8URDGCdg BpTuE2IXycJY8qIZ50Lth3huedsCZws7VhrTh8Pa/9SYwonHFl5XdBvnbkg6LAHdvW7Q 0DFYEeoTfqNNFKjJuH6gYRqBH9jos7OMN3/6xvM1YfSkO4KYOIxmriIsq8VbaDb75zJQ 9y8idmDNwLa34RC1Vny/cbfJyDEgFJCjvYPhN3xHVNw0PZ9jAT8qT5ixx3KHAOvvPjvJ JZzUnBVOBTrSvo6kB74RItqdRmLdS5GE0gTa1C5N48sC3jaLYKEi3Tpdos3U5Tx5V6DM Ay6w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792256242; x=1792861042; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=VpLo3/AAnt/GG89GZORS4fQcT+goZrlo5Fzhwz8Re3M=; b=pHiDnTnOQQWLN7qwExYUu/ZyhllgTqqHux8KREc5dya5cbdHYKAfw5X/gFSe0S+uqN Fw6ZdeD1BdGTikWZ18pRxvjiIirmzEI3XkcfbhgNZ3NtjlGFf7vF8/1OKSC0n+ROuFGT ZPpMw4aqvbs4zoImlKGPKvru9xUAj6IuVHnZelKcVXSOmZhgH3wZj62XDJsJBwjmJjX4 0xYnMCejSHQ1dVyWcKanb6I/ItSnX1+hcjU1UAWMUDe/VwaIuYUm2ImaO6uJtXkLtL9Y +m6x+jci5lbNtPbPOjw2Sq+24BVkfkJUOTflB4PEKvpEpp0iSdqa3+vXU3VJueRsYcOg Zi0Q== X-Gm-Message-State: AFq9FYKZadufyNTPGZxFaJJOeE1BYcmICP27MhhHkMuPwZCTABrcaBY9 VxYHppOQNYUItWYqoFG0a8LkNwLAO2RxwaX5gKKrdkOxtTAzBCB2J53nJU1FcQ== X-Gm-Gg: AYBFou2ZcXqMPQjeafFbWpn9nWesU4lo7e5x58zHntUYT3d+av/C1OZ/zyO+/U0rYoX yFiS9KsoHBfbqkzY1M06dcI7RWwhHu/hwZ7pDaHUIzxeKMLPbjpRlY5w7P17pXJvzKks9IoO3aH 0jcrNOsJVm49XnnhSJ1hVLpE0KTi0pRoYxhK1rS47vsqSG5ln+e/Vw/sjIbH30AcSTCJy6Ub+Rn YXa+xgtJahmZzBW+G669xWZCmZDOtKb7Ot6/yW6f3YvrpBbYkFGHuxG8n8XaA+X0mdEJOvPk2ve ukC4AbZLtvtUJY8N12lvsCYNrZLA9AvdfBF80px+7oWhjWUmUiUjlXLvPYOoH3ECX/NOVu4xXHC NyA7mx8gqHRJsrBJ9DNrEvTegzrE1SdCXRVSu8Eje015+CMIDZZtHs+QW6J4Hrcj4erzWQ8gJFN B0KvBNNFG3t5KZD8PnGR9/mYzhbcJTLWez0BDLyT8Ydhz7j1J5t84l3X9rw92TfY0I6wW774F3I VdRg4BVa2i73R/+420aeNhBw9Q0+1w= X-Received: by 2002:a17:903:22c3:b0:2e9:9846:2da7 with SMTP id d9443c01a7336-2e9a68437f0mr31887775ad.5.1792256241775; Sat, 17 Oct 2026 09:57:21 -0700 (PDT) Received: from localhost.localdomain ([69.9.132.159]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2e97e5d7efcsm22402035ad.5.2026.10.17.09.57.20 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sat, 17 Oct 2026 09:57:20 -0700 (PDT) From: Tim Orling X-Google-Original-From: Tim Orling To: yocto-patches@lists.yoctoproject.org Cc: Tim Orling Subject: [layerindex-web][PATCH 3/3] layerindex/views: BranchCompareView: parse layers as a list of ids Date: Sat, 17 Oct 2026 09:57:03 -0700 Message-ID: <20261017165703.70617-3-tim.orling@konsulko.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261017165703.70617-1-tim.orling@konsulko.com> References: <20261017165703.70617-1-tim.orling@konsulko.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Sat, 17 Oct 2026 16:57:32 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/5038 The 'layers' parameter is a comma-separated list of layer ids, but the view used the raw string as the list and iterated over it one character at a time. As a result: * layers=59 compared layers 5 and 9 instead of layer 59 * layers=59,1 tried to look up a layer with id ',' and raised a ValueError (Internal Server Error) * the "Filter layers" badge showed the length of the string rather than the number of layers (e.g. 3 for "2,1") * the selected layers were not pre-checked in the layer dialog, since integer ids were being compared against a string Split the parameter into integer ids (also accepting it repeated, as in layers=59&layers=1), ignore anything that is not a published layer id, and use the cleaned list for the hidden form field, the badge and the text/rST/CSV output. Add tests covering these cases. Fixes [YOCTO #15332] AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling --- layerindex/views.py | 19 +++++- tests/test_branch_compare_view.py | 109 ++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 tests/test_branch_compare_view.py diff --git a/layerindex/views.py b/layerindex/views.py index 73a4f6a..dea0a98 100644 --- a/layerindex/views.py +++ b/layerindex/views.py @@ -1902,6 +1902,19 @@ class BranchCompareView(FormView): changed.append(recipe) return added, changed, removed + def get_layer_ids(self): + # 'layers' is a comma-separated list of layer ids (also accept it + # repeated); ignore anything that isn't a selectable layer + valid_ids = set(LayerItem.objects.filter(status__in=['P', 'X']).values_list('id', flat=True)) + values = self.request.GET.getlist('layers') or [str(LayerItem.objects.get(name=settings.CORE_LAYER_NAME).id)] + layer_ids = [] + for value in values: + for layer_id in value.split(','): + layer_id = layer_id.strip() + if layer_id.isdigit() and int(layer_id) in valid_ids and int(layer_id) not in layer_ids: + layer_ids.append(int(layer_id)) + return layer_ids + def form_valid(self, form): return HttpResponseRedirect(reverse_lazy('branch_comparison', args=(form.cleaned_data['from_branch'].name, form.cleaned_data['to_branch'].name))) @@ -1913,7 +1926,7 @@ class BranchCompareView(FormView): to_branch_id = self.request.GET.get('to_branch', None) if to_branch_id is not None: initial['to_branch'] = get_object_or_404(Branch, id=to_branch_id) - initial['layers'] = self.request.GET.get('layers', str(LayerItem.objects.get(name=settings.CORE_LAYER_NAME).id)) + initial['layers'] = ','.join(str(i) for i in self.get_layer_ids()) return initial def get_context_data(self, **kwargs): @@ -1921,7 +1934,7 @@ class BranchCompareView(FormView): from_branch_id = self.request.GET.get('from_branch', None) to_branch_id = self.request.GET.get('to_branch', None) - layer_ids = self.request.GET.get('layers', str(LayerItem.objects.get(name=settings.CORE_LAYER_NAME).id)) + layer_ids = self.get_layer_ids() from_branch = None if from_branch_id is not None: from_branch = get_object_or_404(Branch, id=from_branch_id) @@ -1936,7 +1949,7 @@ class BranchCompareView(FormView): context['layers'] = LayerItem.objects.filter(status__in=['P', 'X']).order_by('name') context['showlayers'] = layer_ids layerlist = dict(context['layers'].values_list('id', 'name')) - context['showlayers_text'] = ', '.join([layerlist[int(i)] for i in layer_ids]) + context['showlayers_text'] = ', '.join([layerlist[i] for i in layer_ids]) return context diff --git a/tests/test_branch_compare_view.py b/tests/test_branch_compare_view.py new file mode 100644 index 0000000..6211c5f --- /dev/null +++ b/tests/test_branch_compare_view.py @@ -0,0 +1,109 @@ +# layerindex-web - tests for BranchCompareView +# +# Copyright (C) 2026 Tim Orling +# +# Licensed under the MIT license, see COPYING.MIT for details +# +# SPDX-License-Identifier: MIT + +# Tests for bug #15332 - Branch Comparison throws Internal Server Error +# https://bugzilla.yoctoproject.org/show_bug.cgi?id=15332 +# +# The 'layers' parameter is a comma-separated list of layer ids, but the view +# iterated over the raw string character by character. So layers=59 selected +# layers 5 and 9, and layers=59,1 tried to look up a layer with id ','. + +import pytest +from django.test import TestCase +from django.urls import reverse + +from layerindex.models import Branch, LayerItem, LayerBranch, Recipe + + +@pytest.mark.django_db +class TestBranchCompareView(TestCase): + """Tests for BranchCompareView layer selection (bug #15332).""" + + def setUp(self): + self.from_branch = Branch.objects.create( + name='old', bitbake_branch='old', short_description='Old', + sort_priority=2) + self.to_branch = Branch.objects.create( + name='new', bitbake_branch='new', short_description='New', + sort_priority=1) + + # Explicit ids so that '59' would be misread as layers 5 and 9 + self.layers = {} + for layer_id, name in ((1, 'openembedded-core'), (5, 'meta-five'), + (9, 'meta-nine'), (59, 'meta-fiftynine')): + layer = LayerItem.objects.create( + id=layer_id, name=name, status='P', layer_type='S', + summary=name, description=name, + vcs_url='git://example.com/%s.git' % name) + self.layers[layer_id] = layer + for branch, pv in ((self.from_branch, '1.0'), (self.to_branch, '2.0')): + lb = LayerBranch.objects.create(layer=layer, branch=branch) + Recipe.objects.create( + layerbranch=lb, filename='%s_%s.bb' % (name, pv), + pn='%s-recipe' % name, pv=pv, filepath='recipes-test') + + def _get(self, query='', url_name='branch_comparison'): + url = '%s?from_branch=%d&to_branch=%d%s' % ( + reverse(url_name), self.from_branch.id, self.to_branch.id, query) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + return response + + def _changed_pns(self, response): + return sorted(r.pn for r in response.context['changed']) + + def test_default_is_core_layer(self): + response = self._get() + self.assertEqual(self._changed_pns(response), ['openembedded-core-recipe']) + + def test_multi_digit_layer_id(self): + response = self._get('&layers=59') + self.assertEqual(self._changed_pns(response), ['meta-fiftynine-recipe']) + self.assertEqual(response.context['showlayers_text'], 'meta-fiftynine') + + def test_multiple_layers(self): + response = self._get('&layers=59%2C1') + self.assertEqual(self._changed_pns(response), + ['meta-fiftynine-recipe', 'openembedded-core-recipe']) + + def test_repeated_layers_param(self): + response = self._get('&layers=59&layers=1') + self.assertEqual(self._changed_pns(response), + ['meta-fiftynine-recipe', 'openembedded-core-recipe']) + + def test_legacy_url_with_l_params(self): + # URL as previously generated by the form, with the checkbox values + # also submitted as 'l' + response = self._get('&layers=59%2C1&l=59&l=1') + self.assertEqual(self._changed_pns(response), + ['meta-fiftynine-recipe', 'openembedded-core-recipe']) + + def test_invalid_layer_ids_ignored(self): + response = self._get('&layers=59%2C%2Cbogus%2C9999') + self.assertEqual(self._changed_pns(response), ['meta-fiftynine-recipe']) + + def test_badge_and_checkboxes_match_selection(self): + response = self._get('&layers=59%2C1') + self.assertContains(response, 'id="id_layers_count">2', html=False) + content = response.content.decode() + for layer_id in (1, 59): + self.assertRegex(content, r'id="id_layercheckbox_%d"\s+checked' % layer_id) + for layer_id in (5, 9): + self.assertNotRegex(content, r'id="id_layercheckbox_%d"\s+checked' % layer_id) + # Checkboxes must not be submitted alongside the hidden 'layers' field + self.assertNotContains(response, 'name="l"') + + def test_plain_text_output(self): + response = self._get('&layers=59%2C1', url_name='branch_comparison_plain') + self.assertContains(response, '(layers: meta-fiftynine, openembedded-core)') + + def test_from_branch_only(self): + url = '%s?from_branch=%d' % (reverse('branch_comparison'), self.from_branch.id) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertIsNone(response.context['to_branch'])