mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 18:19:04 +02:00
152 lines
4.4 KiB
Go
152 lines
4.4 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Copyright The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//go:build windows
|
|
|
|
package winspool
|
|
|
|
import (
|
|
"path/filepath"
|
|
"slices"
|
|
"testing"
|
|
"unsafe"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
//nolint:gochecknoglobals
|
|
var (
|
|
procStartDocPrinterW = modWinspool.NewProc("StartDocPrinterW")
|
|
procAbortPrinter = modWinspool.NewProc("AbortPrinter")
|
|
)
|
|
|
|
// docInfo1 is DOC_INFO_1.
|
|
type docInfo1 struct {
|
|
pDocName *uint16
|
|
pOutputFile *uint16
|
|
pDatatype *uint16
|
|
}
|
|
|
|
func TestLayout(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if unsafe.Sizeof(uintptr(0)) != 8 {
|
|
t.Skip("offsets are checked for 64-bit Windows only")
|
|
}
|
|
|
|
// Offsets of the 64-bit SDK structures.
|
|
require.Equal(t, uintptr(136), unsafe.Sizeof(printerInfo2{}))
|
|
require.Equal(t, uintptr(0x08), unsafe.Offsetof(printerInfo2{}.pPrinterName))
|
|
require.Equal(t, uintptr(0x7c), unsafe.Offsetof(printerInfo2{}.Status))
|
|
require.Equal(t, uintptr(0x80), unsafe.Offsetof(printerInfo2{}.cJobs))
|
|
|
|
require.Equal(t, uintptr(96), unsafe.Sizeof(jobInfo1{}))
|
|
require.Equal(t, uintptr(0x08), unsafe.Offsetof(jobInfo1{}.pPrinterName))
|
|
require.Equal(t, uintptr(0x30), unsafe.Offsetof(jobInfo1{}.pStatus))
|
|
require.Equal(t, uintptr(0x38), unsafe.Offsetof(jobInfo1{}.Status))
|
|
require.Equal(t, uintptr(0x4c), unsafe.Offsetof(jobInfo1{}.Submitted))
|
|
}
|
|
|
|
func TestEnumPrinters(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
printers, err := EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, printers)
|
|
|
|
for _, printer := range printers {
|
|
require.NotEmpty(t, printer.Name)
|
|
}
|
|
}
|
|
|
|
func TestOpenPrinterInvalidName(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := OpenPrinter("windows_exporter test printer that does not exist")
|
|
require.ErrorIs(t, err, windows.ERROR_INVALID_PRINTER_NAME)
|
|
}
|
|
|
|
// TestEnumJobs starts a document without writing data, so the job stays in the
|
|
// spooling state and never reaches the port. AbortPrinter deletes it again.
|
|
func TestEnumJobs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
printers, err := EnumPrinters(PRINTER_ENUM_LOCAL)
|
|
require.NoError(t, err)
|
|
|
|
// CIPrinter is created by the CI workflow. Microsoft Print to PDF exists on most desktops.
|
|
idx := slices.IndexFunc(printers, func(p Printer) bool {
|
|
return p.Name == "CIPrinter" || p.Name == "Microsoft Print to PDF"
|
|
})
|
|
if idx == -1 {
|
|
t.Skip("no test printer available")
|
|
}
|
|
|
|
printerName := printers[idx].Name
|
|
|
|
handle, err := OpenPrinter(printerName)
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
require.NoError(t, ClosePrinter(handle))
|
|
})
|
|
|
|
docName, err := windows.UTF16PtrFromString("windows_exporter test job")
|
|
require.NoError(t, err)
|
|
|
|
// An output file keeps Microsoft Print to PDF from prompting for a file name.
|
|
outputFile, err := windows.UTF16PtrFromString(filepath.Join(t.TempDir(), "job.out"))
|
|
require.NoError(t, err)
|
|
|
|
docInfo := docInfo1{
|
|
pDocName: docName,
|
|
pOutputFile: outputFile,
|
|
}
|
|
|
|
jobID, _, err := procStartDocPrinterW.Call(uintptr(handle), 1, uintptr(unsafe.Pointer(&docInfo)))
|
|
require.NotZero(t, jobID, "StartDocPrinterW: %v", err)
|
|
|
|
t.Cleanup(func() {
|
|
r1, _, err := procAbortPrinter.Call(uintptr(handle))
|
|
require.NotZero(t, r1, "AbortPrinter: %v", err)
|
|
})
|
|
|
|
jobs, err := EnumJobs(handle)
|
|
require.NoError(t, err)
|
|
|
|
idx = slices.IndexFunc(jobs, func(j Job) bool { return j.ID == uint32(jobID) })
|
|
require.NotEqual(t, -1, idx, "job %d not found in %+v", jobID, jobs)
|
|
|
|
job := jobs[idx]
|
|
require.Equal(t, printerName, job.PrinterName)
|
|
require.NotZero(t, job.Status&JOB_STATUS_SPOOLING, "status 0x%08X", job.Status)
|
|
|
|
printers, err = EnumPrinters(PRINTER_ENUM_LOCAL)
|
|
require.NoError(t, err)
|
|
|
|
idx = slices.IndexFunc(printers, func(p Printer) bool { return p.Name == printerName })
|
|
require.NotEqual(t, -1, idx)
|
|
require.NotZero(t, printers[idx].Jobs)
|
|
}
|
|
|
|
func BenchmarkEnumPrinters(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for b.Loop() {
|
|
_, _ = EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS)
|
|
}
|
|
}
|