Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Polestar 3 Performance, test drive: comodità e potenza possono convivere
Polestar 3 Performance, test drive: comodità e potenza possono convivere
Abbiamo passato diversi giorni alla guida di Polestar 3, usata in tutti i contesti. Come auto di tutti i giorni è comodissima, ma se si libera tutta la potenza è stupefacente
Qualcomm Snapdragon X2 Elite: l'architettura del SoC per i notebook del 2026
Qualcomm Snapdragon X2 Elite: l'architettura del SoC per i notebook del 2026
In occasione del proprio Architecture Deep Dive 2025 Qualcomm ha mostrato in dettaglio l'architettura della propria prossima generazione di SoC destinati ai notebook Windows for ARM di prossima generazione. Snapdragon X2 Elite si candida, con sistemi in commercio nella prima metà del 2026, a portare nuove soluzioni nel mondo dei notebook sottili con grande autonomia
Recensione DJI Mini 5 Pro: il drone C0 ultra-leggero con sensore da 1 pollice
Recensione DJI Mini 5 Pro: il drone C0 ultra-leggero con sensore da 1 pollice
DJI Mini 5 Pro porta nella serie Mini il primo sensore CMOS da 1 pollice, unendo qualità d'immagine professionale alla portabilità estrema tipica di tutti i prodotti della famiglia. È un drone C0, quindi in un peso estremamente contenuto e che non richiede patentino, propone un gimbal rotabile a 225 gradi, rilevamento ostacoli anche notturno e autonomia fino a 36 minuti. Caratteristiche che rendono il nuovo drone un riferimento per creator e appassionati
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 03-10-2005, 21:53   #1
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Test Driven Development: un esempio pratico (CICLO 1, Task 2.5)

Io e Vicius faremo Pair Programming in questo topic, svolgendo il task 2.5 totalmente test driven.

Non abbiamo ne' scritto il codice, ne' preparato i test prima di scrivere questo topic; avverra' tutto "live", e lo impostiamo come un dialogo fra me e lui, proprio come se fossimo davanti allo stesso PC.

Vediamo che cosa ne esce


Questo e' il task:
Riprodurre un suono quando il diamante collide con un bordo

Parto io, Vicius, scrivo il primo test nel file TestGemCollisionSound.java.
Ci serve un qualche modo per comunicare alla gemma che vogliamo riprodurre un suono e il suono da riprodurre.

Codice:
package it.diamonds.tests;

import it.diamonds.Gem;
import it.diamonds.audio.Sound;
import it.diamonds.audio.SoundException;
import junit.framework.TestCase;

public class TestGemCollisionSound extends TestCase
{
    public void testSetCollisionSound()
    {
        Sound sound = null;
        
        try
        {
            sound = Sound.createForTesting("diamond");
        }
        catch(SoundException e)
        {
            fail();
        }
        
        Gem gem = Gem.createGemForTesting();
        
        gem.setCollisionSound(sound);
        
        assertTrue("Collision sound not set", gem.isCollisionSoundSet());
    }

}
Il test ovviamente fallisce, non compila, a te. Per prima cosa bisogna far compilare questo test e vederlo fallire.

Cercheremo di seguire TDD alla lettera, di volta in volta il minimo cambiamento possibile per far passare il test e poi eliminazione di tutte le duplicazioni nel codice.

Ultima modifica di fek : 03-10-2005 alle 22:02.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 22:08   #2
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
eccomi.
Allora prima di tutto ho creato delle stubs per far compilare il programma. Questo è il codice:
Codice:
    public void setCollisionSound(Sound collisionSound)
    {
        return;
    }

    public boolean isCollisionSoundSet()
    {
        return true;
    }
Ora è tutto verde. Quindi sono passato ad implementare un po il codice.
Prima di tutto ho aggiunto una variabile dove meorizzare il "suono".
Codice:
    private Sound collisionSound;
Ora modifico il metodo che imposta il suono in modo che memorizzi l'istanza passata a setCollisionSound(). Ed ecco il codice:
Codice:
    public void setCollisionSound(Sound collisionSound)
    {
        this.collisionSound = collisionSound;
    }
Ed ora passo ad implementare il codice per isCollisionSoundSet()
Codice:
    public boolean isCollisionSoundSet()
    {
        return collisionSound != null;
    }
Per ora è tutto. La palla ha te fek.

ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 22:20   #3
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Vicius e' bravo, quindi lui puo' permettersi di saltare qualche passaggio, ma se avessimo voluto fare un passettino per volta, per andare piano piano, avremmo prima dovuto scrivere gli stub di modo da far fallire il test.

Quindi:

Codice:
  public void setCollisionSound(Sound collisionSound)
    {
        return;
    }

    public boolean isCollisionSoundSet()
    {
        return false;
    }
Il test cosi' compila ma non passa. Ora mi serve il piu' piccolo cambiamento al codice che mi faccia passare il test.
Eccolo:

Codice:
  public void setCollisionSound(Sound collisionSound)
    {
        return;
    }

    public boolean isCollisionSoundSet()
    {
        return true;
    }
Ora il test e' passato, e sappiamo che e' stato proprio il nostro cambiamento a farlo passare, perche' prima non passava. Eureka! Siamo un passettino piu' vicini alla fine del task.

Ora prima di scrivere altro codice, ci serve un altro test che fallisce, eccolo:

Codice:
    public void testCollisionSoundNotSet()
    {
        Gem gem = Gem.createGemForTesting();
        
        assertFalse("Collision sound not set", gem.isCollisionSoundSet());
    }
Se lancio questo test con l'implementazione semplice, fallisce. Questo vuol dire che non abbiamo abbastanza funzionalita' per far passare entrambi i test.

Quindi?

Ecco un'implementazione semplice che fa passare entrambi i test:

Codice:
    private boolean collisionSoundSet = false;
    
    public void setCollisionSound(Sound collisionSound)
    {
        collisionSoundSet = true;
    }

    
    public boolean isCollisionSoundSet()
    {
        return collisionSoundSet;
    }


Vicius, scrivi un test che ci porti da qui alla tua implementazione ora. A te, io lo implemento.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 22:25   #4
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Ecco il test.
Codice:
    public void testGetCollisionSound()
    {
        Sound sound = null;
        Audio audio = new Audio();
        audio.initListener();
        
        try
        {
            sound = Sound.createForTesting("diamond");
        }
        catch(SoundException e)
        {
            fail();
        }
        
        Gem gem = Gem.createGemForTesting();
        
        gem.setCollisionSound(sound);
        assertEquals("i due suoni non sono uguali", gem.getCollisionSound(), sound);
    }
ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 22:38   #5
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Mi serve per prima cosa far compilare il test:

Codice:
    public Sound getCollisionSound()
    {
        return null;
    }
Ora il test compila e fallisce. Bene.
Mi serve l'implementazione piu' semplice possibile per farlo passare. Visto che setCollisionSound() mi passa un oggetto e poi il test vuole che getCollisionSound() gli restituisca lo stesso oggetto, questa e' l'implementazione piu' semplice che mi viene in mente:

Codice:
    private boolean collisionSoundSet = false;
    private Sound collisionSound;
    
    public void setCollisionSound(Sound sound)
    {
        collisionSoundSet = true;
        collisionSound = sound;
    }

    
    public boolean isCollisionSoundSet()
    {
        return collisionSoundSet;
    }

    
    public Sound getCollisionSound()
    {
        return collisionSound;
    }
Il test passa assieme a tutti gl'altri. Barra verde. Eureka!

Ma c'e' una duplicazione che mi annoia. collisionSoundSet e collisionSound stanno esprimendo la stessa informazione in due posti diversi. C'e' una duplicazione e la elimino subito cosi'.

Codice:
    private Sound collisionSound;
    
    
    public void setCollisionSound(Sound sound)
    {
        collisionSound = sound;
    }

    
    public boolean isCollisionSoundSet()
    {
        return collisionSound != null;
    }

    
    public Sound getCollisionSouind()
    {
        return collisionSound;
    }
Tutti i test continuano a passare! Un altro passettino verso la soluzione. Una duplicazione eliminata, il mondo e' un posto piu' semplice rispetto a due minuti fa'.

Siamo arrivati passettino per passettino alla stessa prima soluzione di Vicius, pero' in piu' abbiamo altri due test che saranno sempre li' a dirci se combiniamo qualche casino.

Vicius ho dubbio, che succede se passo un suono nullo a setCollisionSound()?

Aspetta, te lo scrivo in un test:

Codice:
    public void testNullCollisionSound()
    {
        Gem gem = Gem.createGemForTesting();
        gem.setCollisionSound(null);
        
        assertFalse("Collision sound still set", gem.isCollisionSoundSet());
    }
Il test passa. E' il comportamento che vogliamo?

Ultima modifica di fek : 03-10-2005 alle 22:42.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 22:46   #6
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Quote:
Originariamente inviato da fek
Il test passa. E' il comportamento che vogliamo?
Io personalmente lo eviterei. Propongo una bella eccezione. Se si vuole un suono null non lo si imposta e basta. QUindi popongo di cambiare il test in:
Codice:
  public void testNullCollisionSound()
    {
        Gem gem = Gem.createGemForTesting();
        try {
            gem.setCollisionSound(null);
        } catch (ArgumentNullException e) {
            fail("boom!");
        }
    }
ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 22:56   #7
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Perfetto, a parte quell'orribile indentazione

Abbiamo preso una decisione di design su come trattare il valore null, ma la cosa interessante e' che ci siamo comunicati il design via test, e la decisione stessa via test. In maniera chiara.

Semplifico un po' i test e andiamo avanti, ho visto qualche duplicazione e codice inutile da eliminare.

Ecco i test:

Codice:
public class TestGemCollisionSound extends TestCase
{
    public void testCollisionSoundNotSet()
    {
        Gem gem = Gem.createGemForTesting();
        
        assertFalse("Collision sound not set", gem.isCollisionSoundSet());
    }
    
    public void testSetCollisionSound() throws SoundException
    {
        Sound sound = Sound.createForTesting("diamond");
       
        
        Gem gem = Gem.createGemForTesting();
        
        gem.setCollisionSound(sound);
        
        assertTrue("Collision sound not set", gem.isCollisionSoundSet());
    }
    
    public void testNullCollisionSound()
    {
        Gem gem = Gem.createGemForTesting();
        try 
        {
            gem.setCollisionSound(null);
        } 
        catch (Exception e) 
        {
            fail("boom!");
        }
    }
    
    public void testGetCollisionSound() throws SoundException
    {
        Sound sound = Sound.createForTesting("diamond");
        
        Gem gem = Gem.createGemForTesting();
        
        gem.setCollisionSound(sound);
        assertEquals("collision sound is wrong", gem.getCollisionSound(), sound);
    }

}
Hmm... c'e' ancora l'oggetto gem e sound creati ad ogni test. Duplicazione da eliminare.

Codice:
public class TestGemCollisionSound extends TestCase
{
    private Sound sound;    
    private Gem gem;
    
    
    public void setUp() throws SoundException
    {
        sound = Sound.createForTesting("diamond");        
        gem = Gem.createGemForTesting();
    }
    
    
    public void testCollisionSoundNotSet()
    {
        assertFalse("Collision sound not set", gem.isCollisionSoundSet());
    }
    
    public void testSetCollisionSound() throws SoundException
    {
        gem.setCollisionSound(sound);
        
        assertTrue("Collision sound not set", gem.isCollisionSoundSet());
    }
    
    public void testNullCollisionSound()
    {
        try 
        {
            gem.setCollisionSound(null);
        } 
        catch (Exception e) 
        {
            fail("boom!");
        }
    }
    
    public void testGetCollisionSound() throws SoundException
    {
        gem.setCollisionSound(sound);
        assertEquals("collision sound is wrong", gem.getCollisionSound(), sound);
    }

}
E' tutto cosi' semplice ora

Prossimo test ora, Vicius, che suggerisci?
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:01   #8
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Non fosse che ho sbagliato un test.

Codice:
 public void testNullCollisionSound()
    {
        try 
        {
            gem.setCollisionSound(null);
        } 
        catch (Exception e) 
        {
            fail("boom!");
        }
    }
Questo sta testando che non venga generata nessuna eccezione che e' il contrario di cio' che vogliamo.
Ecco il test giusto:

Codice:
    public void testNullCollisionSound()
    {
        try 
        {
            gem.setCollisionSound(null);
        } 
        catch (Exception e) 
        {
            return;
        }
        
        fail("Exception not thrown");
    }
Che giustamente fallisce.

Vicius, a te. Il codice piu' semplice che fa passare il test.

Ultima modifica di fek : 03-10-2005 alle 23:13.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:03   #9
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Come prossimo test possiamo a controlalre che il suonon non venga riprodotto prima della collisione. Questo è il codice del test:
Codice:
    public void testSoundBeforeCollision()
    {
        Sound sound = null;
        
        try
        {
            sound = Sound.createForTesting("diamond");
        }
        catch(SoundException e)
        {
            fail();
        }
        
        Gem gem = Gem.createGemForTesting();
        
        gem.setCollisionSound(sound);
        
        assertFalse("Sound must not be played before a collision",
            sound.wasPlayed());
    }
ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:03   #10
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Vai troppo veloce per me!
Fai prima passare il mio test.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:13   #11
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Quote:
Originariamente inviato da fek
Vai troppo veloce per me!
Fai prima passare il mio test.
OK. OK.

Allora in setCollisionSound non ho dovuto far altro che inseire un if per lanciare la nuova eccezione. abbiamo quindi questo codice.
Codice:
    public void setCollisionSound(Sound sound)
        throws NullPointerException
    {
        if (sound == null) 
        {
            throw new NullPointerException();
        }
        
        collisionSound = sound;
    }
ora il test passa.

ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:26   #12
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Ora il tuo test, ecco la versione semplificata:

Codice:
    public void testSoundBeforeCollision()
    {
        gem.setCollisionSound(sound);
        
        assertFalse("Sound must not be played before a collision",
            sound.wasPlayed());
    }
... che passa. Ecco, questo test va a rinforzare le maglie di test pero' non e' quello che ci serve per proseguire nel nostro task.

Una regola del TDD e' che non si aggiunge nessuna funzionalita' se non si ha un test che fallisce.

Quindi ora ci serve un test che fallisca per proseguire:
Eccolo qui:

Codice:
    public void testSoundAfterCollision()
    {
        gem.setCollisionSound(sound);
        
        Input input = Input.createInputForTesting();
        input.generateKey(KeyCode.vk_Left, true);

        gem.setSpeed(1000.0f, 1000.0f);
        gem.reactToInput(input);
               
        assertTrue("Sound must be played after a collision",
            sound.wasPlayed());
    }
Non e' un test semplice, e c'e' quel metodo setSpeed che non abbiamo. E ci serve un test per scriverlo.

Mettiamo da parte questo test e ne scriviamo uno per setSpeed prima, che dici? Facciamo passare quello e poi torniamo qui.

Ecco il test:

Codice:
    public void testSetSpeed() throws TextureNotFoundException
    {
        Input input = Input.createInputForTesting();
        input.generateKey(KeyCode.vk_Left, true);

        gem().setSpeed(10.0, 0.0);
        gem().reactToInput(input);
        
        assertEquals(90.0f, gem().getX());        
    }
Questo test e' in TestGemMove.java.

A te.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:38   #13
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Perfetto procediamo con questo setSpeed. Questa volta cerco di procedere piu lentamente. Dunque prima lo stub.
Codice:
    public void setSpeed(float vx, float vy)
    {
        return;
    }
Compila ma da semaforo rosso.
Ok. Facciamo passare questo benedetto test. Dunque visto che Gem è una classe figlia di Sprite che non devo far altro che chiamare setSpeed di sprite dal mio codice in questo modo.
Codice:
    public void setSpeed(float vx, float vy)
    {
        super.setSpeed(vx, vy);
    }
Ancora rosso... Uhm... che cosa è successo ?

ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:42   #14
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Quote:
Originariamente inviato da VICIUS
Ancora rosso... Uhm... che cosa è successo ?
E' successo che qualcuno ha implementato setSpeed in Sprite senza che fosse richiesto da nessun task o test (grrrr), e senza scrivere un test che lo testasse (grrrr#2) ed infatti non funziona.

Direi di togliere il metodo da Sprite e spostarlo a livello di Gem. Abbiamo un bel test che fallisce, dovrebbe essere piuttosto immediato farlo passare. Possiamo andare passo passo.
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:48   #15
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Come mi hai fatto notare il baco è banale. I valori vx e vy non vengono usati come velocita ma come variabili temporane mentre la velocita è sempre 1f. Per correggere è bastato cambiare nome alle due variabili temporane ed usare le due variabili vy e vx al posto del valore fisso di velocità.
qindi il codice ora è:
Codice:
    public void reactToInput(Input input)

    {

        float dx = 0f;

        float dy = 0f;



        if(input.isKeyUp())

        {

            dy += vy;

        }

        if(input.isKeyDown())

        {

            dy -= vy;

        }

        if(input.isKeyLeft())

        {

            dx -= vx;

        }

        if(input.isKeyRight())

        {

            dx += vx;

        }



        move(dx, dy);

    }
Tutti i test ci danno semaforo verde. possiamo procedere.

ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:51   #16
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Ora possiamo tornare al test di prima che compila... e fallisce.

A te
fek è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:56   #17
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Non ci resta che far partire il suono. Quindi vado nel metodo move della classe Gem e aggiungo il seguente codice subito dopo stopPulse();
Codice:
collisionSound.play();
Faccio partire junit ed ecco che mi da luce verde. Test Passato.

ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 03-10-2005, 23:58   #18
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Mamma butta la pasta?

Ora, ci sono condizioni particolari da testare secondo te?
fek è offline   Rispondi citando il messaggio o parte di esso
Old 04-10-2005, 00:09   #19
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Quote:
Originariamente inviato da fek
Mamma butta la pasta?

Ora, ci sono condizioni particolari da testare secondo te?
Quanto sei crudele

Dunque cosa succede se fate partire ad esempio Game o in uno sprite non impostate il suono di collisione? Semplice il sistema salta perchè mi sono, volutamente ,scordato di testa se collisionSound è diverso da null prima di riprodurlo. Quindi scrivo prima un bel test che faccia scattare il baco.
Codice:
    public void testPlayNullCollisionSound()

    {      

        Input input = Input.createInputForTesting();

        input.generateKey(KeyCode.vk_Left, true);



        gem.setSpeed(1000.0f, 1000.0f);

        try 

        {

            gem.reactToInput(input);

        }

        catch (Exception e)

        {

            fail("collision sound è null");

        }

    }
Ora che ho un test posso passare a correggere l'errore inserendo il mio bel if nel codice per far passare il test precedente. Abbiamo quindi:
Codice:
if (collisionSound != null)
                {
                    collisionSound.play();
                }
ciao
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 04-10-2005, 00:11   #20
fek
Senior Member
 
L'Avatar di fek
 
Iscritto dal: Oct 2002
Città: San Jose, California
Messaggi: 11794
Che dici si va a dormire ora o c'e' altro?
fek è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Polestar 3 Performance, test drive: comodità e potenza possono convivere Polestar 3 Performance, test drive: comodit&agra...
Qualcomm Snapdragon X2 Elite: l'architettura del SoC per i notebook del 2026 Qualcomm Snapdragon X2 Elite: l'architettura del...
Recensione DJI Mini 5 Pro: il drone C0 ultra-leggero con sensore da 1 pollice Recensione DJI Mini 5 Pro: il drone C0 ultra-leg...
ASUS Expertbook PM3: il notebook robusto per le aziende ASUS Expertbook PM3: il notebook robusto per le ...
Test ride con Gowow Ori: elettrico e off-road vanno incredibilmente d'accordo Test ride con Gowow Ori: elettrico e off-road va...
ESA: rilevati 40 mila asteroidi vicino a...
La batteria salva fabbriche di EQORE ott...
SpaceX Starship: iniziati i test della t...
Datacenter IA nello spazio entro 5 anni,...
Telescopio spaziale James Webb: rilevato...
Ericsson Mobility Report: nel 2025 il 5G...
PLAI DEMO DAY: si chiude il secondo cicl...
Google rilascia Nano Banana Pro: il nuov...
ChatGPT si rinnova ancora: disponibile l...
Ring lancia super sconti di Black Friday...
Black Friday 2025: 450 euro di sconto su...
Tutte le offerte Blink in un unico posto...
OpenAI e Foxconn uniscono le forze per r...
Ricarica delle auto elettriche in 3 minu...
Lucid presenta Gravity Touring, il SUV e...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 05:38.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Served by www3v