- 第三栏内容工厂:按拖入类型展示动作,动作按钮本身可接收 drop - 链接分类:http(s) 按扩展名判定图片/音视频/文档/压缩包/网页 - http 链接动作:打开、下载、另存为、复制、Markdown、复制文件名、待办、问小埃 - 非标准 scheme:尝试打开、复制;thunder 解析 base64 真实地址,magnet 解析 dn - 文字动作:ICU 分词、正则提取链接、朗读(spd-say)、复制、待办 - 文件/图片动作:打开、提取文字(占位)、生成描述、保存图片、复制路径、待办 - Downloader:异步下载到 XDG 下载目录 / 另存为,面板 toast 提示 - TodoModel:持久化到 todo.json,供「添加到待办」使用 - 无头自测 gxde-ai-see-selftest:链接分类/分词/提取链接/动作匹配 - 合成器 portal 自测脚本 tests/portal(虚拟指针 + GTK 源/目标)
110 lines
4.5 KiB
C++
110 lines
4.5 KiB
C++
// SPDX-FileCopyrightText: 2026 GXDE OS Team
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// Headless checks for the pure (non widget) parts of the content factory.
|
|
|
|
#include <QCoreApplication>
|
|
#include <QDebug>
|
|
#include <QTextStream>
|
|
|
|
#include "actions/texttools.h"
|
|
#include "factory/contentclassifier.h"
|
|
#include "factory/factoryactions.h"
|
|
#include "factory/linktools.h"
|
|
|
|
static int failures = 0;
|
|
|
|
static void check(bool condition, const QString &what)
|
|
{
|
|
QTextStream out(stdout);
|
|
if (condition) {
|
|
out << "PASS " << what << "\n";
|
|
} else {
|
|
out << "FAIL " << what << "\n";
|
|
++failures;
|
|
}
|
|
}
|
|
|
|
static bool hasAction(const QVector<FactoryAction> &actions, const QString &id)
|
|
{
|
|
for (const FactoryAction &action : actions) {
|
|
if (action.id == id) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QCoreApplication app(argc, argv);
|
|
|
|
// http link with a video extension
|
|
const LinkInfo video = LinkTools::parse(QStringLiteral("https://example.com/media/clip.mp4"));
|
|
check(video.kind == LinkInfo::Http, "https link recognised");
|
|
check(video.extension == QStringLiteral("mp4"), "mp4 extension parsed");
|
|
check(video.fileType == QStringLiteral("video"), "mp4 classified as video");
|
|
check(hasAction(actionsFor(ContentClassifier::classifyText(QStringLiteral(
|
|
"https://example.com/media/clip.mp4"))),
|
|
QStringLiteral("download")),
|
|
"video link offers download");
|
|
check(hasAction(actionsFor(ContentClassifier::classifyText(QStringLiteral(
|
|
"https://example.com/media/clip.mp4"))),
|
|
QStringLiteral("play")),
|
|
"video link offers play");
|
|
|
|
const LinkInfo image = LinkTools::parse(QStringLiteral("https://example.com/a.png?x=1"));
|
|
check(image.fileType == QStringLiteral("image"), "png classified as image");
|
|
|
|
const LinkInfo page = LinkTools::parse(QStringLiteral("https://example.com/article"));
|
|
check(page.fileType == QStringLiteral("page"), "extensionless url is a page");
|
|
|
|
// thunder:// -> base64("AA" + url + "ZZ")
|
|
const QByteArray thunderPayload =
|
|
QByteArray("AA") + "http://example.com/file.zip" + QByteArray("ZZ");
|
|
const QString thunder =
|
|
QStringLiteral("thunder://") + QString::fromLatin1(thunderPayload.toBase64());
|
|
const LinkInfo thunderInfo = LinkTools::parse(thunder);
|
|
check(thunderInfo.kind == LinkInfo::Custom, "thunder link classified as custom");
|
|
check(thunderInfo.decodedUrl == QStringLiteral("http://example.com/file.zip"),
|
|
"thunder payload decoded");
|
|
check(hasAction(actionsFor(ContentClassifier::classifyText(thunder)),
|
|
QStringLiteral("decode")),
|
|
"thunder offers decode action");
|
|
|
|
// magnet
|
|
const LinkInfo magnet =
|
|
LinkTools::parse(QStringLiteral("magnet:?xt=urn:btih:abc&dn=Some%20File"));
|
|
check(magnet.kind == LinkInfo::Custom, "magnet classified as custom");
|
|
check(magnet.displayName == QStringLiteral("Some File"), "magnet display name parsed");
|
|
|
|
// spk://
|
|
const LinkInfo spk = LinkTools::parse(QStringLiteral("spk://store/app/com.example"));
|
|
check(spk.kind == LinkInfo::Custom, "spk link classified as custom");
|
|
|
|
// plain text is not a link
|
|
const LinkInfo text = LinkTools::parse(QStringLiteral("hello world"));
|
|
check(!text.isValid(), "plain text is not a link");
|
|
|
|
// segmentation
|
|
const QStringList words = TextTools::segment(QStringLiteral("我爱北京天安门"));
|
|
check(words.size() >= 3, "segmentation produced words");
|
|
check(words.contains(QStringLiteral("北京")), "segmentation keeps 北京 together");
|
|
|
|
// link extraction
|
|
const QStringList links = TextTools::extractLinks(
|
|
QStringLiteral("看 https://a.example/x?y=1 和 test@example.com,还有 www.b.example"));
|
|
check(links.size() == 3, QStringLiteral("extractLinks found 3 links, got %1").arg(links.size()));
|
|
|
|
// classifier
|
|
const DropContent textContent = ContentClassifier::classifyText(QStringLiteral("just words"));
|
|
check(textContent.kind == DropContent::Text, "text classified as text");
|
|
const DropContent linkContent =
|
|
ContentClassifier::classifyText(QStringLiteral("https://example.com/a.zip"));
|
|
check(linkContent.kind == DropContent::Link, "link classified as link");
|
|
check(hasAction(actionsFor(linkContent), QStringLiteral("save_as")), "link offers save as");
|
|
|
|
QTextStream(stdout) << (failures == 0 ? "ALL PASS\n" : "FAILURES\n");
|
|
return failures == 0 ? 0 : 1;
|
|
}
|