feat(drawing): формат и ориентация листа (drawing_set_sheet_format)
Задать формат (A0–A5/user) и ориентацию (книжная/альбомная) листа чертежа через API7 ILayoutSheet.Format (ISheetFormat). Стандартный формат — размеры авто; user — явные width/height (landscape игнорируется, ориентация из соотношения сторон). - DrawingService.SetSheetFormatAsync + RequireLayoutSheet - PaperFormat (enum + Parse/ToKompas/FromKompas), SheetFormatResult - DrawingValidation.ValidateFormatDimensions (user: >0; стандартный: размеры запрещены) - 19 unit + 7 интеграционных тестов (всего 304 зелёных), сборка Release чистая Спайк подтвердил: дефолт A4 portrait; стандартный формат пересчитывает W/H авто (A3 landscape→420×297); user применяет width/height. Ревью спека pi/glm-5.1 + pi/kimi-k2.6 учтено (FromKompas для дружелюбного имени, запрет размеров у стандартного, sheetNumber>0, landscape игнорируется для user). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# Дизайн: формат и ориентация листа чертежа (drawing_set_sheet_format) через API7
|
||||
|
||||
**Дата:** 2026-05-27
|
||||
**Статус:** дизайн согласован, спайк проведён, ревью pi/glm-5.1 + pi/kimi-k2.6 учтено — к реализации
|
||||
|
||||
## Правки по ревью (pi/glm-5.1 + pi/kimi-k2.6)
|
||||
|
||||
- **A** для `user` флаг `landscape` **игнорируется** — ориентация задаётся соотношением `width`/`height`
|
||||
(КОМПАС сам выставит `VerticalOrientation`). `VerticalOrientation=!landscape` ставим ТОЛЬКО для
|
||||
стандартных форматов (избегаем неоднозначного свопа W/H). Задокументировано в описании инструмента.
|
||||
- **B/E** `width`/`height` для стандартного формата запрещены: валидатор `ValidateFormatDimensions`
|
||||
(для `User` — оба конечны и `>0`; для стандартного — оба `==0`, иначе `ArgumentException` «размеры
|
||||
только для user»).
|
||||
- **C** read-back формата возвращает дружелюбное имя через новый `PaperFormats.FromKompas(ksEnum)` →
|
||||
`SheetFormatResult.Format` = `"A3"`/`"User"` (симметрия вход/выход), а не COM-имя `"ksFormatA3"`.
|
||||
- **D** `RequireLayoutSheet(sheetNumber)`: `sheetNumber>0` (`ArgumentOutOfRangeException`) + null-check
|
||||
после `ItemByNumber[sheetNumber]` (`InvalidOperationException` «нет листа N»); + интеграционный тест.
|
||||
- **F** read-back `fmt2 = sheet.Format` с null-check.
|
||||
- **G** `SheetFormatResult` — `public sealed record` с `public required … { get; init; }` (как
|
||||
`DrawingDimensionResult`/`DrawingViewsResult`).
|
||||
- **H** интеграционные сравнения размеров — через `Assert.InRange` (допуск на float).
|
||||
- **I** члены `PaperFormat` — с XML-`<summary>` (как `RoughSignType`).
|
||||
- **J** `FormatMultiplicity` оставляем на COM-дефолте (1) — отмечено в реализации.
|
||||
- **K** (верхняя граница `width`/`height`) — отклонено, в OPEN_QUESTIONS как будущее упрочнение
|
||||
(консистентно с `RequirePositiveRadius` — верхней границы нет).
|
||||
|
||||
## Цель
|
||||
|
||||
Веха 2D-ЧЕРТЁЖ, инкремент 8. Задать **формат** (A0–A5 или пользовательский) и **ориентацию**
|
||||
(книжная/альбомная) листа активного чертежа. Фундамент «правильного» по ГОСТ чертежа (сейчас лист
|
||||
всегда дефолтный A4 книжный).
|
||||
|
||||
## Спайк: механизм подтверждён вживую (НЕ разучивать)
|
||||
|
||||
Спайк (`_SpikeSheetFormat`, прогнан на реальном КОМПАС, затем удалён): новый чертёж →
|
||||
`doc.LayoutSheets.ItemByNumber[1]` (`ILayoutSheet`) → `sheet.Format` (`ISheetFormat`).
|
||||
|
||||
```
|
||||
ISheetFormat fmt = sheet.Format;
|
||||
fmt.Format = ksDocumentFormatEnum.ksFormatA3; // A0=0..A5=5, User=6
|
||||
fmt.VerticalOrientation = false; // true=книжная (portrait), false=альбомная (landscape)
|
||||
sheet.Update(); // True
|
||||
// для стандартного формата FormatWidth/Height пересчитываются АВТО (A3 landscape → 420×297)
|
||||
```
|
||||
|
||||
**Проверено:**
|
||||
- Дефолт нового чертежа: `ksFormatA4`, `VerticalOrientation=true`, 210×297, `FormatMultiplicity=1`.
|
||||
- A3 landscape: `Update=True`, read-back `Format=ksFormatA3`, `Vertical=False`, **W=420, H=297 (авто)** —
|
||||
для стандартных форматов размеры не задаём, их даёт enum+ориентация.
|
||||
- A4 portrait: 210×297.
|
||||
- Пользовательский: `Format=ksFormatUser` + `FormatWidth=500` + `FormatHeight=300` + `Update` → применён.
|
||||
|
||||
`ksDocumentFormatEnum`: `ksFormatA0=0`, `A1=1`, `A2=2`, `A3=3`, `A4=4`, `A5=5`, `ksFormatUser=6`.
|
||||
`ISheetFormat`: `Format` (enum), `VerticalOrientation` (bool), `FormatWidth`/`FormatHeight` (double, мм),
|
||||
`FormatMultiplicity` (int).
|
||||
|
||||
## MCP-инструмент
|
||||
|
||||
| Инструмент | Параметры | Поведение |
|
||||
|---|---|---|
|
||||
| `drawing_set_sheet_format` | `format="A4"` (A0\|A1\|A2\|A3\|A4\|A5\|user), `landscape=false` (false=книжная), `width=0`,`height=0` (ТОЛЬКО для `user`, мм, >0), `sheetNumber=1` | Задать формат и ориентацию листа `sheetNumber` активного чертежа. Стандартный формат — размеры авто (по enum+ориентации), `width`/`height` для него запрещены (должны быть 0). `user` — задать `width`/`height` (>0); для `user` флаг `landscape` ИГНОРИРУЕТСЯ (ориентация — из соотношения сторон). Возвращает итоговый формат («A3»/«User»), ширину, высоту, ориентацию (read-back). |
|
||||
|
||||
## Архитектура
|
||||
|
||||
В `DrawingService` (namespace `Kompas.Mcp.Core.Drawings`).
|
||||
- `SetSheetFormatAsync(PaperFormat format, bool landscape, double width, double height, int sheetNumber, ct)`
|
||||
→ `SheetFormatResult`.
|
||||
- Helper `RequireLayoutSheet(sheetNumber)` → `ILayoutSheet`: `sheetNumber>0`
|
||||
(`ArgumentOutOfRangeException`) → нет активного чертежа / `LayoutSheets` null / `ItemByNumber[sheetNumber]`
|
||||
null («нет листа N») — раздельная диагностика, рядом с `RequireActiveStamp`.
|
||||
- Новый файл `src/Kompas.Mcp.Core/Drawings/PaperFormat.cs`: `enum PaperFormat {A0,A1,A2,A3,A4,A5,User}`
|
||||
(каждый член с XML-`<summary>`) + `PaperFormats.Parse(string)` / `ToKompas(PaperFormat)→ksDocumentFormatEnum`
|
||||
/ `FromKompas(ksDocumentFormatEnum)→PaperFormat` (для read-back дружелюбного имени), по образцу `RoughSignTypes`.
|
||||
- Новый файл `src/Kompas.Mcp.Core/Drawings/SheetFormatResult.cs`:
|
||||
`public sealed record SheetFormatResult { public required string Format; public required double Width;
|
||||
public required double Height; public required bool Landscape; }` (стиль `DrawingDimensionResult`).
|
||||
|
||||
## Реализация
|
||||
|
||||
`SetSheetFormatCore`: `DrawingValidation.ValidateFormatDimensions(format, width, height)` →
|
||||
`PaperFormats.ToKompas(format)` → `RequireLayoutSheet(sheetNumber)` → `fmt = sheet.Format` (null-check) →
|
||||
`fmt.Format = ksEnum`; if `format==User`: `fmt.FormatWidth=width`, `fmt.FormatHeight=height` (ориентацию
|
||||
НЕ трогаем — задаётся W/H); else: `fmt.VerticalOrientation = !landscape` (размеры авто) → `sheet.Update()`
|
||||
(FALSE → ошибка; объект уровня листа, перезапись идемпотентна — отката `Delete` нет) → read-back
|
||||
`fmt2 = sheet.Format` (null-check) → `SheetFormatResult { Format = PaperFormats.FromKompas(fmt2.Format)
|
||||
.ToString(), Width = fmt2.FormatWidth, Height = fmt2.FormatHeight, Landscape = !fmt2.VerticalOrientation }`.
|
||||
|
||||
`FormatMultiplicity` оставляем на COM-дефолте (1) — не читаем и не задаём. RCW точечно не освобождаем
|
||||
(консистентно; долг v2-2).
|
||||
|
||||
## Валидация (чистые static, unit)
|
||||
|
||||
- `PaperFormats.Parse` — `A0..A5`/`user` (+ регистр/пробелы); неизвестное → `ArgumentException`; null → `ArgumentNullException`.
|
||||
- Новый `DrawingValidation.ValidateFormatDimensions(PaperFormat format, double width, double height)`:
|
||||
для `User` — `width` и `height` оба конечны и `> 0` (иначе `ArgumentOutOfRangeException`); для стандартного —
|
||||
`width==0 && height==0` (иначе `ArgumentException` «размеры задаются только для format=user»).
|
||||
- `PaperFormats.ToKompas`/`FromKompas` (↔ `ksDocumentFormatEnum`) НЕ покрываем unit — граница проекта
|
||||
(тест не ссылается на `Kompas6Constants`), как у прочих enum-маппингов; покрываются интеграцией.
|
||||
|
||||
## Тестирование
|
||||
|
||||
### Unit (`PaperFormatsTests`, `DrawingValidationTests`)
|
||||
- `PaperFormats.Parse`: `A4/a3/ A2 /a0/a5/user` → enum; неизвестное → `ArgumentException`; null → `ArgumentNullException`.
|
||||
- `ValidateFormatDimensions`:
|
||||
- `User`: бросает на (0,300)/(500,0)/(−1,300)/(NaN,300)/(500,∞); пропускает (500,300).
|
||||
- стандартный (`A4`): бросает на (500,0)/(0,300)/(500,300); пропускает (0,0).
|
||||
|
||||
### Integration (`DrawingSheetFormatTests`; сравнения размеров — `Assert.InRange`)
|
||||
1. **A3 landscape**: `format=A3, landscape=true` → `Format=="A3"`, `Width≈420`, `Height≈297`, `Landscape==true`.
|
||||
2. **A4 portrait** (`landscape=false`): → `Format=="A4"`, `Width≈210`, `Height≈297`, `Landscape==false`.
|
||||
3. **User 500×300**: `format=user, width=500, height=300` → `Format=="User"`, `Width≈500`, `Height≈300`.
|
||||
4. **User без размеров** (`width=0`) → `ArgumentOutOfRangeException` (до COM).
|
||||
5. **Размеры для стандартного** (`format=A3, width=400`) → `ArgumentException` (до COM).
|
||||
6. **Несуществующий лист** (`sheetNumber=99`) → `InvalidOperationException`.
|
||||
7. **Активный документ не чертёж** (деталь) → понятная ошибка.
|
||||
|
||||
## Дальнейшее (вне спека)
|
||||
- Рамка/основная надпись по конкретному ГОСТ-стилю оформления (`LayoutLibraryFileName`/`LayoutStyleNumber`),
|
||||
несколько листов (`LayoutSheets.Add`); привязка к дуге, угловой/шероховатость; выноски/базы/допуски формы.
|
||||
@@ -84,6 +84,63 @@ public sealed class DrawingService
|
||||
?? throw new InvalidOperationException("У листа оформления нет основной надписи (Stamp).");
|
||||
}
|
||||
|
||||
/// <summary>Лист оформления чертежа по номеру (1-based) с диагностикой (номер > 0, лист существует).</summary>
|
||||
private ILayoutSheet RequireLayoutSheet(int sheetNumber)
|
||||
{
|
||||
if (sheetNumber <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(sheetNumber), "Номер листа должен быть > 0.");
|
||||
var doc2d = RequireActiveDrawing();
|
||||
var sheets = doc2d.LayoutSheets
|
||||
?? throw new InvalidOperationException("Не удалось получить листы оформления чертежа.");
|
||||
return sheets.ItemByNumber[sheetNumber] as ILayoutSheet
|
||||
?? throw new InvalidOperationException($"На чертеже нет листа оформления #{sheetNumber}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Задать формат (<paramref name="format"/>: A0–A5 или user) и ориентацию листа
|
||||
/// <paramref name="sheetNumber"/> активного чертежа. Для стандартного формата размеры авто
|
||||
/// (<paramref name="width"/>/<paramref name="height"/> запрещены); для user — заданные width/height (мм),
|
||||
/// при этом <paramref name="landscape"/> игнорируется (ориентация из соотношения сторон). Возвращает
|
||||
/// итоговый формат, размеры и ориентацию (read-back из COM).
|
||||
/// </summary>
|
||||
public Task<SheetFormatResult> SetSheetFormatAsync(PaperFormat format, bool landscape, double width,
|
||||
double height, int sheetNumber = 1, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() => SetSheetFormatCore(format, landscape, width, height, sheetNumber), ct);
|
||||
|
||||
private SheetFormatResult SetSheetFormatCore(PaperFormat format, bool landscape, double width,
|
||||
double height, int sheetNumber)
|
||||
{
|
||||
DrawingValidation.ValidateFormatDimensions(format, width, height);
|
||||
|
||||
var sheet = RequireLayoutSheet(sheetNumber);
|
||||
var fmt = sheet.Format
|
||||
?? throw new InvalidOperationException("У листа недоступен формат (Format == null).");
|
||||
fmt.Format = PaperFormats.ToKompas(format);
|
||||
if (format == PaperFormat.User)
|
||||
{
|
||||
// Для пользовательского ориентация задаётся соотношением width/height — VerticalOrientation не трогаем.
|
||||
fmt.FormatWidth = width;
|
||||
fmt.FormatHeight = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt.VerticalOrientation = !landscape; // стандартный: размеры авто по формату+ориентации
|
||||
}
|
||||
// FormatMultiplicity оставляем на COM-дефолте (1).
|
||||
if (!sheet.Update())
|
||||
throw new InvalidOperationException("ILayoutSheet.Update вернул FALSE — формат листа не задан.");
|
||||
|
||||
var fmt2 = sheet.Format
|
||||
?? throw new InvalidOperationException("Формат листа недоступен после Update (Format == null).");
|
||||
return new SheetFormatResult
|
||||
{
|
||||
Format = PaperFormats.FromKompas(fmt2.Format).ToString(),
|
||||
Width = fmt2.FormatWidth,
|
||||
Height = fmt2.FormatHeight,
|
||||
Landscape = !fmt2.VerticalOrientation,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Поставить линейный размер на виде активного чертежа между точками (x1,y1)-(x2,y2) в ЛОКАЛЬНОЙ
|
||||
/// СК вида (мм); размерная линия проходит через (x3,y3). <paramref name="viewNumber"/> — номер вида
|
||||
|
||||
@@ -55,6 +55,26 @@ public static class DrawingValidation
|
||||
throw new ArgumentException("Текст не должен быть пустым.", paramName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверить размеры формата листа: для <see cref="PaperFormat.User"/> — ширина и высота оба
|
||||
/// конечны и > 0; для стандартного формата — оба должны быть 0 (размеры авто, задавать нельзя).
|
||||
/// </summary>
|
||||
public static void ValidateFormatDimensions(PaperFormat format, double width, double height)
|
||||
{
|
||||
if (format == PaperFormat.User)
|
||||
{
|
||||
if (!double.IsFinite(width) || width <= 0 || !double.IsFinite(height) || height <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(width),
|
||||
"Для format=user ширина и высота должны быть конечными и > 0.");
|
||||
}
|
||||
else if (width != 0 || height != 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Ширина/высота задаются только для format=user; для стандартного формата размеры авто (передайте 0).",
|
||||
nameof(width));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Минимальное расстояние между выносными точками линейного размера (мм).</summary>
|
||||
private const double MinPointDistance = 1e-7;
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
using Kompas6Constants;
|
||||
|
||||
namespace Kompas.Mcp.Core.Drawings;
|
||||
|
||||
/// <summary>Формат листа чертежа (обёртка над <see cref="ksDocumentFormatEnum"/>).</summary>
|
||||
public enum PaperFormat
|
||||
{
|
||||
/// <summary>A0 (841×1189 мм).</summary>
|
||||
A0,
|
||||
/// <summary>A1 (594×841 мм).</summary>
|
||||
A1,
|
||||
/// <summary>A2 (420×594 мм).</summary>
|
||||
A2,
|
||||
/// <summary>A3 (297×420 мм).</summary>
|
||||
A3,
|
||||
/// <summary>A4 (210×297 мм).</summary>
|
||||
A4,
|
||||
/// <summary>A5 (148×210 мм).</summary>
|
||||
A5,
|
||||
/// <summary>Пользовательский формат с явной шириной/высотой.</summary>
|
||||
User,
|
||||
}
|
||||
|
||||
/// <summary>Маппинг формата листа ↔ перечисление КОМПАС и разбор строки.</summary>
|
||||
public static class PaperFormats
|
||||
{
|
||||
public static ksDocumentFormatEnum ToKompas(PaperFormat format) => format switch
|
||||
{
|
||||
PaperFormat.A0 => ksDocumentFormatEnum.ksFormatA0,
|
||||
PaperFormat.A1 => ksDocumentFormatEnum.ksFormatA1,
|
||||
PaperFormat.A2 => ksDocumentFormatEnum.ksFormatA2,
|
||||
PaperFormat.A3 => ksDocumentFormatEnum.ksFormatA3,
|
||||
PaperFormat.A4 => ksDocumentFormatEnum.ksFormatA4,
|
||||
PaperFormat.A5 => ksDocumentFormatEnum.ksFormatA5,
|
||||
PaperFormat.User => ksDocumentFormatEnum.ksFormatUser,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(format), format, "Неизвестный формат листа."),
|
||||
};
|
||||
|
||||
/// <summary>Обратный маппинг (для read-back дружелюбного имени).</summary>
|
||||
public static PaperFormat FromKompas(ksDocumentFormatEnum format) => format switch
|
||||
{
|
||||
ksDocumentFormatEnum.ksFormatA0 => PaperFormat.A0,
|
||||
ksDocumentFormatEnum.ksFormatA1 => PaperFormat.A1,
|
||||
ksDocumentFormatEnum.ksFormatA2 => PaperFormat.A2,
|
||||
ksDocumentFormatEnum.ksFormatA3 => PaperFormat.A3,
|
||||
ksDocumentFormatEnum.ksFormatA4 => PaperFormat.A4,
|
||||
ksDocumentFormatEnum.ksFormatA5 => PaperFormat.A5,
|
||||
ksDocumentFormatEnum.ksFormatUser => PaperFormat.User,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(format), format, "Неизвестный формат листа КОМПАС."),
|
||||
};
|
||||
|
||||
/// <summary>Разобрать строку формата (A0..A5 / user, регистр/пробелы) в <see cref="PaperFormat"/>.</summary>
|
||||
public static PaperFormat Parse(string value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
return value.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"a0" => PaperFormat.A0,
|
||||
"a1" => PaperFormat.A1,
|
||||
"a2" => PaperFormat.A2,
|
||||
"a3" => PaperFormat.A3,
|
||||
"a4" => PaperFormat.A4,
|
||||
"a5" => PaperFormat.A5,
|
||||
"user" or "custom" or "пользовательский" => PaperFormat.User,
|
||||
_ => throw new ArgumentException(
|
||||
$"Неизвестный формат листа '{value}'. Допустимо: A0, A1, A2, A3, A4, A5, user.",
|
||||
nameof(value)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Kompas.Mcp.Core.Drawings;
|
||||
|
||||
/// <summary>Результат задания формата листа чертежа (read-back из COM).</summary>
|
||||
public sealed record SheetFormatResult
|
||||
{
|
||||
/// <summary>Итоговый формат («A0»…«A5» или «User»).</summary>
|
||||
public required string Format { get; init; }
|
||||
|
||||
/// <summary>Ширина листа, мм.</summary>
|
||||
public required double Width { get; init; }
|
||||
|
||||
/// <summary>Высота листа, мм.</summary>
|
||||
public required double Height { get; init; }
|
||||
|
||||
/// <summary>Альбомная ориентация (true) либо книжная (false).</summary>
|
||||
public required bool Landscape { get; init; }
|
||||
}
|
||||
@@ -187,4 +187,24 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing)
|
||||
var lines = await drawing.SetTechnicalRequirementsAsync(text);
|
||||
return $"Технические требования заданы ({lines} стр.).";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "drawing_set_sheet_format")]
|
||||
[Description("Задать формат и ориентацию листа активного чертежа. format: A0|A1|A2|A3|A4|A5|user. " +
|
||||
"landscape: false = книжная (portrait), true = альбомная (landscape). Для стандартного формата " +
|
||||
"размеры вычисляются автоматически — width/height передавать НЕ нужно (должны быть 0). Для format=user " +
|
||||
"задайте width и height в мм (>0); при user флаг landscape игнорируется (ориентация — из соотношения " +
|
||||
"сторон). sheetNumber — номер листа (1 = первый). Возвращает итоговый формат, размеры и ориентацию.")]
|
||||
public async Task<string> SetSheetFormat(
|
||||
[Description("Формат: A0|A1|A2|A3|A4|A5|user")] string format = "A4",
|
||||
[Description("Альбомная ориентация (true) или книжная (false)")] bool landscape = false,
|
||||
[Description("Ширина листа, мм (только для format=user)")] double width = 0,
|
||||
[Description("Высота листа, мм (только для format=user)")] double height = 0,
|
||||
[Description("Номер листа (1 = первый)")] int sheetNumber = 1)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
var f = PaperFormats.Parse(format);
|
||||
var r = await drawing.SetSheetFormatAsync(f, landscape, width, height, sheetNumber);
|
||||
var orient = r.Landscape ? "альбомная" : "книжная";
|
||||
return $"Формат листа {sheetNumber}: {r.Format}, {r.Width:0.#}×{r.Height:0.#} мм, {orient}.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,4 +91,32 @@ public sealed class DrawingValidationTests
|
||||
[Fact]
|
||||
public void RequireNonEmptyText_accepts_non_empty()
|
||||
=> Assert.Null(Record.Exception(() => DrawingValidation.RequireNonEmptyText("Ra 1.6", "text")));
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 300)]
|
||||
[InlineData(500, 0)]
|
||||
[InlineData(-1, 300)]
|
||||
[InlineData(double.NaN, 300)]
|
||||
[InlineData(500, double.PositiveInfinity)]
|
||||
public void ValidateFormatDimensions_user_throws_on_non_positive_or_non_finite(double w, double h)
|
||||
=> Assert.Throws<ArgumentOutOfRangeException>(
|
||||
() => DrawingValidation.ValidateFormatDimensions(PaperFormat.User, w, h));
|
||||
|
||||
[Fact]
|
||||
public void ValidateFormatDimensions_user_accepts_positive()
|
||||
=> Assert.Null(Record.Exception(
|
||||
() => DrawingValidation.ValidateFormatDimensions(PaperFormat.User, 500, 300)));
|
||||
|
||||
[Theory]
|
||||
[InlineData(500, 0)]
|
||||
[InlineData(0, 300)]
|
||||
[InlineData(500, 300)]
|
||||
public void ValidateFormatDimensions_standard_throws_when_dimensions_given(double w, double h)
|
||||
=> Assert.Throws<ArgumentException>(
|
||||
() => DrawingValidation.ValidateFormatDimensions(PaperFormat.A3, w, h));
|
||||
|
||||
[Fact]
|
||||
public void ValidateFormatDimensions_standard_accepts_zero_dimensions()
|
||||
=> Assert.Null(Record.Exception(
|
||||
() => DrawingValidation.ValidateFormatDimensions(PaperFormat.A3, 0, 0)));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
using Kompas.Mcp.Core.Documents;
|
||||
using Kompas.Mcp.Core.Drawings;
|
||||
|
||||
namespace Kompas.Mcp.Tests.Integration;
|
||||
|
||||
/// <summary>Интеграция: формат и ориентация листа чертежа (API7 ILayoutSheet.Format / ISheetFormat).</summary>
|
||||
[Trait("Category", "Integration")]
|
||||
[Collection(KompasCollection.Name)]
|
||||
public sealed class DrawingSheetFormatTests : IntegrationTestBase
|
||||
{
|
||||
private readonly DocumentService _docs;
|
||||
private readonly DrawingService _drawing;
|
||||
|
||||
public DrawingSheetFormatTests(KompasFixture fx) : base(fx)
|
||||
{
|
||||
_docs = new DocumentService(fx.Session, fx.Dispatcher);
|
||||
_drawing = new DrawingService(fx.Session, fx.Dispatcher);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A3_landscape()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
var r = await _drawing.SetSheetFormatAsync(PaperFormat.A3, landscape: true, width: 0, height: 0);
|
||||
Assert.Equal("A3", r.Format);
|
||||
Assert.InRange(r.Width, 419.5, 420.5);
|
||||
Assert.InRange(r.Height, 296.5, 297.5);
|
||||
Assert.True(r.Landscape);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A4_portrait()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
var r = await _drawing.SetSheetFormatAsync(PaperFormat.A4, landscape: false, width: 0, height: 0);
|
||||
Assert.Equal("A4", r.Format);
|
||||
Assert.InRange(r.Width, 209.5, 210.5);
|
||||
Assert.InRange(r.Height, 296.5, 297.5);
|
||||
Assert.False(r.Landscape);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task User_format_with_dimensions()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
var r = await _drawing.SetSheetFormatAsync(PaperFormat.User, landscape: false, width: 500, height: 300);
|
||||
Assert.Equal("User", r.Format);
|
||||
Assert.InRange(r.Width, 499.5, 500.5);
|
||||
Assert.InRange(r.Height, 299.5, 300.5);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task User_without_dimensions_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() =>
|
||||
_drawing.SetSheetFormatAsync(PaperFormat.User, landscape: false, width: 0, height: 0));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Standard_with_dimensions_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<ArgumentException>(() =>
|
||||
_drawing.SetSheetFormatAsync(PaperFormat.A3, landscape: false, width: 400, height: 0));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Nonexistent_sheet_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Drawing);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_drawing.SetSheetFormatAsync(PaperFormat.A4, landscape: false, width: 0, height: 0, sheetNumber: 99));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Active_document_not_drawing_throws()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||||
try
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_drawing.SetSheetFormatAsync(PaperFormat.A4, landscape: false, width: 0, height: 0));
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Kompas.Mcp.Core.Drawings;
|
||||
|
||||
namespace Kompas.Mcp.Tests;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class PaperFormatsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("A4", PaperFormat.A4)]
|
||||
[InlineData("a3", PaperFormat.A3)]
|
||||
[InlineData(" A2 ", PaperFormat.A2)]
|
||||
[InlineData("a0", PaperFormat.A0)]
|
||||
[InlineData("A5", PaperFormat.A5)]
|
||||
[InlineData("user", PaperFormat.User)]
|
||||
[InlineData("USER", PaperFormat.User)]
|
||||
public void Parse_maps_known(string value, PaperFormat expected)
|
||||
=> Assert.Equal(expected, PaperFormats.Parse(value));
|
||||
|
||||
[Fact]
|
||||
public void Parse_throws_on_unknown()
|
||||
=> Assert.Throws<ArgumentException>(() => PaperFormats.Parse("A6"));
|
||||
|
||||
[Fact]
|
||||
public void Parse_throws_on_null()
|
||||
=> Assert.Throws<ArgumentNullException>(() => PaperFormats.Parse(null!));
|
||||
|
||||
// ToKompas/FromKompas (↔ ksDocumentFormatEnum) — граница проекта, проверяются интеграцией.
|
||||
}
|
||||
Reference in New Issue
Block a user