Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ TEST = \
${GOPKG_PREFIX}/clipboard1 \
${GOPKG_PREFIX}/clipboard1/mocks \
${GOPKG_PREFIX}/common/bluetooth \
${GOPKG_PREFIX}/common/cpuinfo \
${GOPKG_PREFIX}/common/scale \
${GOPKG_PREFIX}/common/dsync \
${GOPKG_PREFIX}/common/sessionmsg \
${GOPKG_PREFIX}/dbus \
Expand All @@ -51,6 +53,7 @@ TEST = \
${GOPKG_PREFIX}/grub_common \
${GOPKG_PREFIX}/grub_gfx \
${GOPKG_PREFIX}/housekeeping \
${GOPKG_PREFIX}/image_effect1 \
${GOPKG_PREFIX}/inputdevices1 \
${GOPKG_PREFIX}/inputdevices1/iso639 \
${GOPKG_PREFIX}/keybinding1 \
Expand Down Expand Up @@ -88,6 +91,9 @@ TEST = \
${GOPKG_PREFIX}/x_event_monitor1 \
${GOPKG_PREFIX}/bin/default-file-manager \
${GOPKG_PREFIX}/display1 \
${GOPKG_PREFIX}/display1/brightness \
${GOPKG_PREFIX}/display1/utils \
${GOPKG_PREFIX}/common/fileutil \
#${GOPKG_PREFIX}/timedate1/zoneinfo \

BINARIES = \
Expand Down
112 changes: 112 additions & 0 deletions calltrace/calltrace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

package calltrace

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestStoi64(t *testing.T) {
assert.Equal(t, int64(123), stoi64("123"))
assert.Equal(t, int64(0), stoi64(""))
assert.Equal(t, int64(0), stoi64("abc"))
assert.Equal(t, int64(-42), stoi64("-42"))
}

func TestCpuTimeInfo_Total(t *testing.T) {
info := &cpuTimeInfo{
utime: 10,
stime: 20,
cutime: 5,
cstime: 3,
nice: 2,
start: 100,
hertz: 100,
}
assert.Equal(t, int64(40), info.Total())
}

func TestCpuTimeInfo_Total_Zero(t *testing.T) {
info := &cpuTimeInfo{}
assert.Equal(t, int64(0), info.Total())
}

func TestCpuTimeInfo_Percentage(t *testing.T) {
// start=200, hertz=100 → start/hertz=2, seconds = uptime - 2
// Total=100, Total/hertz=1, Percentage = 100 * (1 / (uptime-2))
info := &cpuTimeInfo{
utime: 50,
stime: 50,
cutime: 0,
cstime: 0,
nice: 0,
start: 200,
hertz: 100,
}
// uptime comes from /proc/uptime; can't control, but we can verify
// the formula doesn't panic and returns a float (likely 0 in test env
// because /proc/uptime read may fail or return 0 → division by zero).
// Instead test the pure arithmetic directly:
assert.Equal(t, int64(100), info.Total())
}

func TestGetInterge(t *testing.T) {
// format: "RssAnon: 100 kB" → split → ["RssAnon:", "100", "kB"]
v, err := getInterge("RssAnon: 100 kB")
assert.NoError(t, err)
assert.Equal(t, int64(100), v)

v, err = getInterge("VmPTE: 8 kB")
assert.NoError(t, err)
assert.Equal(t, int64(8), v)
}

func TestGetInterge_BadFormat(t *testing.T) {
// 只有两个字段(不足 3 个)→ error
_, err := getInterge("VmPTE: 100")
assert.Error(t, err)

// 中间不是数字 → error
_, err = getInterge("VmPTE: abc kB")
assert.Error(t, err)
}

func TestSumMemByFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "status")
content := "Name:\ttest\n" +
"RssAnon: 100 kB\n" +
"VmPTE: 8 kB\n" +
"VmPMD: 2 kB\n" +
"Other: 999 kB\n"
require.NoError(t, os.WriteFile(path, []byte(content), 0600))

total, err := sumMemByFile(path)
assert.NoError(t, err)
// 100 + 8 + 2 = 110
assert.Equal(t, int64(110), total)
}

func TestSumMemByFile_MissingFields(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "status")
// 只有 RssAnon,缺 VmPTE/VmPMD → 只累加 RssAnon
content := "RssAnon: 50 kB\n"
require.NoError(t, os.WriteFile(path, []byte(content), 0600))

total, err := sumMemByFile(path)
assert.NoError(t, err)
assert.Equal(t, int64(50), total)
}

func TestSumMemByFile_NonExistent(t *testing.T) {
_, err := sumMemByFile("/nonexistent/path/to/status")
assert.Error(t, err)
}
126 changes: 126 additions & 0 deletions common/fileutil/fileutil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

package fileutil

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestSafeReadFile_RegularFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "data.txt")
content := []byte("hello dde-daemon")
require.NoError(t, os.WriteFile(path, content, 0600))

got, err := SafeReadFile(path)
assert.NoError(t, err)
assert.Equal(t, content, got)
}

func TestSafeReadFile_NonExistent(t *testing.T) {
dir := t.TempDir()
_, err := SafeReadFile(filepath.Join(dir, "missing"))
assert.Error(t, err)
}

func TestSafeReadFile_SymlinkRejected(t *testing.T) {
dir := t.TempDir()
target := filepath.Join(dir, "target")
require.NoError(t, os.WriteFile(target, []byte("x"), 0600))
link := filepath.Join(dir, "link")
require.NoError(t, os.Symlink(target, link))

_, err := SafeReadFile(link)
assert.Error(t, err)
assert.Contains(t, err.Error(), "symlink")
}

func TestSafeReadFile_DirectoryRejected(t *testing.T) {
dir := t.TempDir()
// 目录不是普通文件,应被拒绝
_, err := SafeReadFile(dir)
assert.Error(t, err)
assert.Contains(t, err.Error(), "not a regular file")
}

func TestSafeWriteFile_CreateNew(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "new.txt")
content := []byte("fresh content")

err := SafeWriteFile(path, content, 0600)
assert.NoError(t, err)

// 权限:owner 读写位必置(用掩码校验,对仅清 group/other 位的 umask 稳健)
info, err := os.Stat(path)
require.NoError(t, err)
assert.True(t, info.Mode().IsRegular())
assert.Equal(t, os.FileMode(0600), info.Mode().Perm()&os.FileMode(0600))

got, err := SafeReadFile(path)
assert.NoError(t, err)
assert.Equal(t, content, got)
}

func TestSafeWriteFile_OverwriteRegular(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "exists.txt")
require.NoError(t, os.WriteFile(path, []byte("old"), 0600))

err := SafeWriteFile(path, []byte("new"), 0600)
assert.NoError(t, err)

got, err := SafeReadFile(path)
assert.NoError(t, err)
assert.Equal(t, []byte("new"), got)
}

func TestSafeWriteFile_SymlinkRejected(t *testing.T) {
dir := t.TempDir()
target := filepath.Join(dir, "target")
require.NoError(t, os.WriteFile(target, []byte("x"), 0600))
link := filepath.Join(dir, "link")
require.NoError(t, os.Symlink(target, link))

err := SafeWriteFile(link, []byte("y"), 0600)
assert.Error(t, err)
assert.Contains(t, err.Error(), "symlink")

// 被拒绝后,符号链接目标内容不应被改写
got, err := os.ReadFile(target)
require.NoError(t, err)
assert.Equal(t, []byte("x"), got)
}

func TestSafeWriteFile_DirectoryRejected(t *testing.T) {
// 目标已存在且为目录(非普通文件),写侧应拒绝,与读侧对称
dir := t.TempDir()
err := SafeWriteFile(dir, []byte("x"), 0600)
assert.Error(t, err)
assert.Contains(t, err.Error(), "not a regular file")
}

func TestSafeWriteFile_NonExistentParent(t *testing.T) {
dir := t.TempDir()
// 父目录不存在,O_CREAT|O_EXCL 创建应失败
err := SafeWriteFile(filepath.Join(dir, "sub", "file"), []byte("z"), 0600)
assert.Error(t, err)
}

func TestSafeWriteFile_RoundTrip(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "round.txt")
payload := []byte{0x00, 0x01, 0xFF, 'A', '\n', '\t'}

require.NoError(t, SafeWriteFile(path, payload, 0600))
got, err := SafeReadFile(path)
assert.NoError(t, err)
assert.Equal(t, payload, got)
}
125 changes: 125 additions & 0 deletions common/sessionmsg/sessionmsg_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

package sessionmsg

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestBodyNotify_MessageType(t *testing.T) {
var b BodyNotify
assert.Equal(t, MessageTypeNotify, b.MessageType())
}

func TestNewMessage_Notify(t *testing.T) {
body := &BodyNotify{Icon: "dialog-info", AppName: "test-app"}
m := NewMessage(true, body)

assert.Equal(t, MessageTypeNotify, m.Type)
assert.True(t, m.OnlyActive)
assert.True(t, m.Body == body)
}

func TestNewMessage_OnlyActiveFalse(t *testing.T) {
body := &BodyNotify{Icon: "x"}
m := NewMessage(false, body)

assert.False(t, m.OnlyActive)
assert.Equal(t, MessageTypeNotify, m.Type)
assert.True(t, m.Body == body)
}

func TestMessage_UnmarshalJSON_Notify(t *testing.T) {
raw := `{"OnlyActive":true,"Type":1,"Body":{"Icon":"dialog-info","AppName":"test-app","ExpireTimeout":5000}}`
var m Message
err := m.UnmarshalJSON([]byte(raw))
require.NoError(t, err)

assert.Equal(t, MessageTypeNotify, m.Type)
assert.True(t, m.OnlyActive)

bn, ok := m.Body.(*BodyNotify)
assert.True(t, ok, "Body should be *BodyNotify")
assert.Equal(t, "dialog-info", bn.Icon)
assert.Equal(t, "test-app", bn.AppName)
assert.Equal(t, 5000, bn.ExpireTimeout)
}

func TestMessage_UnmarshalJSON_UnknownType(t *testing.T) {
// Type=99 不是已知类型,switch 不匹配,Body 保持 nil,无错误
raw := `{"OnlyActive":false,"Type":99,"Body":{}}`
var m Message
err := m.UnmarshalJSON([]byte(raw))
assert.NoError(t, err)
assert.Equal(t, MessageType(99), m.Type)
assert.False(t, m.OnlyActive)
assert.Nil(t, m.Body)
}

func TestMessage_UnmarshalJSON_InvalidJSON(t *testing.T) {
raw := `{invalid json`
var m Message
err := m.UnmarshalJSON([]byte(raw))
assert.Error(t, err)
}

func TestMessage_UnmarshalJSON_BodyUnmarshalError(t *testing.T) {
// Type=1 触发 BodyNotify 解析,但 ExpireTimeout 传字符串无法转为 int
raw := `{"OnlyActive":true,"Type":1,"Body":{"ExpireTimeout":"not-a-number"}}`
var m Message
err := m.UnmarshalJSON([]byte(raw))
assert.Error(t, err)
assert.Nil(t, m.Body)
}

func TestMessage_MarshalUnmarshal_RoundTrip(t *testing.T) {
orig := NewMessage(true, &BodyNotify{
Icon: "dialog-warning",
AppName: "roundtrip-app",
ExpireTimeout: -1,
})
data, err := json.Marshal(orig)
require.NoError(t, err)

var decoded Message
err = decoded.UnmarshalJSON(data)
require.NoError(t, err)
assert.Equal(t, orig.Type, decoded.Type)
assert.Equal(t, orig.OnlyActive, decoded.OnlyActive)

bn, ok := decoded.Body.(*BodyNotify)
assert.True(t, ok)
assert.Equal(t, "dialog-warning", bn.Icon)
assert.Equal(t, "roundtrip-app", bn.AppName)
assert.Equal(t, -1, bn.ExpireTimeout)
}

func TestLocalizeStr_String_Nil(t *testing.T) {
var ls *LocalizeStr
assert.Equal(t, "", ls.String())
}

func TestLocalizeStr_String_NoArgs(t *testing.T) {
ls := &LocalizeStr{Format: "hello world"}
assert.Equal(t, "hello world", ls.String())
}

func TestLocalizeStr_String_WithArgs(t *testing.T) {
ls := &LocalizeStr{Format: "Hello %s, welcome to %s", Args: []string{"Alice", "DDE"}}
assert.Equal(t, "Hello Alice, welcome to DDE", ls.String())
}

func TestLocalizeStr_String_SingleArg(t *testing.T) {
ls := &LocalizeStr{Format: "value is %s", Args: []string{"42"}}
assert.Equal(t, "value is 42", ls.String())
}

func TestMessageType_Constants(t *testing.T) {
assert.Equal(t, MessageType(1), MessageTypeNotify)
}
Loading
Loading