پرش به مطلب اصلی

بخش ۶: build، تست و review

کار روی یک تونل با کامپایل‌شدن تمام نمی‌شود. تغییر باید با یک preset معتبر build شود، integration test‌های مرتبط را پاس کند و موارد چک‌لیست review را رعایت کند. در این بخش می‌بینید چطور این موارد را بررسی و نتیجه را گزارش کنید.

build​

preset پیشنهادی CMake، ‏linux است. یک بار configure کنید و بعد build بگیرید:

cmake --preset linux
cmake --build --preset linux -j8

هنگام کار روی یک تونل، برای build سریع‌تر فقط target همان تونل را بسازید:

cmake --build --preset linux --target TlsClient -j8

نام target هر تونل با نام دایرکتوری آن یکسان است؛ مثل TcpConnector، UdpConnector، ‏TcpListener، ‏TlsClient و MuxClient.

build tree در build/linux/ قرار دارد و فایل اجرایی برای هر config جداگانه تولید می‌شود؛ برای مثال build/linux/Release/Waterwall و همچنین مسیرهای Debug/ و RelWithDebInfo/. preset‌های دیگر هم در build/ دایرکتوری‌های جداگانه‌ای مثل linux-gcc-x64، ‏linux-clang-x64 و linux-tsan-unit نیز وجود دارند. در یک روند اعتبارسنجی، این build tree‌ها را با هم ترکیب نکنید.

قواعد build​

  • دستور cmake --build build ... را روی یک درخت دلخواه build/ اجرا نکنید، مگر آنکه مطمئن شده باشید درخت سالمی است که با preset ساخته شده و قصد استفاده از همان را دارید.
  • در یک روند اعتبارسنجی، درخت‌های ساخت build/، ‏build/linux و build/linux-gcc-x64 را با هم ترکیب نکنید؛ ممکن است در نهایت فایل اجرایی قدیمی را تست کنید.
  • build یک target با preset را به build از دایرکتوری ریشه ترجیح دهید.

format فایل‌های C و header‌ها​

روی هر فایل C یا header که تغییر می‌دهید، فرمان قابل‌حمل clang-format را با فایل .clang-format مخزن اجرا کنید:

clang-format -i --style=file path/to/changed_file.c
clang-format --dry-run -Werror --style=file path/to/changed_file.c

هر محیط توسعه باید clang-format را به نسخه‌ی نصب‌شده و سازگار با پروژه متصل کند؛ مستندات مخزن نباید نام یک فایل اجرایی مخصوص توزیعی مشخص را ثابت کنند. فایل‌های جدید باید کاملاً با قالب پروژه سازگار باشند. بعضی فایل‌های قدیمی هنوز کاملاً سازگار نیستند؛ پس کد دست‌نخورده را یک‌جا دوباره قالب‌بندی نکنید. اگر اجرای فرمان روی کل فایل، خط‌های نامرتبط را بازنویسی می‌کند، محدوده‌های تغییرکرده را با --lines=<start>:<end> قالب‌بندی و بررسی کنید. اگر فرمان در دسترس نیست، به‌جای نادیده‌گرفتن بی‌سروصدای بررسی، دقیقاً گزارش کنید کدام قالب‌بندی یا اعتبارسنجی انجام نشده است.

حالت‌های build authority​

مدل build authority برای Ninja در مخزن، پیمایش بدون symlink در گراف، بستار دقیقِ دستورهای include/subninja، زمان ارزیابی متغیرهای Ninja و ورودی‌های تولیدشده‌ی اعلام‌شده را اعتبارسنجی می‌کند. هویت toolchain نیز بایت‌های مقصد نهایی symlink را دربر می‌گیرد.

با این حال، روند کنونی toolchain میزبان فقط تشخیصی (diagnostic) است و مرجع انتشار نیست: compiler، subprogram‌ها، header‌ها، library‌ها، ‏plugin‌ها، object‌های CRT و sysroot هنوز همگی از یک snapshot تغییرناپذیر اجرا نمی‌شوند. بنابراین tests/tools/cmake_ci_step.py --mode integrity با خطای مرز toolchain تغییرناپذیر و به‌صورت fail-closed متوقف می‌شود. تا زمانی که image یا snapshot تغییرناپذیر toolchain بازبینی نشده است، برای CI عادی از --mode diagnostic استفاده کنید و هیچ گزارش تشخیصی را تضمینی برای Hermeticبودن یا مقاومت در برابر کاربر متخاصمِ هم‌سطح معرفی نکنید.

تست​

تست‌ها با نام‌هایی مانند waterwall.<case> در CTest ثبت شده‌اند و framework موجود در tests/ آن‌ها را اجرا می‌کند. هر مورد یکپارچه‌سازی در tests/cases/<case>/ قرار دارد.

در Linux، integration test‌های قطعی بدون نیاز به دسترسی کاربر root، درون user namespace و network namespace خصوصی با جداسازی صرفاً loopback اجرا می‌شوند (tests/run_in_network_namespace.sh) که مانع از تداخل port‌ها شده و امکان اجرای هم‌زمان را فراهم می‌کند. تست‌ها در مسیرهای (lane) مجزا از طریق tests/run_test_lane.sh سازمان‌دهی شده‌اند:

موارد قطعی نباید از DNS عمومی استفاده کنند یا به آزادبودن پورت در شبکه‌ی میزبان وابسته باشند. موردی که عمداً به اینترنت زنده نیاز دارد باید در مسیر صریح external قرار بگیرد.

مسیر privileged از همان جداسازی شبکه استفاده می‌کند؛ اما چون wrapper آن از قبل با sudo -E اجرا شده است، در user namespace فراخواننده باقی می‌ماند و فقط یک network namespace خصوصی می‌سازد. این کار دسترسی به مسیرهای فضای کاری متعلق به اجراکننده را حفظ می‌کند؛ قرار دادن root درون یک user namespace نگاشت‌شده، اختیار دسترسی آن به فایل‌سیستم میزبان را از بین می‌برد.

  • support: تست‌های سیاست تولید و framework تست (۱۳ تست) که کاملاً ترتیبی اجرا می‌شوند.
  • functional: integration test‌های قطعی (۱۴۹ تست) که به‌صورت موازی (min(32, max(4, 4 * CPUs)) کار) اجرا می‌شوند.
  • external: integration test‌های نیازمند اینترنت زنده‌ی میزبان (مانند Reality در برابر Google) که کاملاً ترتیبی اجرا می‌شوند.
  • speed: معیارهای توان عملیاتی چندجریانه (۱۶ تست) که برای جلوگیری از رقابت، کاملاً ترتیبی (RUN_SERIAL TRUE) اجرا می‌شوند.
  • privileged: تست‌های probe نیازمند دسترسی root/TUN (۶ تست) که زیر sudo -E اجرا می‌شوند.
  • all: اجرای توالی مسیرهای support، ‏functional، ‏external و speed (در مجموع ۱۷۹ تست).

درخت تولید فقط integration test‌ها، smoke و سیاست کد منبع را دارد. تست‌های واحد بومی (Native Unit) در درخت جداگانه‌ی بدون LTO اجرا می‌شوند؛ بنابراین ctest --preset linux فقط تست‌های تولید را اجرا می‌کند.

اندازه‌گیری‌های مرجع با ساخت گرم روی یک میزبان ۴ پردازنده‌ای حدود ۵۵ ثانیه برای مسیر support، حدود ۲۲ تا ۲۴ ثانیه برای مسیر قطعی و موازی functional (۱۶ کار)، کمتر از یک ثانیه برای مسیر ترتیبی external، ‏۴۶ تا ۴۸ ثانیه برای مسیر ترتیبی speed و ۱۴ تا ۱۵ ثانیه برای مسیر privileged است. در نتیجه، توالی کامل تولید بدون نیاز به مجوز ویژه (مسیر all: مجموع ۱۷۹ تست support + functional + external + speed) حدود ۲ دقیقه (~۱۲۷ ثانیه) طول می‌کشد. اجرای مسیر functional روی میزبان محدود به یک CPU، چهار کار را به‌کار می‌گیرد و حدود ۷۱ ثانیه طول می‌کشد. این اعداد فقط برای برنامه‌ریزی هستند و تضمین کارایی روی همه‌ی سامانه‌ها نیستند. مجموع all بررسی‌های سیاست و framework تست تولید را نیز دربر می‌گیرد؛ درخت unit test‌های native جدا باقی می‌ماند. اعتبارسنجی کامل تولید به‌همراه مسیر Release unit test‌های native، با احتساب مسیر privileged، حدود ۳٫۵ دقیقه طول می‌کشد. بخش Release unit test‌های native در سه اجرای گرم linux-unit-release میانه‌ای حدود ۷۴ ثانیه داشت. این اندازه‌گیری مسیر اضافی Debug را دربر نمی‌گیرد؛ CI ابری Release و Debug را به‌صورت موازی اجرا می‌کند، بنابراین Debug توان پردازشی بیشتری مصرف می‌کند اما معمولاً کل زمان آن را به مسیر بحرانی اضافه نمی‌کند. این اعداد به سخت‌افزار و گرم‌بودن ساخت وابسته‌اند.

این زمان کوتاه‌تر، اعتبارسنجی گسترده را عملی می‌کند. برای تغییرهای مشترک شبکه یا رفتار تونل، مسیر کامل functional را به‌آسانی اجرا کنید و برای تغییرهای گسترده‌ی تونل یا framework تست، توالی عادی یکپارچه‌سازی را اجرا کنید. برای قراردادهای هسته‌ی مشترک، چرخه‌ی عمر/هم‌زمانی، تغییرهای چندزیرسامانه‌ای یا تغییرهایی که دامنه‌ی اثرشان مشخص نیست، اعتبارسنجی کامل تولید به‌همراه هر دو config تست‌های واحد بومی را اجرا کنید. بررسی متمرکز همچنان برای تغییرهای محدود یا بدون تغییر رفتار انتخاب مناسب‌تری است.

# توالی عادی Release تولید بدون نیاز به مجوز ویژه در میان مسیرهای آزمون.
cmake --preset linux
cmake --build --preset linux --config Release --target check_waterwall_tests -j8

# اجرای مسیر support تولید (بررسی‌های سیاست و چارچوب آزمون).
bash tests/run_test_lane.sh support build/linux Release

# اجرای مستقیم و موازی مسیر functional.
bash tests/run_test_lane.sh functional build/linux Release

# اجرای همه‌ی مسیرهای تولید بدون نیاز به مجوز ویژه در قالب یک توالی.
bash tests/run_test_lane.sh all build/linux Release

# مجموعه‌ی کامل آزمون‌های واحد بومی: Release بهینه‌شده و Debug همراه با Assert.
cmake --preset linux-unit-tests
cmake --build --preset linux-unit-release -j8
ctest --preset linux-unit-release --output-on-failure
cmake --build --preset linux-unit-debug -j8
ctest --preset linux-unit-debug --output-on-failure

پوشش API تغییر رمز​

پوشش change-user-password از handler ثبت‌شدهٔ واقعی و helperهای collection استفاده می‌کند. آزمون provider/native ورودی bounded، خطای typed backend/collision، lookupهای credential و تبدیل‌های UUID، حفظ هویت/حسابداری، contention، حد ظرفیت/رمز قدیمی، budget/deadline و revision exhaustion را بررسی می‌کند. namespace واقعی مدیر رمز مستقل دارد و monitor/editor/creator و credential تولیدکننده را رد می‌کند؛ رمز Unicode و whitespace دقیق را از پروتکل موجود lookup، افزایش revision برای مقدار برابر و invalidation cursor را بررسی و ACK واقعی مصرف را دقیق replay می‌کند. پس از دورریختن پاسخ، read عمومی و ارسال مجدد صریح با token تازه انجام می‌شود؛ read به‌تنهایی رمز نصب‌شده را اثبات نمی‌کند. endpoint با سقف ۱۰۲۴ بایت و آزمون حداکثر ACK در codec همان schema پاسخ را می‌پوشانند. آزمون support تولید waterwall.api_example_client خود CLI نمونه را با transport قطعی اجرا می‌کند: موفقیت، خطای node، conflict و پاسخ نامعلوم فقط یک تلاش دارند، بازیابی صریح را توضیح می‌دهند و رمز یا token مدیر را چاپ نمی‌کنند. این آزمون جای transport واقعی namespace را نمی‌گیرد.

کلاینت مدیریت Python آزمون waterwall.api_admin_client و ماژول transport مشترک آزمون waterwall.api_transport_client را به همان lane پشتیبانی اضافه می‌کنند. آن‌ها اعتبارسنجی فرمان، pagination محدود، edit token صریح، بایت دقیق secret، اعتبارسنجی frame/پاسخ و تشخیص محافظه‌کارانهٔ نتیجهٔ write را پوشش می‌دهند. case به نام waterwall.api_admin_cli در namespace، CLI واقعی را برای workflow حساب به زنجیرهٔ واقعی AuthenticationServer/APIServer وصل می‌کند. فرمان مستقیم در fixture و فایل آزمون Python ثبت شده است؛ آزمون مستقل Python به build کد native C نیاز ندارد.

waterwall.authentication_sync_unit از API واقعی برای دو کلاینت با زمان reconnect متفاوت، ACK گم‌شدهٔ مصرف و replay دقیق batch معلق استفاده می‌کند و جداگانه pull نگه‌داشته‌شده همراه ترافیک در حال انتقال را می‌سنجد. پذیرش رمز قدیم/جدید پیش و پس از refresh هر کلاینت، ترافیک دقیق، مدرک اولین استفاده/ACK، projection انقضا، reservation و accounting/release از handle اتصال موجود را بررسی کنید. انتخاب native متمرکز را در Debug و Release اجرا کنید؛ ownership parser/preparation/index/runtime را زیر Debug واقعی ASan/UBSan و raceهای reader/password/duplicate را زیر Debug واقعی TSan بسنجید. flagهای compile/link را تأیید کنید؛ هدف optimized بدون instrumentation مدرک sanitizer نیست. هدف unity تولید را هم بسازید؛ helper خصوصی در translation unit مشترک باید نام یکتا داشته باشد. برای پوشاندن collision، unity را غیرفعال نکنید و ترتیب بایتی یکسان اعضای JSON بین buildها نخواهید.

unit test‌های native​

unit test‌های native از یک درخت جداگانه‌ی multi-config استفاده می‌کنند که IPO/LTO پیش از ساخته‌شدن هر هدف پروژه در آن غیرفعال می‌شود. Release بهینه‌سازی و NDEBUG را حفظ می‌کند و Debug، assert‌ها و guardrail‌های قراردادی را فعال می‌کند. Release تولید همچنان IPO/LTO را فعال دارد؛ بنابراین اعتبارسنجی کامل رفتاری از مسیر تولید و هر دو config unit test بومی استفاده می‌کند:

# Release تولید: فایل اجرایی منتشرشونده و مجموعه‌ی کامل آزمون‌های تولید.
cmake --preset linux
cmake --build --preset linux --config Release --target check_waterwall_tests -j8

# آزمون واحد بومی: رفتار بهینه‌شده‌ی Release بدون LTO.
cmake --preset linux-unit-tests
cmake --build --preset linux-unit-release -j8
ctest --preset linux-unit-release --output-on-failure

# آزمون واحد بومی: Assertها و Guardrailهای Debug در همان درخت بدون LTO.
cmake --build --preset linux-unit-debug -j8
ctest --preset linux-unit-debug --output-on-failure

برای کار رفتاری متمرکز، ابتدا انتخاب مرتبط linux-unit-debug را اجرا کنید؛ assert‌ها معمولاً نقض قرارداد مالکیت، چرخه‌ی عمر یا state را نزدیک‌تر به علت آن نشان می‌دهند. پیش از پایان کار همان پوشش را با linux-unit-release تکرار کنید، زیرا بهینه‌سازی و NDEBUG می‌توانند رفتار متفاوتی را آشکار کنند. تغییرهای گسترده یا مشترک هر دو preset کامل را اجرا می‌کنند. Debug جای Release، ‏AddressSanitizer، UndefinedBehaviorSanitizer یا ThreadSanitizer را نمی‌گیرد. unit test‌های native را هرگز در درخت تولید نسازید، زیرا کتابخانه‌های آن برای Waterwall با LTO ساخته می‌شوند.

اعتبارسنجی backend‌های رمزنگاری نیز از همین قانون دو درخت پیروی می‌کند. هر preset تولید مانند linux-sodium-crypto یک preset متناظر برای unit test بومی مانند linux-sodium-crypto-unit دارد. فایل اجرایی منتشرشونده و integration test‌ها را در درخت تولید بسازید و اجرا کنید و unit test‌های native رمزنگاری را فقط در درخت متناظرِ تست‌های واحدِ بدون LTO بسازید و اجرا کنید. preset‌های رمزنگاری تولید عمداً هدف‌های unit test بومی را تعریف نمی‌کنند.

مسیر متمرکز هم‌زمانی shutdown جداگانه configure می‌شود:

cmake --preset linux-tsan-unit
cmake --build --preset linux-tsan-unit -j8
ctest --preset linux-tsan-unit --output-on-failure

این مسیر controller، پذیرش مربوط به پیام worker و event loop، نشست reader و fixture‌های ترتیب واقعی shutdown تولید را زیر ThreadSanitizer پوشش می‌دهد. این یک مسیر متمرکز هم‌زمانی است و جای مسیر تست واحد در Release بدون LTO را نمی‌گیرد.

سیاست ثبت‌شده‌ی no-LTO مستقیماً cache مربوط به درخت واحد، فرمان‌های قابل‌دسترسی compile/link و artifact‌های نماینده از WaterWall، ‏lwIP و TlsClient را بررسی می‌کند. همین برای گرفتن regression مربوط به mixed-LTO کافی است و نیازی به ساختن یک سامانه‌ی attestation دوم پیرامون کامپایلر، ابزار ساخت یا verifier نیست.

CI ابری Linux preset‌های کامل linux-unit-release و linux-unit-debug را در دو job مستقل و موازی اجرا می‌کند. در macOS و Windows، ‏CI هدف waterwall_platform_unit_tests را می‌سازد، همان سیاست مستقیم no-LTO را اجرا می‌کند و تست‌های واحد platform را در Release اجرا می‌کند.

برای اجرای یک مورد با نام ثبت‌شده، از عبارت منظم (Regex) دارای anchor استفاده کنید:

ctest --preset linux --output-on-failure -R '^waterwall\.tls_roundtrip$'

برای اجرای مستقیم یک مورد یکپارچه‌سازی هنگام debug، فایل اجرایی، دایرکتوری مورد و timeout بر حسب ثانیه را بدهید:

tests/run_waterwall_case.sh \
build/linux/Release/Waterwall \
tests/cases/tls_roundtrip \
60

موردهای roundtrip زیادی برای الگوبرداری تست جدید وجود دارند، از جمله encryption_roundtrip، خانواده‌ی mux_*_roundtrip، ‏http1_* / http2_*، packets_stream_bridge_roundtrip و خانواده‌ی ping_*_roundtrip. نزدیک‌ترین مورد به تونل خود را به‌عنوان مرجع ساختاری انتخاب کنید؛ سپس بررسی کنید که assert‌های آن واقعاً رفتار تغییرکرده و حالت خرابی موردنظر را پوشش می‌دهند.

تستی بنویسید که با برعکس‌شدن جهت شکست بخورد

ارزشمندترین تست برای تغییر در جریان یا packet آن است که با جابه‌جایی handler مربوط به upstream/downstream بشکند. در تونل‌های جفت، مانند client/server یا encode/decode، همین یک assert رایج‌ترین regression این کدبیس را پیدا می‌کند. قواعد جهت را در بخش ۲ و بخش ۴ ببینید.

پوشش اندازه‌گذاری صف کرنل​

برای تغییر تنظیم صف هنگام startup، تست device_kernel_queue_tuning_unit و unitهای مرتبط TUN/Capture و تنظیمات node را در هر دو حالت Debug و Release اجرا کنید. هدف profile، سقف خودکار، تعداد نهایی صف‌ها پس از fallback، override، حفظ مقدار موجودِ بزرگ‌تر و رد ورودی نامعتبر حتی با tuning یا capture غیرفعال را بررسی کنید. fixtureهای native باید حالت غیرفعال، خطای request/readback، ACK ردشدهٔ NFQUEUE، شکست probe با socket در همان namespace و محدودشدن SO_RCVBUF توسط میزبان را پوشش دهند. probe ناموفق یا بدون تأیید افزایش باید buffer موجود را حفظ کند و socket موقت را ببندد، بدون اینکه صف قابل‌استفاده به شکست startup تبدیل شود. مقدار ناشناختهٔ کرنل در گزارش باید ناشناخته بماند؛ درخواست یک مقدار، اثبات پذیرش آن نیست.

integrationهای TUN و Capture در namespace خصوصی startup، حالت تک‌صف و multiqueue، تحویل دقیق packet و cleanup را بررسی می‌کنند. fixture checksum صف خصوصی TUN را صریحاً روی 16,384 می‌گذارد تا burst چهار upload هم‌زمان 1 MiB در MTU 576 به پیش‌فرض 500 کرنل یا هدف خودکار profile وابسته نباشد. این بررسی‌های صحت، بهبود throughput یا نبود loss در اشباع را ثابت نمی‌کنند. پیش از مقایسهٔ performance، workload، سیاست صف، MTU/offload، تعداد workerها، metricها و ترتیب نمونه‌ها را مطابق سیاست بهینه‌سازی اختیاری ثابت کنید.

انتخاب دامنه‌ی اعتبارسنجی و پوشش regression​

اعتبارسنجی باید مستقیماً به پرسش مربوط به محصول پاسخ دهد و متناسب با ریسک باقی بماند:

  • از کوچک‌ترین بررسی‌ای شروع کنید که تغییر را واقعاً می‌آزماید: بازبینی، قالب‌بندی، یک هدف متمرکز، یک تست متمرکز موجود یا یک مورد یکپارچه‌سازی مستقیم.
  • برای رفتار مشترک شبکه یا تونل و تغییرهایی که چند مورد را دربر می‌گیرند، دامنه را تا مسیر functional گسترش دهید. برای تغییرهای هسته‌ی مشترک، چرخه‌ی عمر/هم‌زمانی، چندزیرسامانه‌ای، حساس به امنیت یا دشوار از نظر تعیین دامنه، همه‌ی مسیرهای تولید را همراه با تست‌های native unit اجرا کنید.
  • مستندات، توضیحات کد و تغییرهای مکانیکی و آشکارا بدون تغییر رفتار ممکن است فقط به بازبینی، بررسی link و diff یا یک بررسی policy مرتبط نیاز داشته باشند. صرفاً به‌دلیل تغییر کد یا در دسترس‌بودن یک مسیر، آن مسیر گسترده را اجرا نکنید.
  • رفتار قابل‌مشاهده‌ی جدید، حالت‌های مرزی مهم، قراردادهای تغییرکرده و bug‌های قابل‌بازتولید و مستعد بازگشت، معمولاً باید پوشش متمرکز داشته باشند. هرگاه عملی است، تست regression باید پیش از اصلاح fail و پس از آن pass شود.
  • وقتی پوشش موجود دقیقاً همان رفتار را نشان می‌دهد، بازتولید عملی نیست یا تغییر هیچ رفتار معناداری برای assertکردن ندارد، تست تازه لازم نیست. به‌جای ساختن تست یا harness مصنوعی، دلیل آن را بیان کنید.
  • یک بررسی مستقیم می‌تواند یک قاعده‌ی محدود و قطعی را ثابت کند. وقتی هم‌زمانی، زمان‌بندی، امنیت یا رفتار غیرقطعی باعث می‌شود یک مشاهده کافی نباشد، از تکرار، sanitizer‌ها، تست فشار، تزریق خطا یا تست‌های گسترده‌تر استفاده کنید.

شکست اعتبارسنجی یا در دسترس نبودن محیط، مدرکی درباره‌ی همان تلاش اعتبارسنجی است؛ نه نتیجه‌ی pass و نه اثبات خودکار نادرستی محصول. خطای دقیق را حفظ کنید و میان مدرک شکست‌خورده (failed) و غیرقطعی (inconclusive) تفاوت بگذارید.

بررسی‌های صرفاً نحوی (Syntax-only)​

دستور کامپایل را دستی سرهم نکنید. اگر به کامپایل تک‌فایل یا صرفاً نحوی نیاز دارید، flag‌های دقیق همان فایل را از build/linux/compile_commands.json تولیدشده بردارید.

خطر تداخل structure.h

هر تونل header محلی خود را با نام include/<Tunnel>/structure.h دارد. اگر در یک دستور کامپایل دستی، دایرکتوری include/ چند تونل را هم‌زمان اضافه کنید، ممکن است structure.h اشتباه بی‌سروصدا انتخاب شود. نتیجه می‌تواند خطاهای گیج‌کننده یا، بدتر از آن، ساختی باشد که layout نادرست را پیوند داده است. هر تونل را جداگانه اعتبارسنجی کنید یا ترتیب اصلی include‌ها را از compile_commands.json حفظ کنید. این مهم‌ترین دلیل برای پرهیز از کامپایل دستی است.

خطاهای رایج کامپایل دستی عبارت‌اند از نبود header‌های وابستگی تولیدشده، ریشه‌ی اشتباه include در درخت ساخت و تداخل structure.h که بالاتر توضیح داده شد.

وقتی خود کامپایلر از کار می‌افتد​

مشکلی شناخته‌شده در محیط ساخت وجود دارد که طی آن یک درخت ساخت دستی یا ساخته‌شده بدون preset می‌تواند باعث خطای داخلی کامپایلر در GCC شود؛ برای مثال:

internal compiler error: Bus error

این اتفاق اغلب هنگام کامپایل‌شدن فایل‌های هسته مانند ww/libc/wlibc.c رخ می‌دهد؛ حتی پیش از آنکه کامپایلر به تونل تغییرکرده‌ی شما برسد.

خطای ICE یا Bus error نتیجه‌ای غیرقطعی است. این خطا اغلب از درخت ساخت قدیمی/دستی یا کمبود منابع میزبان ناشی می‌شود، اما ممکن است یک نقص کامپایلر نیز باشد که translation unit تغییرکرده آن را آشکار کرده است. این خطا نه نادرستی تونل را اثبات می‌کند و نه درستی آن را. پیش از تصمیم درباره‌ی آنچه باید debug شود، خطا را با یک preset پشتیبانی‌شده و پاک بازتولید کنید و هرگز آن مسیر را موفق گزارش نکنید.

راهبرد جایگزین در صورت مسدودشدن کامپایل​

این مراحل را به‌ترتیب انجام دهید:

  1. cmake --preset linux
  2. cmake --build --preset linux --target <ChangedTargets> -j8
  3. cmake --build --preset linux -j8
  4. اگر خطا باقی ماند، در صورتی که یک preset/toolchain پشتیبانی‌شده‌ی دیگر همان پرسش را پاسخ می‌دهد، آن را نیز امتحان کنید؛ برای مثال preset متناظر Clang.
  5. خطای دقیق کامپایلر را گزارش کنید و اعتبارسنجی مربوط را غیرقطعی (inconclusive) بنامید.
  6. قوی‌ترین اعتبارسنجی باقی‌مانده را ادامه دهید:
    • ساخت‌های هدف‌محور با preset؛
    • بررسی صرفاً نحوی با build/linux/compile_commands.json؛
    • بازبینی متمرکز کد بر اساس قراردادهای این راهنما.

بررسی‌های صرفاً نحوی و بازبینی، ابزارهای تشخیصی سودمندی هستند؛ اما برای یک تغییر رفتاری، جای شواهد ازدست‌رفته‌ی پیوند و زمان اجرا را نمی‌گیرند. دقیقاً بیان کنید چه چیزی اعتبارسنجی‌نشده باقی مانده است.

خلاصه‌ی اولویت‌های ساخت:

  • preset linux را ترجیح دهید؛
  • ساخت یک هدف با preset را بر ریشه‌ی build/ ترجیح دهید؛
  • build/linux/compile_commands.json را به flag‌های حدسی ترجیح دهید؛
  • تا وقتی یک ساخت پاک و پشتیبانی‌شده خلافش را مشخص نکرده است، GCC ICE یا Bus error را نتیجه‌ای غیرقطعی بدانید.

بازبینی بهینه‌سازی اختیاری و شواهد​

این بخش زمانی کاربرد دارد که یک گزینه‌ی بهینه‌سازی اختیاری، یا benchmark، parser، ‏manifest یا ابزار گردآوری شواهد برای تصمیم‌گیری درباره‌ی آن، در دامنه‌ی کار باشد. این قواعد، بازبینی عادی محصول را محدود نمی‌کنند و تسکی را که صریحاً اصلاح زیرساخت تست یا benchmark قابل‌استفاده‌ی مجدد را می‌خواهد نیز محدود نمی‌کنند. درخواست صریح نگه‌دارنده یا کاربر برای چنین زیرساختی، همان مجوز جداگانه‌ی لازم برای این دامنه است.

اعتبارسنجی یک کار پشتیبان است، نه محصولی دوم. باید یک regression واقع‌بینانه را پیدا کند یا به پرسش مهندسی ازپیش‌اعلام‌شده پاسخ دهد؛ نه اینکه به‌صورت بازگشتی بکوشد بی‌نقص‌بودن تک‌تک ابزارهای زنجیره‌ی شواهد را اثبات کند.

یافته‌ها را بدون پنهان‌کردن دسته‌بندی کنید​

در کار بهینه‌سازی/شواهد، این دسته‌ها را از هم جدا کنید:

  1. درستی محصول — نقص مشخصی در قرارداد، مالکیت، طول عمر، هم‌زمانی، امنیت، قابلیت حمل یا رفتار قابل‌مشاهده‌ی کاربر در کدی که منتشر می‌شود.
  2. پوشش مستقیم regression — نبود پوشش واقع‌بینانه برای رفتار تغییرکرده‌ی محصول که احتمال regression آن وجود دارد.
  3. تصمیم کارایی — مدرکی که نشان دهد یک گزینه‌ی مشخص محصول در برابر بار کاری و معیار پذیرش تثبیت‌شده، آستانه را برآورده می‌کند، از آن بازمی‌ماند یا روی میزبان فعلی قابل‌اندازه‌گیری نیست.
  4. کیفیت ابزار پشتیبان — نقص یا فرصت مقاوم‌سازی در اجراکننده، parser، ‏manifest، محل اتصال تستی، گردآورنده‌ی artifact یا ابزار شواهد دیگر.

این دسته‌بندی اولویت را مشخص می‌کند و هرگز دلیلی برای پنهان‌کردن یک یافته‌ی معتبر نیست. نقص ابزار پشتیبان زمانی تصمیم جاری را متوقف می‌کند که بتواند رفتار مرتبط را جا بیندازد، نتیجه‌ی موفق/ناموفق کاذب بسازد، metric را خراب کند یا به‌شکل دیگری تصمیم را تغییر دهد. در غیر این صورت، آن را به‌عنوان کار تکمیلی غیربازدارنده گزارش کنید؛ دامنه‌ی بازبینی گزینه‌ی جاری را گسترش ندهید. درخواست بازبینی محصول، مجوز بازطراحی نامرتبط زیرساخت benchmark نیست و پاک‌سازی آن نباید فقط برای موفق‌شدن شواهد، رفتار محصول را تغییر دهد.

گزینه را به یک تصمیم نهایی برسانید​

پیش از جمع‌آوری داده، بار کاری، metric‌ها، آستانه‌ها، تعداد نمونه‌ها و ترتیب‌ها، نیازمندی‌های میزبان و مرز گزینه‌ها را تثبیت کنید. سپس:

  • برای هر گزینه‌ی اختیاری و جداشده، دقیقاً یک نتیجه‌ی نهایی تعیین کنید: نگه‌داشتن (keep)، برگرداندن/به‌تعویق‌انداختن (revert/defer) یا غیرقطعی روی این میزبان (inconclusive on this host). در حالی که سامانه‌ی پذیرش را پیرامون گزینه بزرگ‌تر می‌کنید، آن را برای همیشه در حالت معلق رها نکنید.
  • regression یا افت کاراییِ اندازه‌گیری‌شده، یا نرسیدن به بهبود اعلام‌شده، baseline ساده‌تر را انتخاب می‌کند. نتیجه‌ی غیرقطعی نیز به‌طور پیش‌فرض baseline ساده‌تر محصول را نگه می‌دارد؛ گزینه را می‌توان تا دسترسی به میزبان benchmark بهتر به تعویق انداخت.
  • میزبانی که نتواند بار کاری تثبیت‌شده را با اطمینان اجرا کند، توان تصمیم‌گیری ندارد. این واقعیت را ثبت کنید. پس از دیدن نتایج، بار کاری را تنظیم نکنید، تا رسیدن به نتیجه‌ی مطلوب پی‌درپی تلاش نکنید، معیار پذیرش را ضعیف نکنید و برای تحمیل یک تصمیم، هویت‌های جایگزین برای framework تست نسازید.
  • به‌طور پیش‌فرض پس از نخستین تلاش سرتاسری، فقط یک نوبت محدودِ تعمیر جانبیِ framework تست مجاز است. تعمیر بیشتر به یک تسک زیرساختی جدا، با دامنه و درخواست صریح نیاز دارد.
  • پس از موفق‌شدن بررسی‌های مستقیم درستی و ثبت تصمیم نهایی، اعتبارسنجی گسترده‌ی محصول را یک بار روی مجموعه‌ی نهایی و نگه‌داشته‌شده‌ی تولید اجرا کنید. آن را پس از هر بازنگری ابزار شواهد دوباره و دوباره اجرا نکنید.

وقتی تنها محدودیت‌های غیربازدارنده‌ی ابزار شواهد باقی مانده‌اند، آن‌ها را همراه با نتیجه‌ی نهایی محصول گزارش کنید. مگر آنکه نقصی در محصول به‌طور مستقل بازتولید شود یا زیرساخت قابل‌استفاده‌ی مجدد صریحاً درخواست شده باشد، برنامه‌ی پیاده‌سازی دیگری برای محصول نسازید.

برای اثبات، زنجیره‌ای از اثبات‌های تازه نسازید​

مگر آنکه یک مدل تهدید جداگانه برای امنیت، supply chain یا ساخت بازتولیدپذیر به آن‌ها نیاز داشته باشد، موارد زیر را اضافه یا الزامی نکنید:

  • اجرای دوباره‌ی verifier، خوداعتبارسنجی validator، framework mutation برای تست‌کردن خود تست یا attestation تراکنش پذیرش؛
  • hash فایل اجرایی/ابزار/کد منبع، کپی کد منبع مربوط به verifier در هر دایرکتوری نتیجه، اثبات ancestry پردازه‌ی فرزند، منشأ فایل‌سیستم یا هویت CI runner؛
  • parser عمومی برای فرمان‌های shell/CMake که تنها هدفش اثبات تطابق فرمان‌های ثبت‌شده با flag‌های ثبت‌شده است؛
  • schema‌های جامع و fail-closed برای فیلدهای تشخیصی که نمی‌توانند درستیِ در محدوده‌ی کار یا تصمیم keep/revert را تغییر دهند؛ یا
  • تنظیم محصول، فیلد پروتکل، شاخه‌ی زمان اجرا، شمارنده، API یا hook چرخه‌ی عمر که فقط برای آسان‌ترکردن احراز اصالت شواهد اختیاری افزوده شده است.

یک بررسی مستقیم و قطعی اغلب برای گزینه‌ی محدود ساخت، مسیر، توازن مالکیت، timeout یا metric خروجی کافی است. زمان‌بندی، هم‌زمانی، امنیت و رفتار غیرقطعی ممکن است به شواهد تکرارشده یا مستقل نیاز داشته باشند؛ این قاعده اعتبارسنجیِ بازگشتیِ خودِ فرایند اعتبارسنجی را منع می‌کند، نه تست متناسب با ریسک را.

کد پشتیبان و artifact‌ها را تابع محصول نگه دارید​

  • تست‌های موجود و fixture‌های کوچک را ترجیح دهید. اگر کد جدید شواهد به‌طور محسوسی از تغییر محصول بزرگ‌تر می‌شود یا یک framework تازه می‌سازد، متوقف شوید؛ مگر آنکه دامنه‌ی زیرساخت صریحاً مجاز شده باشد.
  • محل‌های اتصال تستی و شمارنده‌هایی که فقط برای شواهد اضافه شده‌اند باید در ساخت عادی محصول کامپایل نشوند. API تولیدی که فقط برای benchmark افزوده می‌شود، به توجیه و تأیید جداگانه‌ی محصول نیاز دارد.
  • خروجی خام benchmark، ‏manifest‌های کپی‌شده، درخت‌های موقت state و snapshot‌های تکراری اجراکننده، ‏artifact تولیدشده‌اند. یک خلاصه‌ی کوتاه از نتایج را در کد منبع نگه دارید و بسته‌های حجیم بازتولیدپذیری را بیرون از تاریخچه‌ی عادی قرار دهید، مگر آنکه صریحاً درخواست شده باشند.
  • اگر اعتماد به سازوکار اثبات اختیاری از اعتماد به بررسی مستقیم دشوارتر شد، ساده‌سازی، جایگزینی یا حذف آن را به افزودن validator دیگر ترجیح دهید. حذف نیز تغییر کد است و باید از مرز مجوز تسک پیروی کند.

قواعد بازبینی کد و چک‌لیست تونل​

این فهرست حداقل موارد است، نه مرز بازبینی. رفتار تغییرکرده و caller‌ها، ‏callee‌ها، همسایه‌ها و مسیرهای خطای متأثر را از نظر رفتار تعریف‌نشده، خطای کران و اعداد صحیح، race، نشت حافظه/منبع، اتمام منابع یا محروم‌سازی از سرویس، مشکلات امنیتی، مدیریت config نامعتبر، سازگاری، قابلیت حمل و نقص‌های ساخت/تست بررسی کنید. الگوهای موجود، تست‌های موفق، بررسی‌های سیاست و توضیحات کد مدرک هستند، نه اثبات.

برای هر یافته، trigger و اثر مشخص گزارش کنید؛ نقص تأییدشده را از پرسش یا بهبود اختیاری جدا کنید و نقص مهم خارج از دامنه را بدون ویرایش پنهانی گزارش دهید. اگر یافته‌ی قابل‌اقدامی وجود ندارد، همین را بگویید و به‌جای ساختن ایرادهای سلیقه‌ای، ریسک باقی‌مانده یا شکاف مهم اعتبارسنجی را نام ببرید.

برای تغییر یک تونل، همه‌ی موارد مرتبط زیر را تأیید کنید:

چرخه‌ی عمر و جهت

  • Init پیش از استفاده‌ی هر callback، ‏line state همین تونل را مقداردهی می‌کند.
  • callback‌های upstream فقط با tunnelNextUpStream* forward می‌شوند.
  • callback‌های downstream فقط با tunnelPrevDownStream* forward می‌شوند.
  • هیچ flag‌ای به نام initialized اضافه نشده است، مگر آنکه یک قرارداد صریح و مستند برای نقش line یا callback، وجود state پیش از Init عادی را لازم بداند.

ایمنی line

  • هر callback از نوع re-entrant یا بلافاصله برمی‌گردد، یا با lineCallWithRef() / lineRef() دستی و lineIsAlive() از line محافظت می‌کند.
  • وقتی lineCallWithRef() مقدار false برمی‌گرداند، کد به line، ‏line state یا LinestateDestroy() دست نمی‌زند.
  • فقط مالک line تابع lineDestroy() را فراخوانی می‌کند.
  • هر مالک line عادی پس از دریافت Finish، تولیدکننده‌ها و inventory‌ها را جدا می‌کند، state خود را نابود می‌کند و در همه‌ی شاخه‌های خطا، timeout و تصمیم پایانی با ! lineIsAlive(line) برمی‌گردد.
  • مسیر مربوط به line قرض‌گرفته‌شده فقط state خودش را نابود می‌کند و هرگز lineDestroy() را فراخوانی نمی‌کند.
  • هرگز requestProgramShutdown() را جایگزین بستن یک line تحت مالکیت نکنید.

Finish

  • Finish پیش از propagateکردن close واقعی، state محلی را نابود می‌کند.
  • teardown از تونل میانی ابتدا upstream و سپس downstream را Finish می‌کند و بعد برمی‌گردد.
  • هیچ‌یک از Payload، ‏Est، ‏Pause، ‏Resume یا Finish به‌سمتی که Finish را فرستاده است بازتاب داده نمی‌شود.
  • اگر هنگام Finish ‏byte‌های پایانی پروتکل فرستاده می‌شوند، پیش از ارسال سمت فرستنده Finish‌شده علامت می‌خورد؛ ارسال re-entrant در حالی که reference مربوط به line نگه داشته شده است انجام می‌شود و handler پس از آزادسازی reference (unref)، بدون دست‌زدن به state نابودشده برمی‌گردد.

backpressure

  • پس از دریافت Pause، تولیدکننده تا هنگام Resume هیچ payload تازه‌ای به‌سمت آن مصرف‌کننده آغاز نمی‌کند.
  • هر تحمل عمدی برای داده‌ی پیش از Pause باید محدود، حافظ ترتیب FIFO، ثبت‌شده و قابل‌ردیابی و دارای مسیر روشن Resume یا overflow/close باشد. تونل‌های میانی شفاف صرفاً به این دلیل که adapter‌ها صف محدود دارند، buffering تازه‌ای ابداع نمی‌کنند.

buffer‌ها و padding

  • هر prepend در محدوده‌ی required_padding_left اعلام‌شده‌ی تونل قرار دارد.
  • فقط با ظرفیت کافی سمت چپ از sbufShiftLeft() استفاده می‌شود.
  • buffer‌ها دقیقاً در مسیرهایی recycle می‌شوند که هنوز مالکشان هستند و هیچ buffer‌ای leak نمی‌شود.

معنای packet، در صورت ارتباط

  • packet line‌ها در runtime عادی زنده نگه داشته می‌شوند.
  • state مربوط به packet line، ‏state محلی و مشترک worker در نظر گرفته می‌شود.
  • منبع Init مربوط به packet line، یعنی node manager یا onStart دستی، بررسی شده است.
  • هر handler مربوط به Finish در packet line برای همان نقش و جهت دقیق دسته‌بندی شده است: anchor مرگبار، forward شفاف در همان جهت یا جذب پایانی و مستندشده. هیچ‌کدام از این دسته‌ها packet line را نابود نمی‌کند.

اعتبارسنجی

  • test‌ها جهت موردنظر callback را واقعاً پوشش می‌دهند.
  • اعتبارسنجی از metadata مربوط به build با preset استفاده کرده است، نه flag‌های حدسی compiler.
  • همه‌ی فایل‌های C/header تغییرکرده با clang-format قالب‌بندی شده‌اند؛ یا در صورت نبودن ابزار، این کمبود صریحاً گزارش شده است.
  • هرجا پوشش regression از رفتار به‌شکل معناداری محافظت می‌کند، افزوده شده است؛ وگرنه دلیل کافی‌بودن پوشش موجود یا اعتبارسنجی بدون تست بیان شده است.
  • دامنه‌ی اعتبارسنجی متناسب است: برای تغییر محدود متمرکز و برای کار مشترک، هم‌زمان، حساس به امنیت یا دشوار از نظر تعیین دامنه، گسترده/کامل.

فهرست جامع خطاهای رایج​

این راهنما برای پیشگیری از خطاهای زیر نوشته شده است:

  • دسترسی به line state تونل پس از LinestateDestroy() یا خواندن فیلدهای zero‌شده.
  • افزودن flag‌های initialized برای جبران control flow ناامن.
  • دست‌زدن به pointer نوع line_t* پس از callback میان‌تونلی، بدون محافظت.
  • فراخوانی tunnelNext* یا tunnelPrev* در جهت اشتباه.
  • reflection مربوط به Pause/Resume/Finish به‌سمتی که Finish شده است.
  • تولید Est، ‏Pause، ‏Resume یا Finish در زمان نادرست.
  • زنده نگه‌داشتن line در طول Finish بدون lineRef().
  • فراموش‌کردن اینکه lineDestroy() انتظار دارد state تونل‌ها zero شده باشد، مگر آنکه reference count همچنان مثبت باشد.
  • leakشدن buffer وقتی callback، ‏line را از بین می‌برد.
  • استفاده‌ی نادرست از sbufShiftLeft() یا نادیده‌گرفتن required_padding_left.
  • رفتاری که فقط در یک چیدمان chain کار می‌کند و قابلیت ترکیب عمومی را می‌شکند.
  • درنظرگرفتن packet line state به‌عنوان state مختص اتصال، یا نابودکردن packet line هنگام runtime.
  • مقداردهی callback‌ها یا دسترسی به state تونل پیش از بررسی نتیجه‌ی nullable از tunnelCreate()، ‏adapterCreate() یا packettunnelCreate().
  • indexکردن array‌های تحت مالکیت worker با getWID() خام، به‌جای استفاده از زمینه‌ی line، ‏worker، ‏loop یا event worker بررسی‌شده.
  • درنظرگرفتن یک پیاده‌سازی موجود به‌عنوان اثبات درستی، به‌جای بررسی قرارداد و caller‌های آن.
  • ساختن دستی دستور compile در حالی که metadata مربوط به build با preset وجود دارد.
  • گزارش crash مربوط به compiler یا محیط دردسترس‌نبوده به‌عنوان اعتبارسنجی موفق.

خروجی مورد انتظار از coding agent​

پاسخ را با درخواست هماهنگ کنید و نتیجه را در ابتدا بیاورید. با این حال، در یک تغییر یا بازبینی کامل باید بتوان این واقعیت‌ها را به‌آسانی بررسی کرد:

  1. نتیجه — چه چیزی تغییر کرده است، یا یافته‌های تأییدشده به‌ترتیب شدت.
  2. شواهد درستی — رفتار مشخص، caller‌ها و قراردادهای مرتبط چرخه‌ی عمر، ‏line، جهت، buffer، packet، ‏worker یا shutdown.
  3. اعتبارسنجی — فرمان‌هایی که واقعاً اجرا شده‌اند، نتیجه‌ی آن‌ها و هر بررسی شکست‌خورده یا غیرقطعی. القا نکنید مسیری که اجرا نشده pass شده است.
  4. ریسک باقی‌مانده — شکاف‌ها، فرض‌ها یا کار تکمیلی مهم.

تحلیل نامرتبط تونل را به نتیجه‌ی یک تسک مستندات یا ابزار تحمیل نکنید. در مقابل، تغییر تونلی که نتواند جهت callback و نقش دقیق line خود را مشخص کند آماده نیست. وقتی موضوع مهمی روشن نیست، کد منبع، تست‌ها، caller‌ها و مستندات را بررسی کنید و اگر مخزن پاسخ را مشخص نمی‌کند، پرسشی متمرکز از maintainer بپرسید؛ الگوی موجود را اثبات ندانید و برای یک راهکار موردی، مفهوم تازه‌ای برای چرخه‌ی عمر ابداع نکنید.


ادامه: بخش ۷: shutdown برنامه.

تست ممیزی مدیریت​

waterwall.authenticationserver_audit_unit نتیجهٔ handler واقعی، حذف secret، اشباع صف محدود، مستقل‌بودن خطای writer، rollback پس از شکست ذخیرهٔ تولیدکننده و تخلیهٔ shutdown را می‌آزماید. wrapper لینکر مرز append واقعی را متوقف می‌کند تا صف به‌صورت deterministic پر شود؛ production test hook لازم نیست. unitهای checked writer جداگانه startup، append، rotation و خطای I/O را بررسی می‌کنند. waterwall.api_authentication_audit با دو worker و loopback خصوصی، نتیجهٔ API/تولیدکننده، هویت gateway، ورودی خراب بدون افشای secret و commit با پاسخ دورریخته‌شده را می‌سنجد. فقط status خواندنی poll می‌شود و mutation تکرار نمی‌شود. تست Python، وضعیت سالم/خراب audit و عدم چاپ خطای دلخواه سیستم را بررسی می‌کند. nativeها را در Debug/Release و sanitizerهای concurrency/memory اجرا کنید؛ این تست‌ها دوام crash یا commit اتمیک مشترک با دیتابیس را اثبات نمی‌کنند.

Google QUICHE و provider مشترک BoringSSL​

INCLUDE_QUIC_CLIENT و INCLUDE_QUIC_SERVER نودها را مستقل فعال می‌کنند. گراف وابستگی QUIC تنها با فعال‌بودن حداقل یکی ساخته و در حالت خاموش‌بودن هر دو fetch نمی‌شود. TlsClient و هر دو QUIC از یک WaterWall::BoringSSL روی vendor پیشونددار موجود استفاده می‌کنند؛ QUIC، TlsClient را روشن نمی‌کند و crypto بی‌پیشوند دیگری نمی‌آورد. وابستگی فعلی RealityClient/IpManipulator به TlsClient جداست.

closure تولید pin‌شده در ww/cmake/dependencies/quiche.cmake و noticeها در NOTICE.md کناری است: Google QUICHE، Abseil، protobuf-lite و protoهای QUIC، Google URL با IDNA فقط ASCII، zlib و BoringSSL مشترک. وابستگی‌های runtime سیاست static، CPU و relocation پروژه را دارند. QUICHE/QuicCommon و closure لازم به‌شکل private از C++20 استفاده می‌کنند؛ C++17 پایه در حالت خاموش QUIC تغییر نمی‌کند و wrapper C همان C11 است. C++ ناسازگار PCH زبان C و unity را غیرفعال می‌کند. host protoc بومی جداگانه با runtime pin‌شدهٔ protobuf تطابق دارد؛ binary هدف cross-build برای generation اجرا نمی‌شود و flagهای host از IPO/CPU زمان runtime جدا می‌ماند.

پوشش متمرکز waterwall.quic شامل unitهای carrier، engine و callback نود در Debug و Release، جفت مستقیم، UDP و TCP قاب‌بندی‌شدهٔ واقعی، server-first، impairment قطعی UDP، تنظیمات و shutdown در namespace خصوصی است. waterwall.quic_speedtest_vless_udp مورد اختیاری و سری throughput برای زنجیرهٔ دقیق VLESS/QUIC/UDP است؛ workload ثابت، نرخ‌های مبتنی بر بایت اندازه‌گیری‌شده و محدودیت‌ها کنار fixture JSON هستند. بررسی وابستگی runtime لینوکس closure QUIC را نیز می‌سنجد. cross-compile فقط compile/link را ثابت می‌کند؛ اجرای ناموجود هدف جداگانه گزارش می‌شود.

پوشش اندازهٔ UDP در Trojan​

waterwall.trojan_udp_size_probe دیتاگرام IPv4 را با جفت TrojanClient/TrojanServer خود WaterWall روی TLS/TCP حمل می‌کند. payloadها شامل خالی و اندازه‌های 8191، 8192، 8193، 32768، 65400 و 65507 بایت‌اند. اندازهٔ request و reply می‌تواند متفاوت باشد؛ یک درخواست کوچک پاسخ حداکثری socket را برمی‌گرداند. بایت‌های دقیق و تحویل یک datagram در هر دو جهت بررسی می‌شوند. fixtureهای native کلاینت/سرور مرز framing برابر 65535 بایت و رد بدنهٔ محلی 65536 بایتی را جدا می‌سنجند؛ این تست codec تلاش برای ارسال UDP معمولی IPv4 با payload ناممکن 65535 بایتی نمی‌کند. قطعه‌قطعه‌شدن حامل باید datagram کامل را حفظ کند.

پوشش مستقل Trojan و VLESS​

تست‌های waterwall.trojan_sing_box_{client,server}_{tcp,udp} و waterwall.vless_sing_box_{client,server}_{tcp,udp} هر دو نقش WaterWall را با sing-box 1.12.0 ثابت می‌سنجند. هر مورد کوتاه در namespace خصوصی اجرا می‌شود و مهلت 25 ثانیه دارد. peer مشترک با cmake --build --preset linux --target waterwall_vmess_SING_BOX_reference آماده می‌شود؛ این target برای موردهای فعال Trojan/VLESS حتی با VMess غیرفعال وجود دارد. اجرای هشت مورد: ctest --preset linux --output-on-failure -j8 -R '^waterwall\.(trojan|vless)_sing_box_'.

Trojan از TLS و VLESS از framing بومی، flow خالی و packet encoding صریحاً خالی استفاده می‌کند؛ multiplex غیرفعال است. کلاینت TLS در sing-box گواهی محلی fixture را verify می‌کند. کلاینت TLS در WaterWall به‌دلیل نداشتن تنظیم CA محلی سفارشی، صریحاً verify: false دارد. TCP شامل payload ‏65536 بایتی است؛ UDP بایت‌های دقیق 8192، 8193 و 15000 و اندازه‌های متفاوت request/reply را می‌سنجد. پاسخ خالی در هر دو نقش و درخواست خالی فقط با WaterWall در نقش client بررسی می‌شود؛ ورودی direct در sing-box ثابت درخواست خالی را حذف می‌کند. فقط token مجزای readiness تکرار می‌شود و packet اندازه‌گیری‌شده هرگز دوباره ارسال نمی‌شود.

این‌ها بررسی مثبت interop هستند. بافر packet ‏16 KiB در sing-box برای هدر پروتکل فضا رزرو می‌کند، پس payload قابل استفاده کوچک‌تر است و به مسیر بستگی دارد. workload ‏15000 بایتی سقف WaterWall را کاهش نمی‌دهد و جای پوشش pair/native نزدیک 65 KB را نمی‌گیرد. توپولوژی و جزئیات در tests/cases/singbox_proxy_interop/README.md ثبت شده است.

پوشش مستقل VMess​

وقتی یکی از endpointهای VMess فعال است، archiveهای رسمی checksum-pin‌شدهٔ V2Ray 5.53.0، ‏Xray 25.6.8 و sing-box 1.12.0 را پیش از ورود به namespace آماده کنید. target تجمیعی هر سه را آماده می‌کند؛ داخل تست نه latest انتخاب می‌شود و نه دانلودی انجام می‌شود:

cmake --build --preset linux --config Release --target waterwall_vmess_reference
ctest --preset linux --output-on-failure -R vmess

مسیر executableهای آماده با WATERWALL_VMESS_REFERENCE، WATERWALL_VMESS_XRAY_REFERENCE و WATERWALL_VMESS_SING_BOX_REFERENCE قابل تعیین است. fixture نسخهٔ دقیق را بررسی می‌کند. نام archive و SHA-256 ثابت در tests/provision_vmess_reference.py و تبدیل کوچک schemaهای بومی در tests/vmess_reference.py است. مقصدها فقط loopback و certificateها محلی‌اند. peer یا مجوز namespace ناموجود، validation ناموجود است؛ موفقیت محسوب نمی‌شود.

peer ثابتپوشش body/transport منتخبپوشش و محدودیت packetaddr
V2Ray 5.53.0AES/ChaCha روی TCP/UDP در هر دو جهت، NONE/zero، profileهای framing و ترکیب‌های موجود TLS/WS/WSS.هر دو جهت و خانوادهٔ IP؛ کلاینت با SOCKS packetEncoding: "Packet". ورودی SOCKS دیتاگرام خالی را پیش از envelope حذف می‌کند؛ این خانهٔ کلاینت صریحاً پشتیبانی نمی‌شود. سرور دیتاگرام خالی packetaddr را می‌پذیرد. چون کل frame نویسندهٔ packet حداکثر 2048 بایت است، payload نزدیک سقف 1940 بایت است. این سقف UDP بومی را هم محدود می‌کند؛ datagram دقیقاً 8192 بایتی برنامه عبور نمی‌کند.
Xray 25.6.8AES/ChaCha نمونه روی TCP/UDP در هر دو جهت و ID کوتاه؛ XRAY_CONE_DISABLED=true صریحاً UDP بومی را به‌جای تبدیل پیش‌فرض به XUDP انتخاب می‌کند؛ experiments: "NoTerminationSignal" بستن صرفاً transport را می‌سنجد. NONE/zero در ماتریس منتخب Xray نیستند. UDP بومی payload دقیقاً 8192 بایتی را حمل نمی‌کند؛ سقف 8192 بایتی فریم خروجی شامل طول، tag و padding است.خارج از پوشش: XUDP این نسخه envelope مربوط به packetaddr نیست.
sing-box 1.12.0AES/ChaCha نمونه روی TCP/UDP در هر دو جهت، NONE/zero خروجی و ID کوتاه. payload دقیقاً 8192 بایتی با AES در UDP بومی و packetaddr و هر دو نقش پشتیبانی می‌شود.هر دو جهت، IPv4/IPv6 و دیتاگرام خالی. سلول جداگانهٔ سقف peer برای IPv4/IPv6 به‌ترتیب payload ‏14993/14981 بایتی دارد؛ sing-vmess 0.2.6 متنِ رمز‌شونده، شامل envelope، را در 15000 بایت chunk می‌کند. این peer نمی‌تواند دیتاگرام 65400 بایتی را کامل حفظ کند؛ writer آن را تقسیم می‌کند و chunk reader بافر 16384 بایتی در جهت مقابل آن را رد می‌کند.

تست دقیق 8 KiB یعنی 8192 بایت دادهٔ برنامه، بدون هدر پروتکل. موردهای مثبت جفت WaterWall و sing-box، بایت‌های کامل request/reply را در یک datagram می‌سنجند. موردهای منفی سقف Xray/V2Ray نخست یک تبادل کنترلی کوچک موفق را لازم دارند، سپس بررسی می‌کنند که echo بزرگ به‌شکل یک datagram سالم کامل نشود. موفقیت چنین تستی ثبت محدودیت peer است، نه موفقیت interop برای 8 KiB. این حدود در نویسندهٔ احرازشدهٔ Xray و نویسندهٔ احرازشدهٔ V2Ray اعمال می‌شوند و ظرفیت کل فریم به‌ترتیب 8192 و 2048 بایت است. سقف peer ظرفیت codec خود WaterWall را کم نمی‌کند و محدودیت حجم انتقال TCP نیست. برای پنهان‌کردن محدودیت peer، دیتاگرام برنامه را کوتاه یا تقسیم نکنید.

tests/cmake/integration/vmess.cmake هر جهت را فقط با targetهای لازم ثبت می‌کند. جفت‌های WaterWall محلی/مدیریت‌شده پوشش admission، rotation، quota، replay، fallback، مقصد، DNS محلی و transport را حفظ می‌کنند. موارد جدید auto، رد security پیش از Init مقصد/fallback، مرز ID کوتاه و UUID canonical، termination و stats ثبت‌شده از APIServer را می‌سنجند. تست سرور packetaddr دو مقصد را interleave و پاسخ‌ها را معکوس می‌کند تا وابستگی آدرس حفظ شود. nativeها سقف دقیق codec، map محدود و مالکیت هنگام reentry را مستقل از محدودیت peer بررسی می‌کنند. جفت WaterWall همان workload ‏65400 بایتی را حفظ می‌کند.

موردهای رمز‌شدهٔ طولانی روی یک socket پایدار 65540 تبادل غیرخالی request/echo از نوع stop-and-wait دارند. درخواست بعدی فقط پس از echo ساخته می‌شود؛ بنابراین هر جهت واقعاً بیش از 65536 record دارد. relay فقط یک transport می‌پذیرد و reconnect نمی‌تواند نتیجه را موفق کند. AES و ChaCha شامل padding و profile ‏29 با طول احرازشده هستند. strict پس از 65535 تبادل موفق و پیش از استفادهٔ دوبارهٔ nonce خروجی بسته می‌شود. انتقال طولانی zero/TLS و UDP با NONE/TLS جداگانه، certificate محلی را در peer verify می‌کنند. regressionهای TLS خروجی تنظیم verify صریح قبلی را حفظ می‌کنند؛ TlsClient برای fixture خروجی محلیِ verifyشده تنظیم custom-trust محلی ندارد.

vmess_crypto_unit و vmess_parser_fuzz_unit متعلق به VmessCommon و با وجود هر endpoint ثبت می‌شوند. mutation محدود parser از vectorهای مستقل موجود، prefix ناقص، طول معیوب، تغییر tagِ body/EOF، هر دو سیاست counter و envelopeهای packetaddr استفاده می‌کند. corpus و نتیجهٔ تولیدشده زیر build/ می‌ماند. nativeهای replay/runtime سرور و config/runtime کلاینت، authentication، ownership، bootstrap، پاسخ تدریجی، reentry، بودجه و پایان عمر carrier را می‌سنجند:

cmake --preset linux-unit-tests
cmake --build --preset linux-unit-debug -j8
ctest --preset linux-unit-debug --output-on-failure -R vmess
cmake --build --preset linux-unit-release -j8
ctest --preset linux-unit-release --output-on-failure -R vmess
cmake --preset linux-software-crypto-sanitize-unit
cmake --build --preset linux-software-crypto-sanitize-unit -j8
ctest --preset linux-software-crypto-sanitize-unit --output-on-failure -R vmess

vectorهای مستقل Python، checkpoint مرزی با مصرف قبلی SHAKE دارند؛ حلقه‌های واقعی codec از صفر شروع می‌شوند و دو بار wrap می‌کنند. regressionهای context پایان‌یافته، strict، authentication، accounting مدیریت‌شده و lifecycle را حفظ کنید. refusal ورودی نگه‌داشته‌شده را از خطای پروتکل/هویت جدا گزارش کنید. native و mutation جایگزین شواهد مستقل session طولانی و transport واقعی نیستند.

INCLUDE_VMESS_CLIENT و INCLUDE_VMESS_SERVER مستقل‌اند و یک بررسی capability مستقیم OpenSSL streaming SHAKE را در VmessCommon به اشتراک می‌گذارند. وجود هرکدام به OpenSSL حداقل 3.3 نیاز دارد؛ WCRYPTO_BACKEND_OPENSSL عمومی مستقل است. هر دو endpoint، هریک به‌تنهایی، crypto عمومی software با VMess و build بدون OpenSSL با هر دو endpoint خاموش را بررسی کنید. برای اثبات استقلال کلاینت، همراه AuthenticationClient/UserController، nodeهای نامرتبط وابسته به آن‌ها را هم خاموش کنید؛ closure کلاینت نباید آن‌ها یا VmessServer را داشته باشد. پس از تثبیت تغییر، کل native Debug/Release، ASan/UBSan متمرکز در linux-software-crypto-sanitize-unit و laneهای معمول production را روی منبع نهایی اجرا کنید. speed/stress جداگانه‌اند.

characterization اختیاری tests/vmess_scale.py همان ۱۰۰٬۰۰۰ حساب مدیریت‌شده، تعداد worker/concurrency، ترتیب و زمان نمونه/آمادگی ثابت را دارد. خروجی در build/ و داخل wrapper namespace موجود می‌ماند. CPU/RSS شامل AuthenticationServer و AuthenticationClient هم‌مکان است. نرسیدن به آمادگی جدول، اندازه‌گیری throughput handshake نیست؛ برای نتیجه مطلوب مهلت را تمدید یا تعداد حساب را کم نکنید.