Packet process time simulation using queue
Efficiency

Your algorithm is actually quite simple and efficient.  Keeping track of the scheduled finish times for each packet in the buffer (using ts_te) is a great idea.  The main problem is your poor choice of data structure for ts_te.

Actually, even though ts_te stores the scheduled start and end times for each buffered packet, you never care about the start times.  So, you can just store the end times.  (Incidentally, if you had to store a pair of immutable elements, you should use a tuple rather than a list.  A list implies that the length may vary or that the contents may change.)

You have a performance problem in ts_te.pop(0).  Removing the first item in a list requires all the subsequent elements to be copied over to fill in the hole.  What you want is a data structure that lets you efficiently remove items from the head and append items at the tail.  Such a structure is called a deque (a double-ended queue), and it's in Python's standard library:


  Deques are a generalization of stacks and queues (the name is pronounced “deck” and is short for “double-ended queue”). Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.
  
  Though list objects support similar operations, they are optimized for fast fixed-length operations and incur O(n) memory movement costs for pop(0) and insert(0, v) operations which change both the size and position of the underlying data representation.


Futhermore, if you specify a maxlen when creating the collections.deque, then it acts as a ring buffer, which is often how fixed-size buffers are implemented in real-life systems.

On a related note, I recommend creating results to be the same size as packets, since you know exactly how long it is supposed to be.  That would be more efficient than repeatedly calling .append() and letting Python resize the list as necessary.  (Each resizing operation can be expensive, possibly involving the copying of the entire existing contents.)  Alternatively, if you don't need the results to be a list, simply yield the results instead of building a list.

Style

Follow PEP 8 naming conventions.  buffSize and startTime don't comply.

Avoid introducing special cases.  I don't think that buffSize == 0 should result in an empty list; rather, the results should all be -1, since every packet would have to be dropped.  Furthermore, your algorithm should be designed so that it does not rely on the buffer always being non-empty.

To avoid writing this:


for packet in packets:
    packetArr = packet[0]
    packetDur = packet[1]



… use destructuring assignments:

for packetArr, packetDur in packets:


As a rule, it's more elegant to write if failure_condition: (e.g detecting a dropped packet) rather than if success_condition:.  Usually, failure cases have short code to handle them, and there may be multiple failure cases to handle.

Suggested solution

from collections import deque

def process_packets(packets, bufsize):
    # Stores the scheduled finish times for packets
    buffer = deque(maxlen=bufsize)

    start_times = [None] * len(packets)
    for i, (arrival, duration) in enumerate(packets):
        # Remove packets from the buffer that have been processed by the
        # arrival time.
        while buffer and buffer[0] &lt;= arrival:
            buffer.popleft()

        if len(buffer) &gt;= bufsize:
            # Buffer overrun
            start_times[i] = -1
        else:
            # This packet will start being processed after the finish time of
            # the last buffered packet (if there is anything in the buffer).
            start_times[i] = max(arrival, buffer[-1] if buffer else 0)

            # Store the scheduled finish time for this packet.
            buffer.append(start_times[i] + duration)
    return start_times