Skip to content

Conversation

@electricface
Copy link
Member

@electricface electricface commented Jan 30, 2026

  • Removed code related to CPU count retrieval; now consistently returns CPU model information without quantity
  • Modified the Processor string to append frequency information, preventing duplicate frequency entries
  • Updated relevant test cases by removing assertions for CPU count and verifying correct frequency appending

fix(systeminfo): 删除CPU计数并增加频率补充逻辑

  • 移除获取CPU数量相关代码,统一返回无数量的CPU型号信息
  • 修改Processor字符串补充频率的实现,避免重复添加频率信息
  • 更新相关测试用例,去除对CPU数量的断言,验证频率补充正确
    PMS: BUG-274991
    Influence: 控制中心->系统->关于本机->处理器

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 30, 2026

Reviewer's Guide

Refines how CPU/processor information is reported by removing CPU count suffixes from processor strings, centralizing CPU info initialization, and introducing a helper to supplement missing frequency data using current or max CPU speed, with tests updated accordingly.

Sequence diagram for SystemInfo initialization with supplemented CPU frequency

sequenceDiagram
    participant SystemInfo
    participant CPUInfoModule
    participant LscpuRunner
    participant FrequencySupplementHelper

    SystemInfo->>SystemInfo: init()
    SystemInfo->>SystemInfo: getVersion()
    SystemInfo-->>SystemInfo: Version
    SystemInfo->>SystemInfo: getMemInfo()
    SystemInfo-->>SystemInfo: Memory fields
    SystemInfo->>SystemInfo: getDiskCapacity()
    SystemInfo-->>SystemInfo: Disk fields

    SystemInfo->>CPUInfoModule: GetCPUInfo(/proc/cpuinfo)
    CPUInfoModule-->>SystemInfo: Processor

    SystemInfo->>LscpuRunner: runLscpu()
    LscpuRunner-->>SystemInfo: lscpuRes (map)
    SystemInfo->>CPUInfoModule: getCPUMaxMHzByLscpu(lscpuRes)
    CPUInfoModule-->>SystemInfo: CPUMaxMHz
    SystemInfo->>CPUInfoModule: getCPUMHzCurrent(lscpuRes)
    CPUInfoModule-->>SystemInfo: CurrentSpeed

    SystemInfo->>FrequencySupplementHelper: supplementProcessorFrequency(Processor, CurrentSpeed, CPUMaxMHz)
    FrequencySupplementHelper-->>SystemInfo: Processor (with frequency if missing)
Loading

Class diagram for updated SystemInfo CPU initialization and helpers

classDiagram
    class SystemInfo {
        +string Processor
        +string Version
        +uint64 CurrentSpeed
        +float64 CPUMaxMHz
        +init()
        +isValidity() bool
    }

    class CPUInfoModule {
        +GetCPUInfo(path string) string
        +getProcessorByLscpuExt(data map_string_string, freq float64) (string, error)
        +getProcessorByLscpu(data map_string_string) (string, error)
        +getCPUMaxMHzByLscpu(data map_string_string) (float64, error)
        +swCPUInfo(data map_string_string) string
        +hwKirinCPUInfo(data map_string_string) string
        +getCPUInfoFromMap(nameKey string, numKey string, data map_string_string) (string, error)
        +getCPUName(key string, data map_string_string) (string, error)
        +getCPUHz(key string, data map_string_string) (float64, error)
    }

    class FrequencySupplementHelper {
        +supplementProcessorFrequency(processor string, currentSpeed uint64, cpuMaxMHz float64) string
    }

    SystemInfo --> CPUInfoModule : uses
    SystemInfo --> FrequencySupplementHelper : uses

    class RemovedCPUCountLogic {
        -getCPUNumber(key string, data map_string_string) (int, error)
        -lscpuKeyCount : string
    }

    CPUInfoModule ..|> RemovedCPUCountLogic : CPU count logic removed
Loading

File-Level Changes

Change Details Files
Centralize CPU info initialization and supplement missing processor frequency using runtime speed data.
  • Move Processor initialization in SystemInfo.init() to after disk/memory initialization so that it can rely on populated CPU speed fields.
  • Introduce supplementProcessorFrequency to append '@ GHz' to Processor when missing, preferring CurrentSpeed over CPUMaxMHz and avoiding duplication when frequency already present.
  • Invoke supplementProcessorFrequency at the end of SystemInfo.init() to normalize Processor across different data sources.
systeminfo1/info.go
Remove CPU core count suffix from processor strings and simplify lscpu-based CPU formatting.
  • Drop usage of lscpu 'CPU(s)' field and associated parsing, so processor strings no longer include 'x '.
  • Simplify getProcessorByLscpuExt/getProcessorByLscpu to only include model name and optional frequency, not CPU count.
  • Remove helper getCPUNumber and all call sites that appended 'x ' in various CPU info builders (swCPUInfo, hwKirinCPUInfo, getCPUInfoFromMap).
systeminfo1/cpu.go
Align tests with new CPU string format and add cache round-trip test using doSaveCache/doReadCache.
  • Update CPU-related expectations in TestCPUInfo and TestGetProcessorByLscpuu to drop the 'x ' suffix.
  • Refactor TestDoReadCache to create a temporary cache using doSaveCache, then read it via doReadCache and compare all relevant SystemInfo fields, including the updated Processor format.
systeminfo1/info_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In supplementProcessorFrequency, consider precompiling the frequency regex as a package-level regexp.Regexp instead of calling regexp.MatchString each time to avoid repeated compilation on every init.
  • The debug log in supplementProcessorFrequency only prints the original processor string and source; including the final formatted value with the appended frequency would make the log more informative for debugging.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `supplementProcessorFrequency`, consider precompiling the frequency regex as a package-level `regexp.Regexp` instead of calling `regexp.MatchString` each time to avoid repeated compilation on every `init`.
- The debug log in `supplementProcessorFrequency` only prints the original processor string and source; including the final formatted value with the appended frequency would make the log more informative for debugging.

## Individual Comments

### Comment 1
<location> `systeminfo1/info.go:297` </location>
<code_context>
+	func supplementProcessorFrequency(processor string, currentSpeed uint64, cpuMaxMHz float64) string {
</code_context>

<issue_to_address>
**issue:** Frequency detection only triggers on the "@ ... Hz" pattern, which can lead to duplicated frequency when the model name already includes GHz without '@'.

Because the regexp only matches `@ <num><unit>Hz`, it misses model strings that already contain a bare `<num>GHz` (e.g. from `/proc/cpuinfo`). In those cases `matched` stays false and we append a second frequency, yielding names like `Intel(R) ... 3.20GHz @ 2.30GHz`. It would be better to detect any existing `<num>\s*(M|G)Hz` (with or without `@`) or, at minimum, check for existing `GHz`/`MHz` tokens before appending.
</issue_to_address>

### Comment 2
<location> `systeminfo1/info.go:309-318` </location>
<code_context>
+		} else {
+			source = "CPUMaxMHz"
+		}
+		logger.Debugf("Added frequency to Processor: %s (from %s)", processor, source)
+		return fmt.Sprintf("%s @ %.2fGHz", processor, freqGHz)
+	}
</code_context>

<issue_to_address>
**suggestion:** Debug log only prints the original processor string, not the final value with frequency appended.

Here `processor` is still the unmodified string, so the log doesn’t match the final returned value. To keep diagnostics accurate, either include `freqGHz` in the log or log the fully formatted string by assigning it to a variable used for both logging and returning.

```suggestion
	if freqGHz > 0 {
		var source string
		if currentSpeed != 0 {
			source = "CurrentSpeed"
		} else {
			source = "CPUMaxMHz"
		}
		formattedProcessor := fmt.Sprintf("%s @ %.2fGHz", processor, freqGHz)
		logger.Debugf("Added frequency to Processor: %s (from %s)", formattedProcessor, source)
		return formattedProcessor
	}
```
</issue_to_address>

### Comment 3
<location> `systeminfo1/info_test.go:106-115` </location>
<code_context>
+func TestDoReadCache(t *testing.T) {
</code_context>

<issue_to_address>
**suggestion (testing):** Extend `TestDoReadCache` to cover processor frequency supplementation behavior

The new test is a good improvement, but it only verifies round-tripping `SystemInfo` as-is. To validate the new `init()` behavior that calls `supplementProcessorFrequency`, please add a test that writes a cached `SystemInfo` whose `Processor` lacks a frequency (e.g. `"Intel(R) Core(TM) i5-4570 CPU"`) but has nonzero `CurrentSpeed`/`CPUMaxMHz`, then:

- Reads it back via `doReadCache`, and
- Either calls `supplementProcessorFrequency` on the loaded struct and asserts the `Processor` gains the `@ xx.xxGHz` suffix, or
- Goes through the public initialization path and asserts the final `Processor` value.

This will ensure the regression (missing frequency from cached data) is properly covered by tests.

Suggested implementation:

```golang
func TestDoReadCache(t *testing.T) {
	tmpDir := t.TempDir()
	cacheFile := filepath.Join(tmpDir, "systeminfo_cache.json")

	// 创建测试数据(包含频率,验证正常的回写、读取)
	expectedInfo := &SystemInfo{
		Version:    "20 专业版",
		DistroID:   "uos",
		DistroDesc: "UnionTech OS 20",
		DistroVer:  "20",
		Processor:  "Intel(R) Core(TM) i5-4570 CPU @ 3.20GHz",
		DiskCap:    uint64(500107862016),
		MemoryCap:  uint64(8280711168),
	}

	data, err := json.Marshal(expectedInfo)
	assert.NoError(t, err)

	err = os.WriteFile(cacheFile, data, 0o644)
	assert.NoError(t, err)

	// 通过 doReadCache 读回并验证与写入时一致
	got, err := doReadCache(cacheFile)
	assert.NoError(t, err)
	assert.Equal(t, expectedInfo, got)
}

func TestDoReadCache_SupplementsProcessorFrequency(t *testing.T) {
	tmpDir := t.TempDir()
	cacheFile := filepath.Join(tmpDir, "systeminfo_cache.json")

	// 缓存中的 SystemInfo:CPU 型号没有频率后缀
	cachedInfo := &SystemInfo{
		Version:    "20 专业版",
		DistroID:   "uos",
		DistroDesc: "UnionTech OS 20",
		DistroVer:  "20",
		Processor:  "Intel(R) Core(TM) i5-4570 CPU",
		DiskCap:    uint64(500107862016),
		MemoryCap:  uint64(8280711168),
		// 这里还应设置表示 CPU 频率的字段(例如 CurrentSpeed / CPUMaxMHz),见 additional_changes
	}

	data, err := json.Marshal(cachedInfo)
	assert.NoError(t, err)

	err = os.WriteFile(cacheFile, data, 0o644)
	assert.NoError(t, err)

	// 通过 doReadCache 读回缓存数据
	got, err := doReadCache(cacheFile)
	assert.NoError(t, err)

	// 调用补全逻辑,期望为 Processor 补上频率后缀
	err = supplementProcessorFrequency(got)
	assert.NoError(t, err)

	assert.Equal(t, "Intel(R) Core(TM) i5-4570 CPU @ 3.20GHz", got.Processor)

```

1. Imports:
   - Ensure `systeminfo1/info_test.go` imports the following packages (add if missing):
     - `encoding/json`
     - `os`
     - `path/filepath`
   Example (adjust to your existing import block style):
   ```go
   import (
       "encoding/json"
       "os"
       "path/filepath"

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

2. `doReadCache` signature:
   - The above tests assume `doReadCache` has the signature:
     ```go
     func doReadCache(cacheFile string) (*SystemInfo, error)
     ```
   - If your actual `doReadCache` has a different signature (e.g. no parameters and uses a global cache path), change the test calls accordingly:
     - Use the same invocation form as the existing/old `TestDoReadCache`.
     - If `doReadCache` does not accept a path, you should:
       - Either temporarily override the global cache path within the test (saving and restoring it with `defer`), or
       - Use whatever helper your original test was using to point `doReadCache` at the temporary file.

3. `supplementProcessorFrequency` signature:
   - The test assumes:
     ```go
     func supplementProcessorFrequency(info *SystemInfo) error
     ```
   - If the actual signature returns no error, adjust to:
     ```go
     supplementProcessorFrequency(got)
     ```
     and remove the `assert.NoError(t, err)` line.
   - If its parameter type or name differs, adapt the call accordingly but keep the semantics: it should operate on the `SystemInfo` instance loaded from `doReadCache`.

4. CPU frequency fields:
   - The comment in `TestDoReadCache_SupplementsProcessorFrequency` mentions setting fields such as `CurrentSpeed`/`CPUMaxMHz`. Modify the `cachedInfo` literal to set the correct fields and types used in your actual `SystemInfo` definition, for example:
     ```go
     cachedInfo := &SystemInfo{
         // ...
         CurrentSpeedMHz: 3200,
         CPUMaxMHz:       3200,
     }
     ```
   - Align the field names and units (MHz/GHz, float vs int) with your existing struct and with what `supplementProcessorFrequency` expects.

5. If you want to keep the original “round-trip without modification” behavior in addition to the new checks:
   - Confirm whether an older version of `TestDoReadCache` had additional assertions or setup beyond what is visible in the provided snippet.
   - If so, reintroduce those assertions into the new implementation of `TestDoReadCache` above, just before the final `assert.Equal(t, expectedInfo, got)`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@electricface electricface changed the title swt/fix bug274991 fix(systeminfo): Remove CPU count and enhance frequency logic Jan 30, 2026
@electricface electricface force-pushed the swt/fix-bug274991 branch 2 times, most recently from 961d0de to 7f85654 Compare January 30, 2026 08:03
- Removed code related to CPU count retrieval; now consistently
returns CPU model information without quantity
- Modified the Processor string to append frequency information,
preventing duplicate frequency entries
- Updated relevant test cases by removing assertions for CPU count
and verifying correct frequency appending

---

fix(systeminfo): 删除CPU计数并增加频率补充逻辑

- 移除获取CPU数量相关代码,统一返回无数量的CPU型号信息
- 修改Processor字符串补充频率的实现,避免重复添加频率信息
- 更新相关测试用例,去除对CPU数量的断言,验证频率补充正确

PMS: BUG-274991
Influence: 控制中心->系统->关于本机->处理器
@deepin-ci-robot
Copy link

deepin pr auto review

Git Diff 代码审查报告

总体评价

该代码修改主要是重构了 CPU 信息处理逻辑,移除了 CPU 核心数的显示,并增加了频率信息补充功能。整体代码结构清晰,但存在一些可以改进的地方。

详细审查意见

1. 代码逻辑问题

1.1 频率单位转换问题

supplementProcessorFrequency 函数中:

if currentSpeed != 0 {
    freqGHz = float64(currentSpeed) / 1000
} else if !isFloatEqual(cpuMaxMHz, 0.0) {
    freqGHz = cpuMaxMHz / 1000
}

问题cpuMaxMHz 变量名暗示单位是 MHz,但除以 1000 得到 GHz,这与变量名不一致。

建议:明确变量单位或添加注释说明:

// cpuMaxMHz: max CPU frequency in MHz (note: variable name is historical, actual unit is MHz)
if currentSpeed != 0 {
    freqGHz = float64(currentSpeed) / 1000
} else if !isFloatEqual(cpuMaxMHz, 0.0) {
    freqGHz = cpuMaxMHz / 1000  // Convert MHz to GHz
}

1.2 正则表达式匹配问题

freqPattern := `\d+\.?\d*\s*[MGT]Hz`

问题:这个正则表达式可能匹配到不完整的频率信息,如 "1.2G" 而不是 "1.2GHz"。

建议:修改正则表达式确保完整匹配:

freqPattern := `\d+\.?\d*\s*[MGT]Hz\b`  // 添加 \b 确保单词边界

2. 代码性能问题

2.1 正则表达式编译

matched, err := regexp.MatchString(freqPattern, processor)

问题:每次调用 supplementProcessorFrequency 都会重新编译正则表达式,影响性能。

建议:将正则表达式预编译为包级变量:

var freqRegex = regexp.MustCompile(`\d+\.?\d*\s*[MGT]Hz\b`)

func supplementProcessorFrequency(...) string {
    // ...
    matched := freqRegex.MatchString(processor)
    // ...
}

3. 代码安全问题

3.1 日志信息泄露

logger.Debugf("Added frequency to Processor: %s (from %s)",
    formattedProcessor, source)

问题:日志中记录了完整的处理器信息,可能泄露系统信息。

建议:考虑在敏感环境中减少日志详细程度或添加日志级别控制。

4. 代码质量问题

4.1 测试用例改进

TestSupplementProcessorFrequency 中,测试用例覆盖良好,但可以增加边界条件测试:

// 测试极端频率值
result = supplementProcessorFrequency("High Speed CPU", 999999, 0)
assert.Equal(t, "High Speed CPU @ 999.999GHz", result)

// 测试负值输入(虽然实际不会发生)
result = supplementProcessorFrequency("Negative CPU", -1000, 0)
assert.Equal(t, "Negative CPU", result)  // 应该忽略负值

4.2 错误处理

if err != nil {
    logger.Warning("Failed to match frequency pattern:", err)
    return processor
}

问题:正则表达式匹配失败时直接返回原始字符串,可能导致频率信息缺失。

建议:考虑更健壮的错误处理或降级策略。

5. 测试代码改进

5.1 TestDoReadCache 改进

当前测试创建临时文件的方式可以更安全:

func TestDoReadCache(t *testing.T) {
    // 使用 t.TempDir() 创建临时目录
    tmpDir := t.TempDir()
    tmpFile := filepath.Join(tmpDir, "systeminfo.cache")
    
    // 创建测试数据
    expectedInfo := &SystemInfo{...}
    
    // 保存并验证
    err := doSaveCache(expectedInfo, tmpFile)
    assert.NoError(t, err)
    
    // 读取并验证
    ret, err := doReadCache(tmpFile)
    assert.NoError(t, err)
    
    // 使用 assert.Equal 比较整个结构体
    assert.Equal(t, expectedInfo, ret)
}

总结

  1. 主要改进点

    • 预编译正则表达式以提高性能
    • 明确变量单位或添加注释
    • 增强正则表达式匹配准确性
    • 改进测试用例覆盖边界条件
  2. 次要改进点

    • 考虑日志信息泄露问题
    • 改进错误处理策略
    • 优化测试代码结构
  3. 建议优先级

    • 高优先级:正则表达式预编译、单位说明
    • 中优先级:正则表达式改进、测试用例增强
    • 低优先级:日志信息控制、错误处理优化

整体而言,代码修改方向正确,测试覆盖充分,但建议在上述几个方面进行改进以提高代码质量和性能。

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: electricface, fly602

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@mhduiy mhduiy merged commit 5ab3f5b into linuxdeepin:master Feb 2, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants