بخش ۶: 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های آن
واقعاً رفتار تغییرکرده و حالت خرابی موردنظر را پوشش میدهند.
پوشش اندازهگذاری صف کرنل
برای تغییر تنظیم صف هنگام 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 پشتیبانیشده و پاک بازتولید کنید و هرگز آن مسیر را موفق گزارش نکنید.
راهبرد جایگزین در صورت مسدودشدن کامپایل
این مراحل را بهترتیب انجام دهید:
cmake --preset linuxcmake --build --preset linux --target <ChangedTargets> -j8cmake --build --preset linux -j8- اگر خطا باقی ماند، در صورتی که یک preset/toolchain پشتیبانیشدهی دیگر همان پرسش را پاسخ میدهد، آن را نیز امتحان کنید؛ برای مثال preset متناظر Clang.
- خطای دقیق کامپایلر را گزارش کنید و اعتبارسنجی مربوط را غیرقطعی
(
inconclusive) بنامید. - قویترین اعتبارسنجی باقیمانده را ادامه دهید:
- ساختهای هدفمحور با 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 واقعبینانه را پیدا کند یا به پرسش مهندسی ازپیشاعلامشده پاسخ دهد؛ نه اینکه بهصورت بازگشتی بکوشد بینقصبودن تکتک ابزارهای زنجیرهی شواهد را اثبات کند.
یافتهها را بدون پنهانکردن دستهبندی کنید
در کار بهینهسازی/شواهد، این دستهها را از هم جدا کنید:
- درستی محصول — نقص مشخصی در قرارداد، مالکیت، طول عمر، همزمانی، امنیت، قابلیت حمل یا رفتار قابلمشاهدهی کاربر در کدی که منتشر میشود.
- پوشش مستقیم regression — نبود پوشش واقعبینانه برای رفتار تغییرکردهی محصول که احتمال regression آن وجود دارد.
- تصمیم کارایی — مدرکی که نشان دهد یک گزینهی مشخص محصول در برابر بار کاری و معیار پذیرش تثبیتشده، آستانه را برآورده میکند، از آن بازمیماند یا روی میزبان فعلی قابلاندازهگیری نیست.
- کیفیت ابزار پشتیبان — نقص یا فرصت مقاومسازی در اجراکننده، 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را فرستاده است بازتاب داده نمیشود. - اگر هنگام
Finishbyteهای پایانی پروتکل فرستاده میشوند، پیش از ارسال سمت فرستنده 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
پاسخ را با درخواست هماهنگ کنید و نتیجه را در ابتدا بیاورید. با این حال، در یک تغییر یا بازبینی کامل باید بتوان این واقعیتها را بهآسانی بررسی کرد:
- نتیجه — چه چیزی تغییر کرده است، یا یافتههای تأییدشده بهترتیب شدت.
- شواهد درستی — رفتار مشخص، callerها و قراردادهای مرتبط چرخهی عمر، line، جهت، buffer، packet، worker یا shutdown.
- اعتبارسنجی — فرمانهایی که واقعاً اجرا شدهاند، نتیجهی آنها و هر بررسی شکستخورده یا غیرقطعی. القا نکنید مسیری که اجرا نشده pass شده است.
- ریسک باقیمانده — شکافها، فرضها یا کار تکمیلی مهم.
تحلیل نامرتبط تونل را به نتیجهی یک تسک مستندات یا ابزار تحمیل نکنید. در مقابل، تغییر تونلی که نتواند جهت 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.0 | AES/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.8 | AES/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.0 | AES/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 نیست؛ برای نتیجه مطلوب مهلت را تمدید یا تعداد حساب را کم نکنید.