Το Travis CI είναι μια κατανεμημένη υπηρεσία web για την κατασκευή και τη δοκιμή λογισμικού που χρησιμοποιεί το GitHub ως φιλοξενία πηγαίου κώδικα. Εκτός από τα παραπάνω σενάρια λειτουργίας, μπορείτε να προσθέσετε τα δικά σας χάρη στις εκτεταμένες επιλογές διαμόρφωσης. Σε αυτό το άρθρο θα διαμορφώσουμε το Travis CI ώστε να λειτουργεί με το PVS-Studio χρησιμοποιώντας το παράδειγμα κώδικα PPSSPP.
Εισαγωγή
Ρύθμιση του Travis CI
Θα χρειαστούμε ένα αποθετήριο στο GitHub, όπου βρίσκεται το έργο που χρειαζόμαστε, καθώς και ένα κλειδί για το PVS-Studio (μπορείτε να πάρετε
Πάμε στον ιστότοπο
Για το τεστ, διχαλωσα το PPSSPP.
Ενεργοποιούμε το αποθετήριο που θέλουμε να συλλέξουμε:
Προς το παρόν, η Travis CI δεν μπορεί να κατασκευάσει το έργο μας επειδή δεν υπάρχουν οδηγίες για την κατασκευή. Ήρθε λοιπόν η ώρα για τη διαμόρφωση.
Κατά τη διάρκεια της ανάλυσης, ορισμένες μεταβλητές θα μας είναι χρήσιμες, για παράδειγμα, το κλειδί για το PVS-Studio, το οποίο δεν θα ήταν επιθυμητό να καθοριστεί στο αρχείο διαμόρφωσης. Ας προσθέσουμε λοιπόν μεταβλητές περιβάλλοντος χρησιμοποιώντας τις ρυθμίσεις κατασκευής στο Travis CI:
Χρειαζόμαστε:
- PVS_USERNAME - όνομα χρήστη
- PVS_KEY - κλειδί
- MAIL_USER - email που θα χρησιμοποιηθεί για την αποστολή της αναφοράς
- MAIL_PASSWORD - κωδικός πρόσβασης email
Τα δύο τελευταία είναι προαιρετικά. Αυτά θα χρησιμοποιηθούν για την αποστολή αποτελεσμάτων μέσω ταχυδρομείου. Εάν θέλετε να διανείμετε την αναφορά με άλλο τρόπο, δεν χρειάζεται να τις υποδείξετε.
Έτσι, προσθέσαμε τις μεταβλητές περιβάλλοντος που χρειαζόμαστε:
Τώρα ας δημιουργήσουμε ένα αρχείο .travis.yml και τοποθετήστε το στη ρίζα του έργου. Το PPSSPP είχε ήδη ένα αρχείο διαμόρφωσης για το Travis CI, ωστόσο, ήταν πολύ μεγάλο και εντελώς ακατάλληλο για το παράδειγμα, οπότε έπρεπε να το απλοποιήσουμε πολύ και να αφήσουμε μόνο τα βασικά στοιχεία.
Αρχικά, ας υποδείξουμε τη γλώσσα, την έκδοση του Ubuntu Linux που θέλουμε να χρησιμοποιήσουμε στην εικονική μηχανή και τα απαραίτητα πακέτα για την κατασκευή:
language: cpp
dist: xenial
addons:
apt:
update: true
packages:
- ant
- aria2
- build-essential
- cmake
- libgl1-mesa-dev
- libglu1-mesa-dev
- libsdl2-dev
- pv
- sendemail
- software-properties-common
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
Όλα τα πακέτα που παρατίθενται χρειάζονται αποκλειστικά για PPSSPP.
Τώρα υποδεικνύουμε τη μήτρα συναρμολόγησης:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
Λίγα περισσότερα για την ενότητα μήτρα. Στο Travis CI, υπάρχουν δύο τρόποι για τη δημιουργία επιλογών κατασκευής: ο πρώτος είναι να ορίσετε μια λίστα μεταγλωττιστών, τύπων λειτουργικών συστημάτων, μεταβλητές περιβάλλοντος κ.λπ., μετά την οποία δημιουργείται μια μήτρα όλων των πιθανών συνδυασμών. το δεύτερο είναι μια ρητή ένδειξη του πίνακα. Φυσικά, μπορείτε να συνδυάσετε αυτές τις δύο προσεγγίσεις και να προσθέσετε μια μοναδική περίπτωση ή, αντίθετα, να την εξαιρέσετε χρησιμοποιώντας την ενότητα αποκλείουν. Μπορείτε να διαβάσετε περισσότερα για αυτό στο
Το μόνο που μένει είναι να παρέχουμε οδηγίες συναρμολόγησης για το έργο:
before_install:
- travis_retry bash .travis.sh travis_before_install
install:
- travis_retry bash .travis.sh travis_install
script:
- bash .travis.sh travis_script
after_success:
- bash .travis.sh travis_after_success
Το Travis CI σάς επιτρέπει να προσθέσετε τις δικές σας εντολές για διάφορα στάδια της ζωής μιας εικονικής μηχανής. Ενότητα πριν_εγκατάσταση εκτελείται πριν από την εγκατάσταση πακέτων. Επειτα εγκαθιστώ, το οποίο ακολουθεί την εγκατάσταση των πακέτων από τη λίστα addons.aptπου αναφέραμε παραπάνω. Η ίδια η συναρμολόγηση πραγματοποιείται σε γραφή. Αν όλα πήγαν καλά, τότε βρισκόμαστε μέσα μετά_επιτυχία (σε αυτή την ενότητα θα εκτελέσουμε στατική ανάλυση). Αυτά δεν είναι όλα τα βήματα που μπορούν να τροποποιηθούν, αν χρειάζεστε περισσότερα, τότε θα πρέπει να κοιτάξετε μέσα
Για ευκολία στην ανάγνωση, οι εντολές τοποθετήθηκαν σε ξεχωριστό σενάριο .travis.sh, το οποίο τοποθετείται στη ρίζα του έργου.
Έχουμε λοιπόν το παρακάτω αρχείο .travis.yml:
language: cpp
dist: xenial
addons:
apt:
update: true
packages:
- ant
- aria2
- build-essential
- cmake
- libgl1-mesa-dev
- libglu1-mesa-dev
- libsdl2-dev
- pv
- sendemail
- software-properties-common
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
matrix:
include:
- os: linux
compiler: "gcc"
env: PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
before_install:
- travis_retry bash .travis.sh travis_before_install
install:
- travis_retry bash .travis.sh travis_install
script:
- bash .travis.sh travis_script
after_success:
- bash .travis.sh travis_after_success
Πριν εγκαταστήσουμε τα πακέτα, θα ενημερώσουμε τις υπομονάδες. Αυτό είναι απαραίτητο για τη δημιουργία του PPSSPP. Ας προσθέσουμε την πρώτη συνάρτηση στο .travis.sh (σημειώστε την επέκταση):
travis_before_install() {
git submodule update --init --recursive
}
Τώρα ερχόμαστε απευθείας στη ρύθμιση της αυτόματης εκκίνησης του PVS-Studio στο Travis CI. Πρώτα πρέπει να εγκαταστήσουμε το πακέτο PVS-Studio στο σύστημα:
travis_install() {
if [ "$CXX" = "g++" ]; then
sudo apt-get install -qq g++-4.8
fi
if [ "$PVS_ANALYZE" = "Yes" ]; then
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
libio-socket-ssl-perl
libnet-ssleay-perl
fi
download_extract
"https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
cmake-3.6.2-Linux-x86_64.tar.gz
}
Στην αρχή της λειτουργίας travis_install εγκαθιστούμε τους μεταγλωττιστές που χρειαζόμαστε χρησιμοποιώντας μεταβλητές περιβάλλοντος. Τότε αν η μεταβλητή $PVS_ANALYZE αποθηκεύει την αξία Ναι (το υποδείξαμε στην ενότητα env κατά τη διαμόρφωση της μήτρας κατασκευής), εγκαθιστούμε το πακέτο pvs-studio. Εκτός από αυτό, υποδεικνύονται και πακέτα libio-socket-ssl-perl и libnet-ssleay-perl, ωστόσο, απαιτούνται για την αποστολή αποτελεσμάτων, επομένως δεν είναι απαραίτητα εάν έχετε επιλέξει άλλη μέθοδο για την παράδοση της αναφοράς σας.
Λειτουργία download_extract κατεβάζει και αποσυσκευάζει το καθορισμένο αρχείο:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
Ήρθε η ώρα να συνδυάσετε το έργο. Αυτό συμβαίνει στην ενότητα γραφή:
travis_script() {
if [ -d cmake-3.6.2-Linux-x86_64 ]; then
export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
fi
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
cmake $CMAKE_ARGS CMakeLists.txt
make
}
Στην πραγματικότητα, αυτή είναι μια απλοποιημένη αρχική διαμόρφωση, εκτός από αυτές τις γραμμές:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
Σε αυτήν την ενότητα κώδικα ορίσαμε για κέικ Σημαία για εξαγωγή εντολών μεταγλώττισης. Αυτό είναι απαραίτητο για έναν αναλυτή στατικού κώδικα. Μπορείτε να διαβάσετε περισσότερα για αυτό στο άρθρο "
Εάν η συναρμολόγηση ήταν επιτυχής, τότε φτάνουμε μετά_επιτυχία, όπου εκτελούμε στατική ανάλυση:
travis_after_success() {
if [ "$PVS_ANALYZE" = "Yes" ]; then
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
sendemail -t [email protected]
-u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-s smtp.gmail.com:587
-xu $MAIL_USER
-xp $MAIL_PASSWORD
-o tls=yes
-f $MAIL_USER
-a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
fi
}
Ας ρίξουμε μια πιο προσεκτική ματιά στις ακόλουθες γραμμές:
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
Η πρώτη γραμμή δημιουργεί ένα αρχείο άδειας χρήσης από το όνομα χρήστη και το κλειδί που καθορίσαμε στην αρχή κατά τη ρύθμιση των μεταβλητών περιβάλλοντος Travis CI.
Η δεύτερη γραμμή ξεκινά απευθείας την ανάλυση. Σημαία -j ορίζει τον αριθμό των νημάτων για ανάλυση, επισήμανση -μεγάλο δηλώνει άδεια, σημαία -ο ορίζει το αρχείο για την έξοδο των αρχείων καταγραφής και τη σημαία -απενεργοποιήστε τον Έλεγχο Λήξης Άδειας απαιτείται για δοκιμαστικές εκδόσεις, αφού από προεπιλογή pvs-studio-analyzer θα προειδοποιήσει τον χρήστη ότι η άδεια χρήσης πρόκειται να λήξει. Για να μην συμβεί αυτό, μπορείτε να καθορίσετε αυτήν τη σημαία.
Το αρχείο καταγραφής περιέχει πρωτογενή έξοδο που δεν μπορεί να διαβαστεί χωρίς μετατροπή, επομένως πρέπει πρώτα να κάνετε το αρχείο αναγνώσιμο. Ας περάσουμε τα κούτσουρα Plog-μετατροπέας, και η έξοδος είναι ένα αρχείο html.
Σε αυτό το παράδειγμα, αποφάσισα να στείλω αναφορές μέσω ταχυδρομείου χρησιμοποιώντας την εντολή να στείλετε e-mail.
Ως αποτέλεσμα, πήραμε το ακόλουθο αρχείο .travis.sh:
#/bin/bash
travis_before_install() {
git submodule update --init --recursive
}
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
travis_install() {
if [ "$CXX" = "g++" ]; then
sudo apt-get install -qq g++-4.8
fi
if [ "$PVS_ANALYZE" = "Yes" ]; then
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
libio-socket-ssl-perl
libnet-ssleay-perl
fi
download_extract
"https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
cmake-3.6.2-Linux-x86_64.tar.gz
}
travis_script() {
if [ -d cmake-3.6.2-Linux-x86_64 ]; then
export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
fi
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
cmake $CMAKE_ARGS CMakeLists.txt
make
}
travis_after_success() {
if [ "$PVS_ANALYZE" = "Yes" ]; then
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
sendemail -t [email protected]
-u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-s smtp.gmail.com:587
-xu $MAIL_USER
-xp $MAIL_PASSWORD
-o tls=yes
-f $MAIL_USER
-a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
fi
}
set -e
set -x
$1;
Τώρα ήρθε η ώρα να προωθήσετε τις αλλαγές στο αποθετήριο git, μετά το οποίο ο Travis CI θα εκτελέσει αυτόματα το build. Κάντε κλικ στο "ppsspp" για να μεταβείτε στις αναφορές κατασκευής:
Θα δούμε μια επισκόπηση της τρέχουσας κατασκευής:
Εάν η κατασκευή ολοκληρωθεί με επιτυχία, θα λάβουμε ένα email με τα αποτελέσματα της στατικής ανάλυσης. Φυσικά, η αλληλογραφία δεν είναι ο μόνος τρόπος για να λάβετε μια αναφορά. Μπορείτε να επιλέξετε οποιαδήποτε μέθοδο υλοποίησης. Αλλά είναι σημαντικό να θυμάστε ότι μετά την ολοκλήρωση της κατασκευής, δεν θα είναι δυνατή η πρόσβαση στα αρχεία της εικονικής μηχανής.
Σύνοψη σφαλμάτων
Έχουμε ολοκληρώσει με επιτυχία το πιο δύσκολο κομμάτι. Τώρα ας βεβαιωθούμε ότι όλες οι προσπάθειές μας αξίζουν τον κόπο. Ας δούμε μερικά ενδιαφέροντα σημεία από την αναφορά στατικής ανάλυσης που μου ήρθε μέσω ταχυδρομείου (δεν ήταν για τίποτε που το υπέδειξα).
Επικίνδυνη βελτιστοποίηση
void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
sha1_context ctx;
sha1_starts( &ctx );
sha1_update( &ctx, input, ilen );
sha1_finish( &ctx, output );
memset( &ctx, 0, sizeof( sha1_context ) );
}
Προειδοποίηση PVS-Studio:
Αυτό το κομμάτι κώδικα βρίσκεται στη μονάδα ασφαλούς κατακερματισμού, ωστόσο, περιέχει ένα σοβαρό ελάττωμα ασφαλείας (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
Όλα είναι εντάξει και η λειτουργία υπόμνημα εκτελείται, αντικαθιστώντας έτσι σημαντικά δεδομένα στη μνήμη RAM, ωστόσο, μην χαίρεστε ακόμα. Ας δούμε τη λίστα συναρμολόγησης της έκδοσης έκδοσης με βελτιστοποίηση:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
Όπως φαίνεται από την καταχώριση, ο μεταγλωττιστής αγνόησε την κλήση υπόμνημα. Αυτό οφείλεται στο γεγονός ότι στη συνάρτηση sha1 μετά την κλήση υπόμνημα όχι άλλη αναφορά στη δομή ctx. Επομένως, ο μεταγλωττιστής δεν βλέπει νόημα να σπαταλάει χρόνο στον επεξεργαστή αντικαθιστώντας τη μνήμη που δεν θα χρησιμοποιηθεί στο μέλλον. Μπορείτε να το διορθώσετε χρησιμοποιώντας τη λειτουργία RtlSecureZeroMemory ή
Σωστά:
void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
sha1_context ctx;
sha1_starts( &ctx );
sha1_update( &ctx, input, ilen );
sha1_finish( &ctx, output );
RtlSecureZeroMemory(&ctx, sizeof( sha1_context ) );
}
Περιττή σύγκριση
static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
int result = 0;
// For some reason, this is the only one that checks for negative.
if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
....
} else {
if (leftvol >= 0) {
chans[chan].leftVolume = leftvol;
}
if (rightvol >= 0) {
chans[chan].rightVolume = rightvol;
}
chans[chan].sampleAddress = samplePtr;
result = __AudioEnqueue(chans[chan], chan, true);
}
}
Προειδοποίηση PVS-Studio:
Δώστε προσοχή στο άλλο κλαδί για το πρώτο if. Ο κώδικας θα εκτελεστεί μόνο εάν όλες οι συνθήκες leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || δεξιά τόμος < 0 θα αποδειχτεί ψευδής. Επομένως, λαμβάνουμε τις ακόλουθες δηλώσεις, οι οποίες θα ισχύουν για τον κλάδο else: leftvol <= 0xFFFF, rightvol <= 0xFFFF, leftvol >= 0 и rightvol >= 0. Προσέξτε τις δύο τελευταίες δηλώσεις. Έχει νόημα να ελέγξουμε ποια είναι η απαραίτητη προϋπόθεση για την εκτέλεση αυτού του κομματιού κώδικα;
Επομένως, μπορούμε να αφαιρέσουμε με ασφάλεια αυτές τις δηλώσεις υπό όρους:
static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
int result = 0;
// For some reason, this is the only one that checks for negative.
if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
....
} else {
chans[chan].leftVolume = leftvol;
chans[chan].rightVolume = rightvol;
chans[chan].sampleAddress = samplePtr;
result = __AudioEnqueue(chans[chan], chan, true);
}
}
Άλλο σενάριο. Υπάρχει κάποιο είδος σφάλματος που κρύβεται πίσω από αυτές τις περιττές συνθήκες. Ίσως δεν έλεγξαν τι απαιτούνταν.
Ctrl+C Ctrl+V Αντεπιτίθεται
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Δώστε προσοχή στον έλεγχο στο εσωτερικό if. Δεν νομίζετε ότι είναι περίεργο που ελέγχουμε αν η διεύθυνση είναι έγκυρη; psmfData, διπλάσια? Οπότε αυτό μου φαίνεται περίεργο... Στην πραγματικότητα, αυτό είναι, φυσικά, τυπογραφικό λάθος και η ιδέα ήταν να ελέγξουμε και τις δύο παραμέτρους εισόδου.
Σωστή επιλογή:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
Ξεχασμένη μεταβλητή
extern void ud_translate_att(
int size = 0;
....
if (size == 8) {
ud_asmprintf(u, "b");
} else if (size == 16) {
ud_asmprintf(u, "w");
} else if (size == 64) {
ud_asmprintf(u, "q");
}
....
}
Προειδοποίηση PVS-Studio:
Αυτό το σφάλμα βρίσκεται στο φάκελο ext, επομένως δεν σχετίζεται πραγματικά με το έργο, αλλά το σφάλμα βρέθηκε πριν το προσέξω, οπότε αποφάσισα να το αφήσω. Εξάλλου, αυτό το άρθρο δεν αφορά τον έλεγχο σφαλμάτων, αλλά την ενσωμάτωση με το Travis CI και δεν πραγματοποιήθηκε καμία ρύθμιση παραμέτρων του αναλυτή.
Μεταβλητή μέγεθος αρχικοποιείται από μια σταθερά, ωστόσο, δεν χρησιμοποιείται καθόλου στον κώδικα, μέχρι τον τελεστή if, που φυσικά δίνει ψευδής ελέγχοντας τις συνθήκες, γιατί, όπως θυμόμαστε, μέγεθος ίσο με μηδέν. Οι μετέπειτα έλεγχοι επίσης δεν έχουν νόημα.
Προφανώς, ο συγγραφέας του τμήματος κώδικα ξέχασε να αντικαταστήσει τη μεταβλητή μέγεθος πριν από αυτό.
στάση
Εδώ μάλλον θα τελειώσουμε με τα λάθη. Ο σκοπός αυτού του άρθρου είναι να καταδείξει τη δουλειά του PVS-Studio μαζί με τον Travis CI και όχι να αναλύσει το έργο όσο το δυνατόν λεπτομερέστερα. Αν θέλετε μεγαλύτερα και πιο όμορφα λάθη, μπορείτε πάντα να τα θαυμάζετε
Συμπέρασμα
Η χρήση υπηρεσιών web για την κατασκευή έργων μαζί με την πρακτική της σταδιακής ανάλυσης σάς επιτρέπει να βρείτε πολλά προβλήματα αμέσως μετά τη συγχώνευση κώδικα. Ωστόσο, μια κατασκευή μπορεί να μην είναι αρκετή, επομένως η ρύθμιση της δοκιμής μαζί με τη στατική ανάλυση θα βελτιώσει σημαντικά την ποιότητα του κώδικα.
χρήσιμοι σύνδεσμοι
Εάν θέλετε να μοιραστείτε αυτό το άρθρο με ένα αγγλόφωνο κοινό, χρησιμοποιήστε τον σύνδεσμο μετάφρασης: Maxim Zvyagintsev.
Πηγή: www.habr.com